【问题标题】:WPF DisplayMemberPath not Updating when SelectedItem is removed删除 SelectedItem 时 WPF DisplayMemberPath 未更新
【发布时间】:2015-04-24 20:56:54
【问题描述】:

我已经尽可能地简化了这个问题。基本上我正在覆盖组合框的“null”值。因此,如果选定的项目被删除,它会恢复为“(null)”。不幸的是,这种行为是错误的,我点击了删除,ObservableCollection 项被删除,因此属性绑定被更新并按预期返回“(null)”项。但组合框外观显示为空白。然而它绑定的值是正确的......这个问题可以用下面的代码重现。

要重现此问题,您选择一个项目,然后点击删除。请注意此时调用了以下行(当您删除所选项目时)。所以它是一个断点的好地方。

                if (m_Selected == null)
            {
                return Items[0]; //items 0 is ItemNull
            }

还请注意,我已尝试通过强制更新 DisplayMemberPath 的属性来修复它。这不起作用。

MainWindow.xaml

<Window x:Class="WPFCodeDump.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding Selected, Mode=TwoWay}" DisplayMemberPath="Name"></ComboBox>
        <Button  Click="ButtonBase_OnClick">Remove Selected</Button>
    </StackPanel>
</Window>

MainWindowViewModel.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace WPFCodeDump
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    //Item class
    public class Item : ViewModelBase
    {
        public Item(string name)
        {
            m_Name = name;
        }

        public string Name
        {
            get { return m_Name; }
        }
        private string m_Name;

        public void ForcePropertyUpdate()
        {
            OnPropertyChanged("Name");
        }
    }

    //Item class
    public class ItemNull : Item
    {
        public ItemNull()
            : base("(null)")
        {
        }
    }

    class MainWindowViewModel : ViewModelBase
    {
        public MainWindowViewModel()
        {
            m_Items.Add(new ItemNull());
            for (int i = 0; i < 10; i++)
            {
                m_Items.Add(new Item("TestItem" + i));
            }
            Selected = null;
        }

        //Remove selected command
        public void RemoveSelected()
        {
            Items.Remove(Selected);
        }

        //The item list
        private ObservableCollection<Item> m_Items = new ObservableCollection<Item>();
        public ObservableCollection<Item> Items
        {
            get { return m_Items; }
        }

        //Selected item
        private Item m_Selected;
        public Item Selected
        {
            get
            {
                if (m_Selected == null)
                {
                    return Items[0]; //items 0 is ItemNull
                }
                return m_Selected;
            }
            set
            {
                m_Selected = value;
                OnPropertyChanged();
                if(m_Selected!=null) m_Selected.ForcePropertyUpdate();
            }
        }
    }
}

MainWindow.xaml.cs

using System.Windows;

namespace WPFCodeDump
{


    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            ((MainWindowViewModel) DataContext).RemoveSelected();
        }
    }
}

结果:

【问题讨论】:

  • return Items[0]... 从什么返回?那应该做什么?当然,您只需要 Selected = Items[0](假设 Items[0] 是数据绑定集合中的实际项目)。
  • 您是否尝试在 SelectedItem-Binding 中设置 UpdateSourceTrigger=PropertyChanged
  • 它返回 ItemNull(),它是 m_Items 内部的一个实例
  • @Tomtom 试过了,同样的问题。反正不是默认属性改变了吗?
  • @Sheridan Selected 属性的值是ItemNull 的实例,它被添加到VM 构造函数的第一行。在 Snoop 中检查程序可确认 SelectedItem 正在返回正确的对象实例,但显示的文本仍为空白,而不是应为的字符串 "(null)"

标签: wpf mvvm combobox


【解决方案1】:

您在那里发现了一个很好的绑定问题。但一如既往,这是我们的错,不是他们的:)

问题是,使用DisplayMemberPathSelectedItemDisplayMemberPath 不会对更改后的SelectedItem 表示意见。

要解决此问题,您必须做两件事:

首先,在RemoveSelected 方法中,将Selected 属性设置为null(强制更新绑定):

public void RemoveSelected()
{
    Items.Remove(Selected);
    Selected = null;
}

然后,在 XAML 定义中,更改绑定属性:

<ComboBox ItemsSource="{Binding Items}"
          SelectedValue="{Binding Selected, Mode=TwoWay}"
          DisplayMemberPath="Name"/>

绑定SelectedValue 属性将正确更新ComboBox 中显示的文本。

【讨论】:

  • 哇,这很有效。有没有办法绕过“Selected = null”?不幸的是,我不知道 Selected 的代码中的点(就像我在我的示例中所做的那样)。但是,我可以通过订阅 ItemsSource OnCollectionChanged 事件来解决这个问题......
  • 抱歉说得更清楚了。我选择的项目不知道它绑定到的集合。
  • @Asheh 没问题。请记住,DisplayMemberPath 是您使用 SelectedValue 时的朋友。与SelectedItem 合作时,ItemTemplate 是你的朋友 :)
  • ^ 是的。它只是向你们展示问题的最少代码!
  • 就我个人而言,我不太喜欢使用 SelectedValue 而不使用 SelectedValuePath... 对我来说,这让我的代码看起来有问题。我宁愿让.RemoveSelected() 方法使用临时值并将选定的项目设置为null,然后再删除先前选定的项目。例如,var tmp = Selected; Selected = null; Items.Remove(x); 也会导致正确的行为,而无需将 SelectedItem(我正确读取)更改为 SelectedValue(这对我来说似乎不正确,因为未设置 SelectedValuePath
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多