删除SelectedItem时,WPF DisplayMemberPath不更新

Ash*_*heh 5 wpf combobox mvvm

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

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

                if (m_Selected == null)
            {
                return Items[0]; //items 0 is ItemNull
            }
Run Code Online (Sandbox Code Playgroud)

另请注意,我已经通过在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>
Run Code Online (Sandbox Code Playgroud)

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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

按下删除后的结果

Her*_*rdo 5

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

问题是(是),使用DisplayMemberPathwith SelectedItem。在DisplayMemberPath不给AF ***有关改变的SelectedItem

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

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

public void RemoveSelected()
{
    Items.Remove(Selected);
    Selected = null;
}
Run Code Online (Sandbox Code Playgroud)

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

<ComboBox ItemsSource="{Binding Items}"
          SelectedValue="{Binding Selected, Mode=TwoWay}"
          DisplayMemberPath="Name"/>
Run Code Online (Sandbox Code Playgroud)

绑定该SelectedValue属性将正确更新ComboBox.

  • @Asheh 没问题。请记住,在使用“SelectedValue”时,“DisplayMemberPath”是您的朋友。使用 `SelectedItem` 时,`ItemTemplate` 是你的朋友 :) (2认同)
  • 就我个人而言,我不太喜欢使用“SelectedValue”而不使用“SelectedValuePath”...对我来说,这使代码看起来像是有问题。我宁愿让“.RemoveSelected()”方法使用临时值并将所选项目设置为“null”,然后再删除先前选择的项目。例如,`var tmp = 选定的;选定=空;Items.Remove(x);` 也会产生正确的行为,而不需要将 `SelectedItem` (对我来说正确读取)更改为 `SelectedValue` (这对我来说似乎不正确,因为未设置 `SelectedValuePath` ) (2认同)