wpf combobox selecteditem

WPF ComboBox SelectedItem-cambiar al valor anterior



(4)

Cuando el usuario dice "no", WPF no sabe que el valor ha cambiado. En lo que respecta a WPF, el valor es cualquiera que sea el usuario seleccionado.

Puede intentar subir una notificación de cambio de propiedad:

public object SelItem { get { ... } set { if (!CancelChange()) { this.selItem = value; } OnPropertyChanged("SelItem"); } }

El problema es que la notificación de cambio ocurre dentro del mismo contexto del evento de selección. Por lo tanto, WPF lo ignora porque ya sabe que la propiedad ha cambiado, ¡al elemento que el usuario seleccionó!

Lo que debe hacer es elevar el evento de notificación en un mensaje separado:

public object SelItem { get { ... } set { if (CancelChange()) { Dispatcher.BeginInvoke((ThreadStart)delegate { OnPropertyChanged("SelItem"); }); return; } this.selItem = value; OnPropertyChanged("SelItem"); } }

WPF procesará este mensaje una vez que haya terminado de procesar el evento de selección de cambio y, por lo tanto, revertirá el valor en la vista a lo que debería ser.

Su VM obviamente necesitará acceso al Dispatcher actual. Vea la publicación de mi blog en una clase de VM base si necesita algunos consejos sobre cómo hacer esto.

Tengo un ComboBox que tiene el SelectedItem enlazado al ViewModel.

<ComboBox SelectedItem="{Binding SelItem, Mode=TwoWay}" ItemsSource="{Binding MyItems}">

Cuando el usuario selecciona un nuevo artículo en el ComboBox de la Vista, quiero mostrar un mensaje y verificar que desea hacer el cambio.

En el ajustador de propiedades SetItem en el modelo de vista, muestro un diálogo para confirmar la selección. Cuando dicen que sí, funciona bien.

Mi problema es que cuando el usuario hace clic en "No", no estoy seguro de a quién le debo devolver el ComboBox para volver al valor anterior. La propiedad en el ViewModel tiene el valor más antiguo correcto, sin embargo, en la Vista, el ComboBox muestra el Valor recién seleccionado.

Quiero que el usuario seleccione un elemento, confirme que quiere seguir adelante con él, y si decide no hacerlo, quiero que ComboBox vuelva al elemento anterior.

¿Cómo puedo lograr esto? ¡Gracias!


Gracias por esta pregunta y respuestas. El Dispatcher.BeginInvoke me ayudó y formó parte de mi solución final, pero la solución anterior no funcionó en mi aplicación WPF 4.

Junté una pequeña muestra para averiguar por qué. Tuve que agregar un código que en realidad cambió el valor de la variable miembro subyacente temporalmente para que cuando WPF volviera a consultar al captador, viera que el valor cambiaba. De lo contrario, la interfaz de usuario no reflejó correctamente la cancelación y la llamada BeginInvoke () no hizo nada.

Aquí hay una publicación de mi blog con mi muestra que muestra una implementación que no funciona y que funciona.

Mi compositor terminó pareciéndose a esto:

private Person _CurrentPersonCancellable; public Person CurrentPersonCancellable { get { Debug.WriteLine("Getting CurrentPersonCancellable."); return _CurrentPersonCancellable; } set { // Store the current value so that we can // change it back if needed. var origValue = _CurrentPersonCancellable; // If the value hasn''t changed, don''t do anything. if (value == _CurrentPersonCancellable) return; // Note that we actually change the value for now. // This is necessary because WPF seems to query the // value after the change. The combo box // likes to know that the value did change. _CurrentPersonCancellable = value; if ( MessageBox.Show( "Allow change of selected item?", "Continue", MessageBoxButton.YesNo ) != MessageBoxResult.Yes ) { Debug.WriteLine("Selection Cancelled."); // change the value back, but do so after the // UI has finished it''s current context operation. Application.Current.Dispatcher.BeginInvoke( new Action(() => { Debug.WriteLine( "Dispatcher BeginInvoke " + "Setting CurrentPersonCancellable." ); // Do this against the underlying value so // that we don''t invoke the cancellation question again. _CurrentPersonCancellable = origValue; OnPropertyChanged("CurrentPersonCancellable"); }), DispatcherPriority.ContextIdle, null ); // Exit early. return; } // Normal path. Selection applied. // Raise PropertyChanged on the field. Debug.WriteLine("Selection applied."); OnPropertyChanged("CurrentPersonCancellable"); } }


Mi forma de hacerlo es dejar que el cambio se realice y realizar la validación en un lambda que sea BeginInvoked en el Dispatcher.

public ObservableCollection<string> Items { get; set; } private string _selectedItem; private string _oldSelectedItem; public string SelectedItem { get { return _selectedItem; } set { _oldSelectedItem = _selectedItem; _selectedItem = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem")); } Dispatcher.BeginInvoke(new Action(Validate)); } } private void Validate() { if (SelectedItem == "Item 5") { if (MessageBox.Show("Keep 5?", "Title", MessageBoxButton.YesNo) == MessageBoxResult.No) { SelectedItem = _oldSelectedItem; } } }

o en su ViewModel:

Synchronization.Current.Post(new SendOrPostCallback(Validate), null);


Otra forma de hacerlo (asegúrate de leer también los comentarios):

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

Desde el enlace: otra solución para el problema de la llamada recursiva del controlador de eventos sin variable global es cancelar la asignación del controlador antes del cambio programático de selección y reasignarlo después de eso.

Ex:

cmb.SelectionChanged -= ComboBox_SelectionChanged; cmb.SelectedValue = oldSel.Key; cmb.SelectionChanged += ComboBox_SelectionChanged;