Reputation: 758
I have an ObservableCollection<UsageItem> MyItems;
:
EDIT :
public class UsageItem : INotifyPropertyChanged
{
string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
}
}
}
int _ChargesLeft;
public int ChargesLeft
{
get
{
return _ChargesLeft;
}
protected set
{
if (_ChargesLeft != value)
{
_ChargesLeft = value;
RaisePropertyChanged("ChargesLeft");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public Consummable(string name, int charges)
{
this.Name = name;
this.ChargesLeft = charges;
}
public override string DisplayName
{
get
{
if (ChargesLeft > 1)
{
return Name + " " + ChargesLeft + "charges";
}
else
{
return Name + " " + ChargesLeft + "charge";
}
}
}
public override void use(Hero hero)
{
if(this.ChargesLeft >= 1)
{
this.ChargesLeft--;
}else{
//Will remove this UsageItem from my ObservableCollection
...
}
}
}
I've bound my Collection
to a ListBox
:
<ListBox x:Name="listBoxBackPack" ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}" DisplayMemberPath="DisplayName"/>
I have a button that when clicked call the use
function on my SelectedItem
, it will decrease the value my ChargesLeft Field or remove the object.
If I click the button while my ChargesLeft value is <= 1 it will remove my Item from my Collection, and my ListBox will be updated as expected, no problem.
But If I use an item with a ChargesLeft >= 1, then I will decrease my field and so in consequence change my DisplayName.
As I still change my Collection I would expect a CollectionChanged
Event to fire and my ListBox to be refreshed and display the new DisplayValue
of the item, but it does not happen, the ListBox
keep displaying the old DisplayValue
.
At this moment if I manually do a Listbox.Items.Refresh()
(Binded it on a button for testing) it will refresh and correctly display the right DisplayValue
, but I can't call this function from my ViewModel
, as it has no knowledge of my ListBox
How can I do to automatically force the refresh of my ListBox
when I modify an item ?
Upvotes: 0
Views: 648
Reputation: 781
CollectionChanged
is raised when you add or delete an item or clear the whole collection. Your UsageItem
class needs to implement INotifyPropertyChanged
itself to track changes in collection items.
https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
Note
To fully support transferring data values from binding source objects to binding targets, each object in your collection that supports bindable properties must implement an appropriate property changed notification mechanism such as the INotifyPropertyChanged interface.
Update See a working example (I used Prism but that doesn't matter):
UsageItem.cs:
public class UsageItem : INotifyPropertyChanged
{
string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
RaisePropertyChanged("DisplayName");
}
}
}
int _ChargesLeft;
public int ChargesLeft
{
get
{
return _ChargesLeft;
}
set
{
if (_ChargesLeft != value)
{
_ChargesLeft = value;
RaisePropertyChanged("ChargesLeft");
RaisePropertyChanged("DisplayName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public UsageItem(string name, int charges)
{
this.Name = name;
this.ChargesLeft = charges;
}
public string DisplayName
{
get
{
if (ChargesLeft > 1)
{
return Name + " " + ChargesLeft + "charges";
}
else
{
return Name + " " + ChargesLeft + "charge";
}
}
}
}
MainViewModel:
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
Items = new ObservableCollection<UsageItem>();
Items.Add(new UsageItem("Bob", 1));
Items.Add(new UsageItem("Bill", 2));
Items.Add(new UsageItem("Joe", 3));
CurrentItem = Items[0];
UseCommand = new DelegateCommand(Use);
}
public ObservableCollection<UsageItem> Items { get; private set; }
private UsageItem _currentItem;
public UsageItem CurrentItem
{
get { return _currentItem; }
set { SetProperty(ref _currentItem, value); }
}
public DelegateCommand UseCommand { get; private set; }
private void Use()
{
if (CurrentItem.ChargesLeft >= 1)
{
CurrentItem.ChargesLeft--;
}
else
{
//Will remove this UsageItem from my ObservableCollection
Items.Remove(CurrentItem);
CurrentItem = Items[0];
}
}
}
MainWindow.xaml:
<Window x:Class="UsageLeft.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
Title="{Binding Title}"
Width="525"
Height="350"
prism:ViewModelLocator.AutoWireViewModel="True">
<StackPanel>
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding CurrentItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding UseCommand}" Content="Use" />
</StackPanel>
</Window>
Upvotes: 2