Reputation: 9603
I feel the need to apologise for this question. I'm sure the answer already exists here somewhere, but I'm having trouble understanding what's going on here. I could use some help learning.
My issue is the oft-raised one of getting WPF to react to changes to objects inside ObservableCollections
. There's one on my ViewModel called ItemPrices
, exposed as a property. The ViewModel implements INotifyPropertyChanged
. The collection is of an EF object called Price
, which does not.
The collection binds to a ListView
. Each item in the collection has a button - clicking on this button changes the state of the Price
object bound to that row. This all works fine. What I want to happen is for the image on the button to change when the user clicks on the button.
I went about this by creating a Converter that returns a different source string depending on the properties of the bound Price
object. This also works - but only if you close the window and re-open it. I'd like it to react right away when the user clicks the button.
I understand that ObservableCollection only responds to additions and removals and that I need to do something else to notify the ViewModel that the object inside the collection has changed. I wasn't using the SelectedItem
on the ListView
for anything else, so I figured I'd use that.
So, I added a SelectedPrice
propoerty to the ViewModel and bound it to the SelectedItem property in the ListView. Then, in the function executed when the user clicked the button I added SelectedPrice = price
- presuming that the call to the OnPropertyChanged would bubble up and cause the item to refresh in the ViewModel.
It didn't work, and I'm not entirely sure why. I've tried a variety of other things such as wrapping the ObservableCollection
in an ICollectionView
and calling Refresh()
on it, and adding INotifyPropertyChanged
to the Price - but nothing I do will get that image to refresh right away.
EDIT: I didn't post code, because all the relevant code would have been overwhelming. But some was asked for:
<Button Width="30" HorizontalAlignment="Right" Margin="5" CommandParameter="{Binding}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.DocumentCommand}" >
<Image HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding Converter={StaticResource PriceDocumentImageConverter} }" />
</Button>
And the converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Price price = (Price)value;
if(price.DocumentId != null)
{
return @"/Resources/Icons/Document-View.png";
}
return @"/Resources/Icons/Document-Add.png";
}
EDIT2 - INotifyPropertyChanged is on Price, like this:
public partial class Price : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Upvotes: 0
Views: 55
Reputation: 6570
You'll have to implement INotifyPropertyChanged
on the Price
object.
PropetyChanged
events don't bubble, it only knows about its own object.
Assuming the button is part of a DataTemplate, you've basically bound the source of the image to "this" (to the Price object itself), where as you actually seem to want to bind it to the state (presumably a property) that changes when the button is clicked. That property is the property that should fire a PropertyChanged event (passing its name as the property name), and the image source should be bound to that property, i.e. (assuming it's called State):
public partial class Price : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public int State
{
get { /*...*/ }
set
{
/*...*/
OnPropertyChanged("State");
}
}
}
XAML:
<Image Source="{Binding State, Converter={StaticResource PriceDocumentImageConverter} }" />
And of course you'll have to update the converter, because it will now get the value of the State property passed in.
Upvotes: 3