如何取消 ComboBox SelectionChanged 事件?

问题描述 投票:0回答:8

有没有一种简单的方法可以提示用户确认组合框选择更改,并且如果用户选择“否”则不处理更改?

我们有一个组合框,更改选择将导致数据丢失。基本上,用户选择一种类型,然后他们就可以输入该类型的属性。如果他们更改类型,我们将清除所有属性,因为它们可能不再适用。问题是,在选择下,您再次引发

SelectionChanged
事件。

这是一个片段:

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;
    }
}

我有两种解决方案,但我都不喜欢。

  1. 用户选择“否”后,删除 SelectionChanged

     事件处理程序,更改所选项目,然后再次注册 
    SelectionChanged
     事件处理程序。这意味着您必须保留类中事件处理程序的引用,以便您可以添加和删除它。

  2. 创建一个

    ProcessSelectionChanged

     布尔值作为类的一部分。始终在事件处理程序开始时检查它。在我们更改回选择之前将其设置为 false,然后将其重置为 true。这会起作用,但我不喜欢使用标志来基本上使事件处理程序无效。

有人对我提到的解决方案有替代解决方案或改进吗?

c# wpf combobox selectionchanged
8个回答
27
投票
我发现了这个很好的实现。

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; }

来源:

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


1
投票
也许创建一个派生自

ComboBox

 的类,并覆盖 
OnSelectedItemChanged
(或 
OnSelectionChangeCommitted
。)


1
投票

SelectionChanged

 事件处理程序中进行验证允许您在选择无效时取消逻辑,但我不知道取消事件或项目选择的简单方法。

我的解决方案是对 WPF 组合框进行子类化并为

SelectionChanged

 事件添加内部处理程序。每当事件触发时,我的私有内部处理程序都会引发自定义 
SelectionChanging
 事件。

如果在相应的

Cancel

 上设置了 
SelectionChangingEventArgs
 属性,则不会引发该事件,并且 
SelectedIndex
 将恢复为其之前的值。否则,会引发一个新的 
SelectionChanged
 来遮蔽基本事件。希望这有帮助!


SelectionChanging 事件的EventArgs 和处理程序委托:

public class SelectionChangingEventArgs : RoutedEventArgs { public bool Cancel { get; set; } } public delegate void SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e);

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; } } }
    

1
投票
在 WPF 中使用

动态设置对象

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

0
投票
我不认为使用调度程序发布(或延迟)属性更新是一个好的解决方案,它更多的是一种并不真正需要的解决方法。以下解决方案是完全 mvvm 并且不需要调度程序。

    首先使用显式绑定模式绑定SelectedItem。 //这使我们能够决定是在 UI 中
  • 使用 UpdateSource() 方法提交对虚拟机的更改,还是使用 UpdateTarget() 方法恢复。 接下来,向虚拟机添加一个方法来确认是否允许更改(此方法可以包含提示用户确认并返回布尔值的服务)。
  • 在视图代码后面挂钩到 SelectionChanged 事件并根据 VM.ConfirmChange(...) 方法是否返回值更新源(即 VM)或目标(即 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(); } } } }



0
投票

ComboBoxHelper.cs:

public class ComboBoxHelper { private readonly ComboBox _control; public ComboBoxHelper(ComboBox control) { _control = control; _control.PreviewMouseLeftButtonDown += _control_PreviewMouseLeftButtonDown; ; _control.PreviewMouseLeftButtonUp += _control_PreviewMouseLeftButtonUp; ; } public Func<bool> IsEditingAllowed { get; set; } public Func<object, bool> IsValidSelection { get; set; } public Action<object> OnItemSelected { get; set; } public bool CloseDropDownOnInvalidSelection { get; set; } = true; private bool _handledMouseDown = false; private void _control_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { var isEditingAllowed = IsEditingAllowed?.Invoke() ?? true; if (!isEditingAllowed) { e.Handled = true; return; } _handledMouseDown = true; } private void _control_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (!_handledMouseDown) return; _handledMouseDown = false; var fe = (FrameworkElement)e.OriginalSource; if (fe.DataContext != _control.DataContext) { //ASSUMPTION: Click was on an item and not the ComboBox itself (to open it) var item = fe.DataContext; var isValidSelection = IsValidSelection?.Invoke(item) ?? true; if (isValidSelection) { OnItemSelected?.Invoke(item); _control.IsDropDownOpen = false; } else if(CloseDropDownOnInvalidSelection) { _control.IsDropDownOpen = false; } e.Handled = true; } } }

它可以在自定义用户控件中使用,如下所示:

public class MyControl : UserControl { public MyControl() { InitializeComponent(); var helper = new ComboBoxHelper(MyComboBox); //MyComboBox is x:Name of the ComboBox in Xaml helper.IsEditingAllowed = () => return Keyboard.Modifiers != Modifiers.Shift; //example helper.IsValidSelection = (item) => return item.ToString() != "Invalid example."; helper.OnItemSelected = (item) => { System.Console.WriteLine(item); }; } }

这与 
SelectionChanged

事件无关,事件触发频率超过要求不会产生副作用。这样其他人就可以安全地收听该事件,例如更新他们的用户界面。还避免了:通过将事件处理程序中的选择重置为有效项目而导致的“递归”调用。

上述关于 

DataContext

的假设可能并不完全适合所有场景,但可以轻松调整。一种可能的替代方法是检查

ComboBox
是否是
e.OriginalSource
的视觉父级,而在选择项目时则不是。
    


0
投票


0
投票

if (e.KeyCode == Keys.Control || e.KeyCode == Keys.C) { Clipboard.SetText(comboBoxPatternSearch.Text); e.Handled = true; e.SuppressKeyPress = true; return; }

我认为 e.Handled 没有做任何事情,是 SuppressKeyPress 做到了。抱歉没有更多时间,只是想贡献这个,因为我正是遇到这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.