2016-06-16 5 views
0

ListBoxはオブジェクトのリストにバインドされており、ListBoxの各アイテムにはコンバーターでバインドされたTextBlockがありますが、オブジェクトのプロパティが変更されたときに、それらを更新することができません。TextBlockコンバータにバインドされていない場合次のようにPropertyChangedイベントを取得するには、コンバーターを使用するバインディングを使用しますか?

ウィンドウのXmaIである:

<Window x:Class="BindingPropertyChanged.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:BindingPropertyChanged" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="400" Width="600"> 
    <Window.Resources> 
     <local:LocationToTextConverter x:Key="LocationToTextConverter" /> 
    </Window.Resources> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="2*"/> 
     </Grid.ColumnDefinitions> 
     <ListBox 
      Grid.Row="2" 
      HorizontalContentAlignment="Stretch" 
      ItemsSource="{Binding Items}" 
      SelectedItem="{Binding SelectedItem}" 
      > 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <Grid> 
         <Grid.RowDefinitions> 
          <RowDefinition Height="auto"/> 
          <RowDefinition Height="auto"/> 
         </Grid.RowDefinitions> 
         <TextBlock x:Name="NameText" 
          Text="{Binding Converter={StaticResource LocationToTextConverter}, UpdateSourceTrigger=PropertyChanged}" /> 
         <TextBlock x:Name="DescriptionText" 
          Grid.Row="1" Text="{Binding Description, UpdateSourceTrigger=PropertyChanged}" /> 
        </Grid> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
     <Grid Grid.Column="1"> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="*"/> 
       <ColumnDefinition Width="2*"/> 
      </Grid.ColumnDefinitions> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="auto"/> 
       <RowDefinition Height="auto"/> 
      </Grid.RowDefinitions> 
      <Label HorizontalAlignment="Right" Content="Place" /> 
      <ComboBox Grid.Column="1" 
       ItemsSource="{Binding Places}" 
       SelectedValue="{Binding SelectedItem.PlaceId}" 
       SelectedValuePath="Id" 
       DisplayMemberPath="Name" 
       IsEditable="False"/> 
      <Label Grid.Row="1" HorizontalAlignment="Right" Content="Description" /> 
      <TextBox Grid.Row="1" Grid.Column="1" 
       Text="{Binding SelectedItem.Description, 
        UpdateSourceTrigger=PropertyChanged, 
        ValidatesOnNotifyDataErrors=True}" 
       VerticalAlignment="Center"/> 
     </Grid> 
    </Grid> 
</Window> 

左側に上述ListBoxであり、右側にListBoxで選択した項目をオーサリングするためのTextBoxComboBoxあります。 ListBox.ItemTemplateでは、右にTextBoxのテキストを変更するとDescriptionTextが更新される可能性がありますが、NameTextはバインディングでコンバーターを使用しているため、更新できません。

public class Repository 
{ 
    public static ICollection<Place> _places; 
    public static ICollection<Place> Places 
    { 
     get { return _places; } 
    } 

    public static ICollection<Location> _locations; 
    public static ICollection<Location> Locations 
    { 
     get { return _locations; } 
    } 

    static Repository() 
    { 
     _places = new List<Place>(); 
     _places.Add(new Place() { Id = 1, Name = "Downtown Center" }); 
     _places.Add(new Place() { Id = 2, Name = "Headquarter" }); 

     _locations = new List<Location>(); 
     _locations.Add(new Location() { PlaceId = 1, Description = "Room 101" }); 
     _locations.Add(new Location() { PlaceId = 2, Description = "B06" }); 
    } 
} 

、ウィンドウのDataContextに設定ViewModelクラス:

public class DomainBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged([CallerMemberNameAttribute] string propertyName = "None") 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

public class Location : DomainBase 
{ 
    private int _placeId; 
    private string _description; 

    public int PlaceId 
    { 
     get { return _placeId; } 
     set 
     { 
      if (_placeId == value) return; 
      _placeId = value; 
      OnPropertyChanged(); 
     } 
    } 

