c# - 如何从 PropertyChanged 事件中正确重置与 ComboBox 关联的值?

标签 c# wpf combobox binding

我有一个 ComboBox绑定(bind)到我的 ViewModel 上的一个属性(从“VM”上听到)。当用户在 ComboBox 上进行选择时它会正确更新我的 VM 中的绑定(bind)属性。在我的 UI 代码中,我订阅了 PropertyChanged我的虚拟机上的事件。

正如它应该表现的那样,当用户在 ComboBox 中进行选择时, 我的 PropertyChanged事件在我的 UI 后端代码中正确执行。当 UI 代码捕捉到此属性的更改时,在某些选择条件下,我需要停止进程并请求用户提供更多信息。从 UI 中,我向他们发送了一个对话框。如果他们取消对话框,我会重置与 ComboBox 关联的 VM 中的值。控制 SelectedValue .

这是我观察到的。当用户取消操作时,我的 VM 属性被设置为新值。但是,ComboBox仍然显示现在已更改的原始条目的文本值。如何强制 ComboBox从我的PropertyChanged 中更新自己事件?在这种情况下,我认为这只是一个文本问题或数字索引更改,它引用了绑定(bind)集合中的文本数据。 VM 中的数据正确,但显示值为 ComboBox是错的。

示例

组合框详情

    <ComboBox
        ItemsSource="{Binding ListOfComboBoxDisplayObjects}"
        SelectedValue="{Binding MySelectionIsAnEnumeration}"
        DisplayMemberPath="Text"
        SelectedValuePath="EnumerationValue"
        Height="27" />

对虚拟机上的冗长属性感到抱歉,但这是为了解释正在发生的事情。我的 ListOfComboBoxDisplayObjects collection 表示一组枚举器值,它们存储在 SelectedValuePath 内的路径中。 .每个值的描述性文本来自 ListOfComboBoxDisplayObjects这是一个为 UI 严格创建的特殊列表。这基本上将枚举值与有意义的描述配对。

ListOfComboBoxDisplayObjects 定义(来自 VM)

编辑 #1 - 将此定义添加到我的示例中
 private ObservableCollection<BindableEnumerationItem<Values>> _listOfComboBoxDisplayObjects;
public ObservableCollection<BindableEnumerationItem<Values>> ListOfComboBoxDisplayObjects
{
    get { return _listOfComboBoxDisplayObjects; }
    private set
    {
        if (value != _listOfComboBoxDisplayObjects)
        {
            _listOfComboBoxDisplayObjects= value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(ListOfComboBoxDisplayObjects)));
        }
    }
}

MySelectionIsAnEnumeration 定义(来自 VM 内)

*编辑#1:添加此代码定义。
    private Values_mySelectionIsAnEnumeration ;
    public Values MySelectionIsAnEnumeration 
    {
        get { return _mySelectionIsAnEnumeration; }
        set
        {
            //Double-checked this-- value is different on the second-call to change this value, once the UI cancels the operation.
            if (value != _mySelectionIsAnEnumeration)
            {
                _mySelectionIsAnEnumeration= value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(MySelectionIsAnEnumeration )));
            }
        }
    }

与 ListOfComboBoxDisplayObjects 关联的相关值

这些值在 VM 的 ctor 中生成。它们在整个应用程序中都是固定的。

