带有DisplayMemberPath的ListBox中的Observable Collection问题

时间:2016-09-12 14:32:45

标签: c# wpf mvvm data-binding observablecollection

我有ObservableCollection<UsageItem> MyItems;

编辑:

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
        ...
       }
    }

}

我已将Collection绑定到ListBox

<ListBox x:Name="listBoxBackPack"  ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}" DisplayMemberPath="DisplayName"/>

我有一个按钮,当点击时调用use上的SelectedItem函数,它会减小我的ChargesLeft字段值或删除对象。

如果我在ChargesLeft值为&lt; = 1时单击该按钮,它将从我的Collection中删除我的Item,我的ListBox将按预期更新,没问题。
但是如果我使用带有ChargesLeft&gt; = 1的项目,那么我将减少我的字段,从而改变我的DisplayName。
当我仍然更改我的收藏时,我希望触发CollectionChanged事件并刷新我的列表框并显示该项目的新DisplayValue,但不会发生,ListBox保持不变显示旧的DisplayValue 此时如果我手动执行Listbox.Items.Refresh()(在按钮上将其绑定以进行测试),它将刷新并正确显示正确的DisplayValue,但我无法从我的{{1}调用此函数因为它不了解我的ViewModel

如何在修改项目时自动强制刷新ListBox

1 个答案:

答案 0 :(得分:2)

添加或删除项目或清除整个集合时会引发

CollectionChanged。您的UsageItem类需要实现INotifyPropertyChanged本身来跟踪收集项中的更改。

https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx

  

请注意

     

要完全支持将数据值从绑定源对象传输到绑定目标,集合中支持可绑定属性的每个对象都必须实现适当的属性更改通知机制,例如INotifyPropertyChanged接口。

<强>更新 看一个工作实例(我使用Prism,但这并不重要):

<强> 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>