2011-12-22 15 views
16

Czy istnieje prosta metoda, aby poprosić użytkownika o potwierdzenie zmiany wyboru pola kombi i nie przetworzyć zmiany, jeśli użytkownik wybrał opcję nie?Jak anulować zdarzenie ComboBox SelectionChanged?

Mamy pole kombi, w którym zmiana wyboru spowoduje utratę danych. Zasadniczo użytkownik wybiera typ, a następnie jest w stanie wprowadzić atrybuty tego typu. Jeśli zmienią typ, usuniemy wszystkie atrybuty, ponieważ mogą one już nie mieć zastosowania. Problem polega na tym, że pod wyborem ponownie podnosisz zdarzenie SelectionChanged.

Oto fragment:

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

Mam dwa rozwiązania, z których żaden nie lubię.

  1. Po użytkownik wybiera „nie”, usuń obsługi SelectionChanged zdarzeń zmienić wybrany element, a następnie ponownie zarejestrować obsługi SelectionChanged zdarzeń. Oznacza to, że musisz trzymać referencję obsługi zdarzeń w klasie, aby móc ją dodawać i usuwać.

  2. Utwórz boolean ProcessSelectionChanged jako część klasy. Zawsze sprawdzaj to na początku programu obsługi zdarzeń. Ustaw go na false, zanim zmienimy wybór, a następnie zresetujemy go do wartości true. To zadziała, ale nie lubię używać flag, aby w zasadzie unieważnić obsługę zdarzeń.

Ktoś ma alternatywne rozwiązanie lub ulepszenie tych, o których wspomniałem?

+1

To stary ale http://stackoverflow.com/questions/314503/how-to-prevent-cancel-a-comboboxs-value-change-in-c pomóc? –

+0

Chciałem powiedzieć, dlaczego nie używałeś javascript, aby to sprawdzić, ale potem zauważyłem jego winform app sorry :(. – JonH

+0

Flaga bool jest standardowym rozwiązaniem.Nie daje użytkownikowi sposobu na wybranie pozycji, po prostu nie włączając jej w kolekcji Przedmiotów jest dobrym rozwiązaniem –

Odpowiedz

15

Pamiętam konieczności to zrobić jakiś czas temu. Zajęło mi to około tygodnia badań i prób, zanim znalazłem dobre rozwiązanie. Zamieściłem tutaj:

WPF: Cancel a user selection in a databound ListBox?

FYI, jest to rozwiązanie oparte MV-VM (jeśli nie są przy użyciu wzoru MV-VM, powinno być!)

+0

PERFECT! DZIĘKUJĘ CI! – 00jt

+1

Aphex, kocham cię – MxFragz

+1

Umieszczenie takiej logiki w selektorze nieruchomości jest złą praktyką.Jeśli coś innego niż ComboBox/ListBox zmieni tę właściwość, programista piszący ją będzie oczekiwał jedynie modyfikowania wartości, a nie otwierania również okna dialogowego. Zmniejsza to elastyczność Nieruchomości – MarkyDD

1

Może utworzyć klasy wynikających z ComboBox i zastąpić OnSelectedItemChanged (lub OnSelectionChangeCommitted.)

+0

Tak, tak też myślałem, chciałbym zobaczyć implementację. – CRice

18

Znalazłem dobrą realizację.

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

źródło: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html

1

Members ramach obsługi zdarzeń SelectionChanged pozwala zrezygnować z logiki, jeśli wybór jest nieważny, ale nie wiem, w prosty sposób, aby anulować zaznaczenie zdarzenia lub elementu.

Moje rozwiązanie polegało na podklasowaniu listy kombi WPF i dodaniu wewnętrznej procedury obsługi zdarzenia SelectionChanged. Za każdym razem, gdy zdarzenie jest uruchamiane, moja prywatna wewnętrzna procedura obsługi podnosi zamiast tego niestandardowe zdarzenie SelectionChanging.

Jeśli właściwość Cancel jest ustawiona na odpowiadającym SelectionChangingEventArgs, zdarzenie nie jest wywoływane, a SelectedIndex powraca do poprzedniej wartości. W przeciwnym razie powstaje nowy obiekt SelectionChanged, który powoduje cieniowanie zdarzenia podstawowego. Mam nadzieję, że to pomoże!


EventArgs i obsługi delegat na razie SelectionChanging:

public class SelectionChangingEventArgs : RoutedEventArgs 
{ 
    public bool Cancel { get; set; } 
} 

public delegate void 
SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e); 

ChangingComboBox klasa wdrożeniowe:

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

Nie wierzę, stosując dyspozytor, aby opublikować (lub opóźnić) aktualizację właściwości, jest dobrym rozwiązaniem, jest raczej rozwiązaniem, które nie jest tak naprawdę potrzebne. Poniższe rozwiązanie w pełni mvvm i nie wymaga dyspozytora.

  • Najpierw należy powiązać SelectedItem z jawnym trybem bindowania. // to pozwala nam zdecydować, czy do zatwierdzić za pomocą metody UpdateSource() zmiany w maszynie wirtualnej lub na Przywrócić za pomocą metody UpdateTarget() w interfejsie użytkownika.
  • Następnie dodaj metodę do maszyny wirtualnej, która potwierdzi, czy zmiana jest dozwolona (ta metoda może zawierać usługę, która monituje o potwierdzenie użytkownika i zwraca wartość bool).

W kodzie widoku z tyłu hak do zdarzenia SelectionChanged i aktualizować źródło (tj VM) lub docelowy (czyli V) zgodnie z czy VM.ConfirmChange (...) Metoda zwrócona wartość w następujący sposób:

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(); 
       } 
      } 
     } 
    } 
Powiązane problemy