项目 #1
  • 文本:“这是一个 Foo!”
  • 值:Values.Foo

  • 项目#2:
  • 文本:“嗨,我是 Bar。”
  • 值:Values.Bar

  • 项目#3:
  • 文字:“这是巴兹。我需要问一个问题才能使用。”
  • 值(value):值(value)观.Baz

  • PropertyChanged 事件 - 来自 UI 后端
    private void VM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                switch (e.PropertyName)
                {
                    case "MySelectionIsAnEnumeration":
                        if (VM.MySelectionIsAnEnumeration == Values.Baz)
                        {
                            //Prompt the user and get DialogResult.
                            bool answerGiven = AskAQuestionAndGetAResult();
                            if(!answerGiven)
                                VM.MySelectionIsAnEnumeration = Values.Foo;
                        }
                        break;
                }
            }
    

    执行上述代码后,我观察到 VM.MySelectionIsAnEnumeration value 确实正在更改为 Value.Foo 的正确值当用户在 AskAQuestionAndGetAResult() 内取消操作时.然而,在它完成后 ComboBox还是写着“This is Baz. I need to ask a question才可以使用。”,这显然是与Value.Baz关联的显示值.

    如何更新基础 VM 属性和 CombobBox 上的显示文本正确显示现在存储在 VM.MySelectionIsAnEnumeration 中的值?

    编辑#2

    下面是我的 BindableEnumerationItem 的代码我在我的 Observable Collections 中用于组合框和列表框。这在我的整个应用程序中都在更简单的情况下使用,并且没有引起任何问题。请注意,这是我实际的、未更改的代码。我没有重命名任何东西。我的组合框可以绑定(bind)到每个 Item类型安全属性的属性和 DisplayText是描述 rune 本。
    public class BindableEnumerationItem<T> : INotifyPropertyChanged 
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    
        private T _item;
    
        public BindableEnumerationItem(T item, string displayText)
        {
            _item = item;
            _displayText = displayText;
        }
    
        private string _displayText;
        public string DisplayText
        {
            get { return _displayText; }
            set
            {
                if (value != _displayText)
                {
                    _displayText = value;
                    PropertyChanged(this, new PropertyChangedEventArgs("DisplayText"));
                }
            }
        }
    
        public T Item
        {
            get { return _item; }
            set
            {
                _item = value;
                PropertyChanged(this, new PropertyChangedEventArgs("Item"));
            }
        }
    }
    

    最佳答案

    创建一个扩展,将 xaml 中的 View 模型中的命令连接到选择器,在本例中为组合框。

    public partial class Extensions
    {
        public static readonly DependencyProperty SelectionChangedCommandProperty = DependencyProperty.RegisterAttached("SelectionChangedCommand", typeof(ICommand), typeof(Extensions), new UIPropertyMetadata((s, e) =>
        {
            var element = s as Selector;
    
            if (element != null)
            {
                element.SelectionChanged -= OnSelectionChanged;
    
                if (e.NewValue != null)
                {
                    element.SelectionChanged += OnSelectionChanged;
                }
            }
        }));
    
        public static ICommand GetSelectionChangedCommand(UIElement element)
        {
            return (ICommand)element.GetValue(SelectionChangedCommandProperty);
        }
    
        public static void SetSelectionChangedCommand(UIElement element, ICommand value)
        {
            element.SetValue(SelectionChangedCommandProperty, value);
        }
    
        private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var element = sender as Selector;
            var command = element.GetValue(SelectionChangedCommandProperty) as ICommand;
    
            if (command != null && command.CanExecute(element.SelectedItem))
            {
                command.Execute(element.SelectedItem);
                e.Handled = true;
            }
        }
    }
    

    在处理值更改事件的 View 模型中创建命令。
    public ICommand EnumerationValueChangedCommand
    {
        get
        {
            return new Command(
                () =>
                    {
                        if (VM.MySelectionIsAnEnumeration == Values.Baz)
                        {
                            //Prompt the user and get DialogResult.
                            bool answerGiven = AskAQuestionAndGetAResult();
                            if (!answerGiven)
                                VM.MySelectionIsAnEnumeration = Values.Foo;
                        }
                    });
        }
    }
    

    然后使用该扩展名进行绑定(bind)。 ext 是您的扩展的命名空间。
    <ComboBox
        ItemsSource="{Binding ListOfComboBoxDisplayObjects}"
        SelectedValue="{Binding MySelectionIsAnEnumeration}"
        DisplayMemberPath="Text"
        SelectedValuePath="EnumerationValue"
        ext:Extensions.SelectionChangedCommand="{Binding EnumerationValueChangedCommand}"
        Height="27" />
    

    关于c# - 如何从 PropertyChanged 事件中正确重置与 ComboBox 关联的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41958534/

    相关文章:

    c# - WPF 应用程序中的音频截断

    jquery - 如何使用 jQuery UI 组合框创建新值

    C#监听80端口

    c# - 更好的JPG或BMP?

    wpf - WPF Window 无法识别同一项目中定义的用户控件时该怎么办?

    c# - 如何将文本框的文本双向数据绑定(bind)到依赖项属性

    C# Windows 窗体如何根据第一个组合框中的选择更改第二个组合框的值

    c# - 使用 List<string> 填充组合框

    c# - 错误 SQL :The conversion of a varchar data type to a datetime data type resulted in an out-of-range value

    c# - 在 getter 中声明变量