简体   繁体   English

如何取消ComboBox SelectionChanged事件?

[英]How to cancel a ComboBox SelectionChanged event?

Is there an easy method to prompt the user to confirm a combo box selection change and not process the change if the user selected no? 是否有一种简单的方法来提示用户确认组合框选择更改,如果用户选择“否”,则不处理更改?

We have a combo box where changing the selection will cause loss of data. 我们有一个组合框,更改选择将导致数据丢失。 Basically the user selects a type, then they are able to enter attributes of that type. 基本上用户选择一种类型,然后他们就能输入该类型的属性。 If they change the type we clear all of the attributes as they may no longer apply. 如果他们更改了类型,我们会清除所有属性,因为它们可能不再适用。 The problem is that to under the selection you raise the SelectionChanged event again. 问题是,在选择下,您再次引发SelectionChanged事件。

Here is a snippet: 这是一个片段:

if (e.RemovedItems.Count > 0)
{
    result = MessageBox.Show("Do you wish to continue?", 
        "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);

    if (result == MessageBoxResult.No)
    {
        if (e.RemovedItems.Count > 0)
            ((ComboBox)sender).SelectedItem = e.RemovedItems[0];
        else
            ((ComboBox)sender).SelectedItem = null;
    }
}

I have two solutions, neither of which I like. 我有两个解决方案,我都不喜欢。

  1. After the user selects 'No' , remove the SelectionChanged event handler, change the selected item and then register the SelectionChanged event handler again. 用户选择“否”后 ,删除SelectionChanged事件处理程序,更改所选项目,然后再次注册SelectionChanged事件处理程序。 This means you have to hold onto a reference of the event handler in the class so that you can add and remove it. 这意味着您必须在类中保留事件处理程序的引用,以便您可以添加和删除它。

  2. Create a ProcessSelectionChanged boolean as part of the class. 创建ProcessSelectionChanged布尔值作为类的一部分。 Always check it at the start of the event handler. 始终在事件处理程序的开头检查它。 Set it to false before we change the selection back and then reset it to true afterwards. 在我们更改选择之前将其设置为false,然后将其重置为true。 This will work, but I don't like using flags to basically nullify an event handler. 这将工作,但我不喜欢使用标志基本上使事件处理程序无效。

Anyone have an alternative solution or an improvement on the ones I mention? 任何人都有替代解决方案或改进我提到的?

I found this good implementation. 我发现这个很好的实现。

 private bool handleSelection=true;

private void ComboBox_SelectionChanged(object sender,
                                        SelectionChangedEventArgs e)
        {
            if (handleSelection)
            {
                MessageBoxResult result = MessageBox.Show
                        ("Continue change?", MessageBoxButton.YesNo);
                if (result == MessageBoxResult.No)
                {
                    ComboBox combo = (ComboBox)sender;
                    handleSelection = false;
                    combo.SelectedItem = e.RemovedItems[0];
                    return;
                }
            }
            handleSelection = true;
        }

source: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html 来源: http//www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html

I remember needing to do this a while back. 我记得有一段时间需要这样做。 It took me about a week of research and attempts before I found a good solution. 在我找到一个好的解决方案之前,我花了大约一周的研究和尝试。 I posted it here: 我在这里发布了:

WPF: Cancel a user selection in a databound ListBox? WPF:取消数据绑定列表框中的用户选择?

FYI, it's an MV-VM based solution (if you aren't using the MV-VM pattern, you should be!) 仅供参考,这是一个基于MV-VM的解决方案(如果你没有使用MV-VM模式,你应该是!)

也许创建一个派生自ComboBox的类,并重写OnSelectedItemChanged (或OnSelectionChangeCommitted 。)

Validating within the SelectionChanged event handler allows you to cancel your logic if the selection is invalid, but I don't know of an easy way to cancel the event or item selection. 如果选择无效,则在SelectionChanged事件处理程序内进行验证允许您取消逻辑,但我不知道取消事件或项目选择的简单方法。

My solution was to sub-class the WPF combo-box and add an internal handler for the SelectionChanged event. 我的解决方案是对WPF组合框进行子类化,并为SelectionChanged事件添加内部处理程序。 Whenever the event fires, my private internal handler raises a custom SelectionChanging event instead. 每当事件触发时,我的私有内部处理程序都会引发自定义SelectionChanging事件。

