views:

87

answers:

2

I have an ObservableCollection of addresses that I am binding to a ListBox. Then in the ItemTemplate I am Binding to the current address record using {Binding .}. This results in my addresses displaying using their ToString method which I have setup to format the address. All is good, except if I update properties on an individual address record the list in the UI does not update. Adds/Deletes to the list do update the UI (using the ObservableCollection behavior). If I bind directly to properties on the address the UI does update (using the INotifyPropertyChanged behavior of the Address object).

My question is, is there a way to notify the UI of the change to the object as a whole so that I can still use this syntax or do I need to punt and put a DisplayText property on my address type that calls the ToString method and bind to that? FYI, this is an MVVM architecture so I don't have the luxury of calling Refresh on the ListBox directly.

Thanks for any help/ideas.

  <ListBox x:Name="AddressList" ItemsSource="{Binding Addresses}" Background="Transparent" BorderBrush="Transparent"
   Width="200" HorizontalAlignment="Left">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <StackPanel>
          <TextBlock Text="{Binding .}" />
        </StackPanel>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
+2  A: 

When you bind to the Address object itself, the object itself -- that is, its identity -- doesn't change, even though its properties do. WPF therefore doesn't know to refresh the binding in this case.

So yes, you need to bind to a notifying property (or properties) rather than the whole object. As you say, one way to do this is to create a DisplayText property, and raise the PropertyChanged event for that property whenever something that affects the display text changes. Another is to use multiple TextBlocks in a horizontally oriented StackPanel, each bound to a particular property e.g.

<StackPanel Orientation="Horizontal">
  <TextBlock Text="{Binding HouseNumber}" />
  <TextBlock Text=", " />
  <TextBlock Text="{Binding Street}" />
  <TextBlock Text=", " />
  <TextBlock Text="{Binding City}" />
</StackPanel>

The advantage of the second approach is that it gives you flexibility in the UI to change how addresses are displayed, e.g. multiple lines, formatting, etc.; the downside is that it gets complicated if you have conditional logic e.g. an optional flat number or second address line.

itowlson
That makes perfect sense. Although I think I'll stick with the display text property route as we need to support US and foreign addresses as well as a second line as you mentioned. To pull that off on the WPF side, would likely require a converter class. It makes more sense to me to keep that centralized with the entity class. The caller has all of the individual fields to override it with if necessary.
Mark
+2  A: 

I tried to reproduce the problem and succeeded.

I activated the step-into-.NET debugging options, and saw that WPF does not listen to INotifyPropertyChanged if the path in the binding is empty.

What worked to get a change to be reflected in the list box is to replace the whole object in the ObservableCollection. This triggers the INotifyCollectionChanged, with the Replace action.

But this may not be acceptable in your case. And it could be seen more like a hack than a solid solution.

I'd seriously consider having a DataTemplate for Address. There you should bind to the exact properties you need (which would create the listener for INotifyPropertyChanged). It is more flexible than ToString() and you may encounter cases where you have a need for ToString() to do something for non-UI stuff, which would create a conflict. And honestly, ToString is not really meant for UI stuff.

Timores