【问题标题】:Observable Collection issue in ListBox with DisplayMemberPath带有 DisplayMemberPath 的 ListBox 中的可观察集合问题
【发布时间】:2017-01-20 00:55:24
【问题描述】:

我有一个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"/>

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

如果我在 ChargesLeft 值 但是,如果我使用 ChargesLeft >= 1 的项目,那么我会减少我的字段,因此会更改我的 DisplayName。
由于我仍在更改我的收藏,我希望触发 CollectionChanged 事件并且刷新我的 ListBox 并显示该项目的新 DisplayValue,但它没有发生,ListBox 继续显示旧的 DisplayValue .
此时如果我手动执行Listbox.Items.Refresh()(将其绑定在按钮上进行测试)它将刷新并正确显示正确的DisplayValue,但我无法从我的ViewModel 调用此函数,因为它没有了解我的ListBox

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

【问题讨论】:

标签: c# wpf mvvm data-binding observablecollection


【解决方案1】:

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>

【讨论】:

  • 我刚刚实现了INotifyPropertyChanged(见编辑),但仍然观察到相同的行为。我可以看到以 ChargesLeft 作为参数的 PropertyEvent 触发,但除非我手动执行,否则 ListBox 仍然不会更新
  • @Belterius 您的代码显然不能如上所示工作,构造函数的名称与类不同,覆盖方法暗示它是子类,use 方法显然不是属于这个类。
  • @Belterius 似乎您需要将 RaisePropertyChanged("DisplayName"); 调用添加到更改 DisplayName 的所有属性的设置器中。查看更新的答案。
  • 现在完美运行,非常感谢。而且我确实修改了很多代码,只保留与我的问题相关的部分,我认为保留 mainsclass 没有用,我错过了删除覆盖以保持它的连贯性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 2021-12-15
  • 2019-12-10
  • 2019-02-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多