Como cancelar um evento ComboBox SelectionChanged?

Existe um método fácil para solicitar ao usuário para confirmar uma alteração de seleção de checkbox de combinação e não processar a alteração se o usuário selecionou não?

Temos uma checkbox de combinação onde a alteração da seleção causará perda de dados. Basicamente, o usuário seleciona um tipo e, em seguida, é capaz de inserir atributos desse tipo. Se eles mudarem o tipo, limparemos todos os atributos, pois eles podem não se aplicar mais. O problema é que, sob a seleção, você aumenta o evento SelectionChanged novamente.

Aqui está um trecho:

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

Eu tenho duas soluções, nenhuma das quais eu gosto.

  1. Depois que o usuário selecionar ‘Não’ , remova o manipulador de events SelectionChanged , altere o item selecionado e, em seguida, registre o manipulador de events SelectionChanged novamente. Isso significa que você precisa manter uma referência do manipulador de events na class para poder adicioná-lo e removê-lo.

  2. Crie um booleano ProcessSelectionChanged como parte da class. Sempre verifique isso no início do manipulador de events. Defina-o como false antes de alterarmos a seleção e depois reconfigurar para true depois. Isso funcionará, mas não gosto de usar sinalizadores para basicamente anular um manipulador de events.

Alguém tem uma solução alternativa ou uma melhoria sobre os que eu menciono?

Lembro-me de precisar fazer isso há algum tempo. Demorei cerca de uma semana de pesquisa e tentativas antes de encontrar uma boa solução. Eu postei aqui:

WPF: cancelar uma seleção de usuário em um ListBox de vinculação de dados?

FYI, é uma solução baseada em MV-VM (se você não estiver usando o padrão MV-VM, você deve estar!)

Eu encontrei esta boa implementação.

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

fonte: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html

Talvez criar uma class derivada de ComboBox e replace o OnSelectedItemChanged (ou OnSelectionChangeCommitted .)

A validação no manipulador de events SelectionChanged permite que você cancele sua lógica se a seleção for inválida, mas não sei de uma maneira fácil de cancelar a seleção de evento ou item.

Minha solução foi subclassificar a checkbox de combinação do WPF e adicionar um manipulador interno para o evento SelectionChanged . Sempre que o evento é acionado, meu manipulador interno privado gera um evento SelectionChanging personalizado.

Se a propriedade Cancel estiver configurada no SelectionChangingEventArgs correspondente, o evento não será gerado e o SelectedIndex será revertido para seu valor anterior. Caso contrário, é gerado um novo SelectionChanged que faz sombra ao evento base. Espero que isso ajude!


EventArgs e delegado do manipulador para o evento SelectionChanging:

 public class SelectionChangingEventArgs : RoutedEventArgs { public bool Cancel { get; set; } } public delegate void SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e); 

Implementação da class 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; } } } 

Eu não acredito que usar o dispatcher para postar (ou atrasar) uma atualização de propriedade seja uma boa solução, é mais uma solução alternativa que não é realmente necessária. A solução a seguir eu totalmente mvvm e não requer um despachante.

  • Primeiro Vincule o SelectedItem a um modo de binding explícita. // isso nos permite decidir se Confirmar usando o método UpdateSource () as alterações na VM ou Reverter usando o método UpdateTarget () na interface do usuário.
  • Em seguida, adicione um método à VM que confirme se a alteração é permitida (esse método pode conter um serviço que solicita a confirmação do usuário e retorna um bool).

No código de visualização por trás do gancho para o evento SelectionChanged e atualizar a fonte (ou seja, a VM) ou o destino (ou seja, o V) de acordo com se o método VM.ConfirmChange (…) retornou o valor da seguinte forma:

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

No WPF, defina dinamicamente o object com

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