Как отменить событие ComboBox SelectionChanged?

есть ли простой способ предложить пользователю подтвердить изменение выбора поля со списком и не обрабатывать изменение, если пользователь выбрал нет?

у нас есть поле со списком, где изменение выбора приведет к потере данных. В основном пользователь выбирает тип, затем они могут вводить атрибуты этого типа. Если они изменяют тип, мы очищаем все атрибуты, поскольку они могут больше не применяться. Проблема в том, что при выборе вы поднимаете 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 boolean как часть класс. Всегда проверяйте его в начале обработчика событий. Установите его в false, прежде чем мы изменим выбор обратно, а затем сбросить его в true после этого. Это будет работать, но мне не нравится использовать флаги, чтобы в основном аннулировать обработчик событий.

у кого-нибудь есть альтернативное решение или улучшение по сравнению с теми, о которых я упоминаю?

6 ответов


Я помню, что мне нужно было сделать это некоторое время назад. Мне потребовалось около недели исследований и попыток, прежде чем я нашел хорошее решение. Я разместил его здесь:

WPF: отменить выбор пользователя в списке базы данных?

FYI, это решение на основе M-V-VM (если вы не используете шаблон M-V-VM, вы должны быть!)


Я нашел эту хорошую реализацию.

 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


возможно, создайте класс, производный от ComboBox и переопределить OnSelectedItemChanged (или OnSelectionChangeCommitted.)


проверки в SelectionChanged обработчик событий позволяет отменить логику, если выбор недопустим, но я не знаю простого способа отменить выбор события или элемента.

моим решением было подкласс WPF combo-box и добавить внутренний обработчик для SelectionChanged событие. Всякий раз, когда событие срабатывает, мой личный внутренний обработчик вызывает обычай событие.

если Cancel свойство устанавливается на соответствующее SelectionChangingEventArgs событие не поднял и SelectedIndex возвращается к предыдущему значению. В противном случае новый SelectionChanged поднимается, что тени базового события. Надеюсь, это поможет!


EventArgs и делегат обработчика для события SelectionChanging:

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

Я не считаю, что использование диспетчера для публикации (или задержки) обновления свойств является хорошим решением, это скорее обходной путь, который на самом деле не нужен. Следующее решение i полностью mvvm и не требует диспетчера.

  • сначала свяжите SelectedItem с явным режимом привязки. //это позволяет нам решить, следует ли фиксация с помощью метода UpdateSource () изменения в VM или в возврат с помощью метода UpdateTarget () in пользовательский интерфейс.
  • затем добавьте метод к виртуальной машине, который подтверждает, разрешено ли изменение (этот метод может содержать службу, которая запрашивает подтверждение пользователя и возвращает bool).

в коде представления за крючком к событию SelectionChanged и обновите источник (т. е. VM) или цель (т. е. V) в соответствии с тем, является ли VM.ConfirmChange(...) метод возвращает значение следующим образом:

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

в WPF динамически установите объект с

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