2011-12-22 2 views
16

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

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

+1

Это старый, но будет http://stackoverflow.com/questions/314503/how-to-prevent-cancel-a-comboboxs-value-change-in-c помочь? –

+0

Я собирался сказать, почему вы не используете javascript для этой проверки, но потом я заметил его приложение winform извините :( – JonH

+0

Флаг bool - это стандартное решение. Не указывая пользователю способ выбора элемента, просто не включая его в коллекции Items является хорошим решением. –

ответ

15

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

WPF: Cancel a user selection in a databound ListBox?

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

+0

PERFECT! СПАСИБО! – 00jt

+1

Aphex, я люблю тебя – MxFragz

+1

Ввод этой логики в Property setter - плохая практика.Если что-то другое, кроме ComboBox/ListBox, изменит это свойство, разработчик, создающий его, будет просто изменять значение, а не открывать диалоговое окно. Это уменьшает гибкость свойства – MarkyDD

1

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

+0

Да, я тоже думал об этом, я бы хотел увидеть реализацию. – CRice

18

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

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

Проверка обработчика событий 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; 
     } 
    } 
} 
0

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

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

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