Как отменить событие ComboBox SelectionChanged? - программирование
Подтвердить что ты не робот

Как отменить событие 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;
    }
}

У меня есть два решения, ни один из которых мне не нравится.

  • После выбора пользователем "Нет" удалите обработчик события SelectionChanged, измените выбранный элемент и затем зарегистрируйте обработчик событий SelectionChanged снова. Это означает, что вам нужно удерживать ссылку обработчика события в классе, чтобы вы могли добавлять и удалять его.

  • Создайте ProcessSelectionChanged boolean как часть класса. Всегда проверяйте его в начале обработчика событий. Установите значение false, прежде чем мы изменим выделение назад, а затем reset на значение true. Это будет работать, но я не люблю использовать флаги, чтобы в принципе скрыть обработчик событий.

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

 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

Ответ 3

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

Ответ 4

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

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

Если свойство 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;
        }
    }
}

Ответ 5

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

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

В коде представления за крючком к событию SelectionChanged и обновлению источника (то есть виртуальной машины) или цели (то есть 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();
                }
            }
        }
    }