    public string Description 
    { 
     get { return _description; } 
     set 
     { 
      if (_description == value) return; 
      _description = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

public class Place : DomainBase 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

は、ダミーデータのRepositoryクラスがあります:

次のコードは、ドメインモデルのクラスです。

public class ViewModel : INotifyPropertyChanged 
{ 
    private ObservableCollection<Location> _items = new ObservableCollection<Location>(Repository.Locations); 
    private Location _selectedItem; 

    public ObservableCollection<Location> Items 
    { 
     get 
     { 
      return _items; 
     } 
     private set 
     { 
      if (_items == value) return; 
      _items = value; 
      OnPropertyChanged(); 
      OnPropertyChanged("SelectedItem"); 
     } 
    } 
    public Location SelectedItem 
    { 
     get 
     { 
      return _selectedItem; 
     } 
     set 
     { 
      if (_selectedItem == value) return; 
      _selectedItem = value; 
      OnPropertyChanged(); 
     } 
    } 
    public ObservableCollection<Place> Places 
    { 
     get 
     { 
      return new ObservableCollection<Place>(Repository.Places); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "None") 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

最後に、th E変換器:

class LocationToTextConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     string result = ""; 
     Location location = value as Location; 
     if (location != null) 
     { 
      Place place = Repository.Places.Single(o => o.Id == location.PlaceId); 
      if (place != null) 
      { 
       string placeName = place.Name; 
       if (!String.IsNullOrWhiteSpace(placeName)) 
       { 
        result = placeName + ", "; 
       } 
      } 

      result += location.Description; 
     } 
     return result; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

私がしたいことはListBoxでのDescriptionLocationとのNamePlaceを表示することであると私はDescriptionさんLocation選択を変更する場合、ListBoxでの表示名を変更する必要があります即座に同様に。誰もそれを達成する方法を知っていますか?

+0

「Location」オブジェクト自体が変更されていないため、「Location」は「PropertyChanged」を呼び出さないという問題があると思います。 –

答えて

3

あなたの問題は、あなたがパスを指定しなかったあなたのバインディングでは、ここで

です。これを行うと、バインドされるアイテムはロケーションオブジェクトになります。 LocationオブジェクトのDescritionプロパティを変更しているため、TextBlockはプロパティ変更通知を取得していません。

コードを数行変更するだけで、代わりにマルチバインディングを使用できます。

<TextBlock.Text> 
    <MultiBinding> 
     <Binding /> 
     <Binding Path=Description /> 
     //dont forget to specify the converter 
.... 
class LocationToTextConverter : IMultiValueConverter 
{ 
    //in the Convert method set, ignore value[1] and change location to values[0] 
    Location location = values[0] as Location; 

ここで、descriptionプロパティを変更すると、いつでもコンバーターが呼び出されるはずです。

+0

これはまさに私が必要なものです!また、 "PlaceId"をTextBlockにバインドする必要があります。 –

+0

マルチバインディングが正しいです。説明変更にPropertyChanged( "PlaceId")を発生させることは難しい問題であり、PlaceIdは変更されていないため、Bindingがそれに該当しない可能性があります。それをテストするのを邪魔するつもりはありません。 Cheers –

+0

PlaceIdは、ウィンドウの右側にコンボボックスがあるため変更できます。 –

0

Locationが変更されないため、NotifyPropertyChangedが呼び出されないという問題があります。最も簡単な解決策は、Locationにプロパティを作成することです。だから、あなたがそれを行うことができますどのように2つのオプションがあります:

  • コール
  • Location
の代わりにこのプロパティに Locationに独自のプロパティを追加し、バインドトリガーされるように Locationから OnPropertyChanged("Location")
+1

これもうまくいくはずです。唯一の問題は、LocationというロケーションのLocationプロパティをLocationという名前で定義した場合、コンパイラが別のタイプを引き出すように指示することです(「メンバー名はその囲みタイプと同じであってはいけません」)。 'パブリックロケーションSelf {get {return this; }} 'OnPropertyChanged(" Self ")' –

+0

@EdPlunkettこれも可能です。私はコードを試していないので、これはあくまでも仮定です:D –

0

なぜプロパティを持ってバインドするだけではないのですか?

public class Location : DomainBase 
{ 
    private int _placeId; 
    private string _description; 

    public int PlaceId 
    { 
     get { return _placeId; } 
     set 
     { 
      if (_placeId == value) return; 
      _placeId = value; 
      OnPropertyChanged(); 
     } 
    } 

    public string Description 
    { 
     get { return _description; } 
     set 
     { 
      if (_description == value) return; 
      _description = value; 
      OnPropertyChanged(); // make sure this Notify RepPlusDescription 
     } 
    } 

    public string RepPlusDescription 
    { 
     get 
     { 
      string result; 
      Place place = Repository.Places.FirstOrDefault(o => o.Id == placeId); 
      if (place != null) 
      { 
       string placeName = place.Name; 
       if (!String.IsNullOrWhiteSpace(placeName)) 
       { 
        result = placeName + ", "; 
       } 
      } 
      return result += location.Description; 
     } 
    } 
} 
+0

私はそれを考えましたが、理想的な解決策ではありません。なぜなら、UIの要件に合わせてドメインモデルのクラスに追加のプロパティを追加する必要があるからです。 –

+0

ああ、いいえ、余分な財産?その答えがどのようにDomainBaseと異なるものである必要があるか見当がつかない。 – Paparazzi

+0

リポジトリは場所に依存しますが、場所はリポジトリに依存しないため、依存関係にも違反します。ここにあるリポジトリはテスト目的のプレースホルダです。実際のケースでは、リポジトリは異なるDB、テキストファイル、バイナリファイルなどから作成することができます。 –

関連する問題