If the Cancel property is set on the corresponding SelectionChangingEventArgs , the event isn't raised and the SelectedIndex is reverted to its previous value. 如果在相应的SelectionChangingEventArgs上设置了Cancel属性,则不会引发该事件,并且SelectedIndex将还原为其先前的值。 Otherwise a new SelectionChanged is raised that shadows the base event. 否则会引发一个新的SelectionChanged ,它会遮蔽基本事件。 Hopefully this helps! 希望这有帮助!


EventArgs and handler delegate for SelectionChanging event: SelectionChanging事件的EventArgs和handler委托:

public class SelectionChangingEventArgs : RoutedEventArgs
{
    public bool Cancel { get; set; }
}

public delegate void 
SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e);

ChangingComboBox class implementation: ChangingComboBox类的实现:

public class ChangingComboBox : ComboBox
{
    private int _index;
    private int _lastIndex;
    private bool _suppress;

    public event SelectionChangingEventHandler SelectionChanging;
    public new event SelectionChangedEventHandler SelectionChanged;

    public ChangingComboBox()
    {
        _index = -1;
        _lastIndex = 0;
        _suppress = false;
        base.SelectionChanged += InternalSelectionChanged;
    }

    private void InternalSelectionChanged(Object s, SelectionChangedEventArgs e)
    {
        var args = new SelectionChangingEventArgs();
        OnSelectionChanging(args);
        if(args.Cancel)
        {
            return;
        }
        OnSelectionChanged(e);
    }

    public new void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        if (_suppress) return;

        // The selection has changed, so _index must be updated
        _index = SelectedIndex;
        if (SelectionChanged != null)
        {
            SelectionChanged(this, e);
        }
    }

    public void OnSelectionChanging(SelectionChangingEventArgs e)
    {
        if (_suppress) return;

        // Recall the last SelectedIndex before raising SelectionChanging
        _lastIndex = (_index >= 0) ? _index : SelectedIndex;
        if(SelectionChanging == null) return;

        // Invoke user event handler and revert to last 
        // selected index if user cancels the change
        SelectionChanging(this, e);
        if (e.Cancel)
        {
            _suppress = true;
            SelectedIndex = _lastIndex;
            _suppress = false;
        }
    }
}

I do not believe using the dispatcher to post (or delay) a property update is a good solution, it is more of a workaround that is not really needed. 我不相信使用调度程序发布(或延迟)属性更新是一个很好的解决方案,它更像是一种并非真正需要的解决方法。 The following solution i fully mvvm and it does not require a dispatcher. 以下解决方案我完全mvvm,它不需要调度程序。

  • First Bind the SelectedItem with an Explicit binding Mode. 首先使用显式绑定模式绑定SelectedItem。 //this enables us to decide whether to Commit using the UpdateSource() method the changes to the VM or to Revert using the UpdateTarget() method in the UI. //这使我们能够决定是使用UpdateSource()方法提交对VM的更改还是使用 UI中的UpdateTarget()方法进行恢复
  • Next, add a method to the VM that confirms if the change is allowed (This method can contain a service that prompts for user confirmation and returns a bool). 接下来,向VM添加一个方法,确认是否允许更改(此方法可以包含提示用户确认并返回bool的服务)。

In the view code behind hook to the SelectionChanged event and update the Source (ie, the VM) or the Target (ie the V) in accordance to whether the VM.ConfirmChange(...) method returned value as follows: 在视图代码后面挂钩到SelectionChanged事件并根据VM.ConfirmChange(...)方法返回值更新Source(即VM)或Target(即V),如下所示:

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if(e.AddedItems.Count != 0)
        {
            var selectedItem = e.AddedItems[0];
            if (e.AddedItems[0] != _ViewModel.SelectedFormatType)
            {
                var comboBoxSelectedItemBinder = _TypesComboBox.GetBindingExpression(Selector.SelectedItemProperty); //_TypesComboBox is the name of the ComboBox control
                if (_ViewModel.ConfirmChange(selectedItem))
                {
                    // Update the VM.SelectedItem property if the user confirms the change.
                    comboBoxSelectedItemBinder.UpdateSource();
                }
                else
                {
                    //otherwise update the view in accordance to the VM.SelectedItem property 
                    comboBoxSelectedItemBinder.UpdateTarget();
                }
            }
        }
    }

In WPF dynamically set the object with 在WPF中动态设置对象

    if (sender.IsMouseCaptured)
    {
      //perform operation
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM