Как отменить событие ComboBox SelectionChanged? - PullRequest
20 голосов
/ 22 декабря 2011

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

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

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

Ответы [ 6 ]

19 голосов
/ 13 января 2012

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

 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

16 голосов
/ 08 марта 2012

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

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

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

1 голос
/ 06 марта 2012

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

Мое решение состояло в том, чтобы создать подкласс поля со списком 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;
        }
    }
}
1 голос
/ 22 декабря 2011

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

0 голосов
/ 13 августа 2018

В WPF динамически задайте объект с помощью

    if (sender.IsMouseCaptured)
    {
      //perform operation
    }
0 голосов
/ 28 февраля 2017

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

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

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

...