【问题标题】:WPF ComboBox SelectedItem - change to previous valueWPF ComboBox SelectedItem - 更改为以前的值
【发布时间】:2011-02-04 19:47:21
【问题描述】:

我有一个将 SelectedItem 绑定到 ViewModel 的 ComboBox。

<ComboBox SelectedItem="{Binding SelItem, Mode=TwoWay}" ItemsSource="{Binding MyItems}">

当用户在 View ComboBox 中选择一个新项目时,我想显示一个提示并验证他们是否要进行更改。

在视图模型的 SetItem 属性设置器中,我显示一个对话框来确认选择。当他们说是时,它工作正常。

我的问题是,当用户点击“否”时,我不确定谁会得到 ComboBox 恢复到以前的值。 ViewModel 中的属性具有正确的 旧值,但在视图中,组合框显示新选择的值。

我希望用户选择一个项目,确认他们想要继续执行它,如果他们 决定不这样做,我希望 ComboBox 恢复到上一个​​项目。

我怎样才能做到这一点? 谢谢!

【问题讨论】:

    标签: wpf combobox selecteditem


    【解决方案1】:

    我的做法是让更改通过并在 Dispatcher 中 BeginInvoked 的 lambda 中执行验证。

        public ObservableCollection<string> Items { get; set; }
        private string _selectedItem;
        private string _oldSelectedItem;
        public string SelectedItem
        {
            get { return _selectedItem; }
            set {
                _oldSelectedItem = _selectedItem;
                _selectedItem = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
                }
                Dispatcher.BeginInvoke(new Action(Validate));                
            }
        }
    
        private void Validate()
        {            
            if (SelectedItem == "Item 5")
            {
                if (MessageBox.Show("Keep 5?", "Title", MessageBoxButton.YesNo) == MessageBoxResult.No)
                {
                    SelectedItem = _oldSelectedItem;
                }
            }
        }
    

    或在您的 ViewModel 中:

       Synchronization.Current.Post(new SendOrPostCallback(Validate), null);
    

    【讨论】:

      【解决方案2】:

      另一种方法(确保您还阅读了 cmets):

      http://amazedsaint.blogspot.com/2008/06/wpf-combo-box-cancelling-selection.html

      从链接: 没有全局变量的事件处理程序递归调用问题的另一种解决方案是在程序选择更改之前取消处理程序分配,然后重新分配。

      例如:

      cmb.SelectionChanged -= ComboBox_SelectionChanged;
      cmb.SelectedValue = oldSel.Key;
      cmb.SelectionChanged += ComboBox_SelectionChanged;
      

      【讨论】:

        【解决方案3】:

        当用户说“不”时,WPF 不知道该值已更改。就 WPF 而言,值是用户选择的任何值。

        您可以尝试提出属性更改通知:

        public object SelItem
        {
            get { ... }
            set
            {
                if (!CancelChange())
                {
                    this.selItem = value;
                }
        
                OnPropertyChanged("SelItem");
            }
        }
        

        问题是,更改通知发生在选择事件的同一上下文中。因此,WPF 会忽略它,因为它已经知道属性已更改 - 更改为用户选择的项目!

        您需要做的是在单独的消息中引发通知事件:

        public object SelItem
        {
            get { ... }
            set
            {
                if (CancelChange())
                {
                    Dispatcher.BeginInvoke((ThreadStart)delegate
                    {
                        OnPropertyChanged("SelItem");
                    });
                    return;
                }
        
                this.selItem = value;
                OnPropertyChanged("SelItem");
            }
        }
        

        WPF 将在 处理完选择更改事件后处理此消息,因此会将视图中的值恢复为应有的值。

        您的虚拟机显然需要访问当前的Dispatcher。如果您需要有关如何执行此操作的一些指示,请参阅基础 VM 类上的 my blog post

        【讨论】:

        【解决方案4】:

        感谢您提出这个问题和答案。 Dispatcher.BeginInvoke 帮助了我,并且是我最终解决方案的一部分,但上述解决方案在我的 WPF 4 应用程序中并不完全有效。

        我整理了一个小样本来找出原因。我必须添加实际上临时更改基础成员变量值的代码,以便当 WPF 重新查询 getter 时,它会看到值发生了变化。否则,UI 无法正确反映取消,并且 BeginInvoke() 调用没有执行任何操作。

        Here's a my blog post 我的示例显示了一个非工作和一个工作实现。

        我的二传手最终看起来像这样:

            private Person _CurrentPersonCancellable;
            public Person CurrentPersonCancellable
            {
                get
                {
                    Debug.WriteLine("Getting CurrentPersonCancellable.");
                    return _CurrentPersonCancellable;
                }
                set
                {
                    // Store the current value so that we can 
                    // change it back if needed.
                    var origValue = _CurrentPersonCancellable;
        
                    // If the value hasn't changed, don't do anything.
                    if (value == _CurrentPersonCancellable)
                        return;
        
                    // Note that we actually change the value for now.
                    // This is necessary because WPF seems to query the 
                    //  value after the change. The combo box
                    // likes to know that the value did change.
                    _CurrentPersonCancellable = value;
        
                    if (
                        MessageBox.Show(
                            "Allow change of selected item?", 
                            "Continue", 
                            MessageBoxButton.YesNo
                        ) != MessageBoxResult.Yes
                    )
                    {
                        Debug.WriteLine("Selection Cancelled.");
        
                        // change the value back, but do so after the 
                        // UI has finished it's current context operation.
                        Application.Current.Dispatcher.BeginInvoke(
                                new Action(() =>
                                {
                                    Debug.WriteLine(
                                        "Dispatcher BeginInvoke " + 
                                        "Setting CurrentPersonCancellable."
                                    );
        
                                    // Do this against the underlying value so 
                                    //  that we don't invoke the cancellation question again.
                                    _CurrentPersonCancellable = origValue;
                                    OnPropertyChanged("CurrentPersonCancellable");
                                }),
                                DispatcherPriority.ContextIdle,
                                null
                            );
        
                        // Exit early. 
                        return;
                    }
        
                    // Normal path. Selection applied. 
                    // Raise PropertyChanged on the field.
                    Debug.WriteLine("Selection applied.");
                    OnPropertyChanged("CurrentPersonCancellable");
                }
            }
        

        【讨论】:

        • 我已经在我的二传手中完成了这个。但是这对我不起作用。
        • 您使用的是什么版本的 .net?你看到了什么行为?
        • 我可以确认有必要将属性支持字段更改为新值,然后才能将其重置为旧值。还有其他用例需要这种行为。在那里,可能还需要为临时值发出属性更改事件,以便让 WPF 之后接受对先前/不同值的更改。
        猜你喜欢
        • 1970-01-01
        • 2011-01-10
        • 2011-04-27
        • 1970-01-01
        • 2012-06-19
        • 2016-06-15
        • 2011-11-01
        • 2017-02-20
        • 1970-01-01
        相关资源
        最近更新 更多