Reputation: 17139
I have the following setup:
XAML:
<ListBox x:Name="MyList" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="20" Width="20" Visibility="{Binding HasInformation, Converter={StaticResource VC}, ConverterParameter=True}" Source="/path/to/information.png" />
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Padding="5,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note: The ConverterParameter being passed in simply controls whether the visibility is "Collapsed" (False
), or "Hidden" (True
), so in this case, I want the visibility to be Hidden
.
ViewModel Snippet:
private ObservableCollection<IItem> _MyItems;
public ObservableCollection<IItem> MyItems
{
get
{
return _MyItems;
}
set
{
NotifyPropertyChanged(ref _MyItems, value, "MyItems");
}
}
private IItem _SelectedItem;
public IItem SelectedItem
{
get
{
return _SelectedItem;
}
set
{
NotifyPropertyChanged(ref _SelectedItem, value, "SelectedItem");
}
}
IItem:
public interface IItem
{
string Name { get; }
bool HasInformation { get; set; }
}
I populate an implementation of a list of IItem
from a database into the list, and the information icon appears appropriately if HasInformation
is true. This all works correctly.
However, if I set HasInformation
by hand, the view does not update. I have tried:
In the ViewModel:
OnPropertyChanged("MyItems");
MyItems[MyItems.IndexOf(SelectedItem)].HasInformation = true;
// Note that "SelectedItem" is persisted correctly, and always
// points to the selected item that we want to update.
In the code behind:
MyList.GetBindingExpression(ItemsControl.ItemsSourceProperty).UpdateTarget();
All of these fire the getter of the MyItems
property, but the view never updates and the icon never displays. I have ensured that the HasInformation
property of the item that I updated does, in fact, remain true
. I've attached to the PropertyChanged
event to ensure that it's firing a property change for "MyItems"
(it is, this also fires the getter), and I've even ensured that it's calling the value converter with the correct value for the HasInformation
property (it is!), so what am I missing? Is there something weird with image showing/hiding or visibility value conversion that I'm not handling correctly?
Upvotes: 2
Views: 7605
Reputation: 2818
ObservableCollection only notifies the collection changes not the changes in each of the item. In order to achieve your goal one of options is to change the IItem from interface to a class which implements INotifyPropertyChanged interface (or implement it in the IItem concrete type), and hook it with the ViewModel's PropertyChanged delegate (remember to unsubscribe it). See some of my code below.
ViewModel
public class MyViewModel: INotifyPropertyChanged
{
private ObservableCollection<Item> _MyItems;
public ObservableCollection<Item> MyItems
{
get
{
return _MyItems;
}
set
{
if (_MyItems != null)
{
foreach (var item in _MyItems)
{
item.PropertyChanged -= PropertyChanged;
}
}
if (value != null)
{
foreach (var item in value)
{
item.PropertyChanged += PropertyChanged;
}
}
OnPropertyChanged();
}
}
private Item _SelectedItem;
public Item SelectedItem
{
get
{
return _SelectedItem;
}
set
{
_SelectedItem = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Item
public class Item : INotifyPropertyChanged
{
private string _name;
private bool _hasInformation;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public bool HasInformation
{
get { return _hasInformation; }
set
{
_hasInformation = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Upvotes: 7