wpf 如何取消组合框SelectionChanged事件?

b0zn9rqh  于 2023-02-16  发布在  其他
关注(0)|答案(6)|浏览(314)

是否有简单的方法提示用户确认组合框选择更改,如果用户选择否,则不处理更改?
我们有一个组合框,改变选择会导致数据丢失。基本上用户选择一个类型,然后他们可以输入该类型的属性。如果他们改变类型,我们清除所有的属性,因为它们可能不再适用。问题是,在选择下,你再次引发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事件处理程序。这意味着您必须保留类中事件处理程序的引用,以便添加和移除它。
1.创建一个ProcessSelectionChanged布尔值作为类的一部分。总是在事件处理程序开始时检查它。在我们将选择更改回之前将它设置为false,然后将它重置为true。这将起作用,但我不喜欢使用标志来基本上使事件处理程序无效。
有人有替代的解决方案或改进我提到的吗?

brgchamk

brgchamk1#

我发现这个很好的实现。

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

yks3o0rb

yks3o0rb2#

也许创建一个从ComboBox派生的类,并覆盖OnSelectedItemChanged(或OnSelectionChangeCommitted)。

iklwldmw

iklwldmw3#

SelectionChanged事件处理程序中进行验证允许您在选择无效时取消逻辑,但我不知道取消事件或项选择的简单方法。
我的解决方案是对WPF组合框进行子类化,并为SelectionChanged事件添加一个内部处理程序。无论何时触发该事件,我的私有内部处理程序都会引发一个自定义的SelectionChanging事件。
如果在对应的SelectionChangingEventArgs上设置了Cancel属性,则不会引发该事件,并且SelectedIndex将恢复为以前的值。否则,将引发一个新的SelectionChanged,该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;
        }
    }
}
7qhs6swi

7qhs6swi4#

在WPF中动态设置对象

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

91zkwejq5#

我不认为使用调度器来发布(或延迟)属性更新是一个好的解决方案,它更多的是一个并不真正需要的解决方案。下面的解决方案完全是mvvm,它不需要调度器。

  • 首先使用显式绑定模式绑定SelectedItem。//这使我们能够决定是在UI中使用UpdateSource()方法提交对VM的更改,还是使用UpdateTarget()方法还原更改。
  • 接下来,向VM添加一个确认是否允许更改的方法(此方法可以包含一个提示用户确认并返回bool的服务)。

在视图代码后挂钩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();
                }
            }
        }
    }
yqhsw0fo

yqhsw0fo6#

这是一个古老的问题,但经过一次又一次的问题,我想出了这个解决方案:

组合框助手.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;
        }
    }
}

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

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事件无关,不会因事件触发频率超过要求而产生副作用。因此其他人可以安全地侦听该事件,例如更新其UI。还应避免:将事件处理程序中的选择重置为有效项而导致的“递归”调用。
以上关于DataContext的假设可能并不完全适合所有场景,但可以很容易地调整。一个可能的替代方案是检查ComboBox是否是e.OriginalSource的可视父项,当选择一个项时,它不是。

相关问题