2012-04-09 10 views
6

Tengo un ComboBox cuyas propiedades ItemsSource y SelectedValue están asociadas a un modelo. Algunas veces, el modelo necesita ajustar el elemento seleccionado a uno diferente, pero cuando lo hago en el modelo, el valor del modelo no se refleja en la Vista, aunque SelectedValue esté configurado correctamente (marcado tanto con snoop como en SelectionChanged controlador de eventos).¿Por qué WPF ComboBox no refleja correctamente el valor encuadernado?

Para ilustrar el problema, aquí es una xaml simple:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Grid> 
     <ComboBox Height="25" Width="120" SelectedValue="{Binding SelectedValue}" SelectedValuePath="Key" ItemsSource="{Binding PossibleValues}" DisplayMemberPath="Value"/> 
    </Grid> 
</Window> 

Y aquí es el modelo:

using System.Collections.Generic; 
using System.Windows; 
using System.ComponentModel; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     int m_selectedValue = 2; 
     Dictionary<int, string> m_possibleValues = new Dictionary<int, string>() { { 1, "one" }, { 2, "two" }, { 3, "three" }, {4,"four"} }; 

     public int SelectedValue 
     { 
     get { return m_selectedValue; } 
     set 
     { 
      if (value == 3) 
      { 
       m_selectedValue = 1; 
      } 
      else 
      { 
       m_selectedValue = value; 
      } 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedValue")); 
     } 
     } 
     public Dictionary<int, string> PossibleValues 
     { 
     get { return m_possibleValues; } 
     set { m_possibleValues = value; } 
     } 


     public MainWindow() 
     { 
     InitializeComponent(); 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 
} 

que esperaba que la conducta es la siguiente:

  1. Inicialmente, se selecciona dos
  2. Seleccione "uno" -> ComboBox displa ys "uno"
  3. Seleccione "dos" -> pantallas ComboBox "dos"
  4. Seleccione "tres" -> ComboBox muestra "uno"
  5. Seleccione "cuatro" -> muestra ComboBox "cuatro"

Sin embargo, en el n. ° 4, se muestra "tres". ¿Por qué? El valor en el modelo se cambió a 1 ("uno"), pero la vista todavía muestra 3 ("tres").

He encontrado una solución al actualizar explícitamente el destino de enlace en un controlador de evento SelectionChanged, pero esto parece incorrecto. ¿Hay alguna otra manera de lograr esto?

+0

¿No debería el ComboBox mostrar el estado actual del modelo? Puede que no sea intuitivo, pero es lo correcto para mostrar. Este es en realidad un ejemplo simplificado. El caso de uso real es un poco más complicado, pero se reduce al mismo problema – Rado

+0

¿Ha intentado hacer el SelectedValue explícitamente bidireccional? {Binding Path = SelectedValue, Mode = TwoWay} –

+0

@JacobProffitt El modo es TwoWay por defecto, pero intenté configurarlo explícitamente y el problema persiste. – Rado

Respuesta

5

Al seleccionar un elemento, el motor de encuadernación actualizará el modelo e ignorará cualquier PropertyChanged de la propiedad que acaba de cambiar durante el tiempo que está invocando el elemento de creación de propiedades. Si no fuera así, las cosas podrían entrar en un ciclo infinito.

El sistema asume que el colocador no cambiará el valor que se pasó. La solución aquí es simple: use Dispatcher.BeginInvoke() o configure IsAsync=True en su enlace para obtener el comportamiento que está buscando. Sin embargo, personalmente desaconsejo encarecidamente que haga esto. No solo porque presenta un montón de nuevos problemas relacionados con los tiempos, sino principalmente porque es una solución a un problema que no debería existir en primer lugar.

Simplemente no hagas esto. No solo se aplica a WPF: cualquier cosa que cambie a un colocador podría esperar que el valor que acaba de establecer se haya configurado correctamente no arroja ninguna excepción. Cambiar el valor dentro del setter es contra intuitivo y podría morderte más tarde. Además, puede ser realmente molesto para el usuario de su aplicación ver que el cuadro combinado ignora su selección.

Si no le gusta el valor que se pasa, ejecute una excepción y use Binding.ValidatesOnExceptions para decirle a WPF que se espera.Si no desea que el usuario seleccione este valor, no lo incluya en la lista en primer lugar. Si el elemento no debe estar disponible condicionalmente debido a otras reglas comerciales, filtre la lista de forma dinámica o aplique desencadenantes.

+0

'IsAsync = True' funciona. Al leer la descripción, no estoy seguro de por qué lo hace, ya que no es para esto, pero sí :). Usted mencionó que desaconseja esta implementación. ¿Alguna sugerencia de lo que puedo hacer? El caso de uso real es el siguiente: tengo una cantidad predefinida de perfiles de valores que debo intentar aproximar. Esto se correlaciona muy bien con un paradigma de combobox ya que el usuario debe seleccionar uno de los valores predefinidos. Sin embargo, hay ciertos casos en los que no puedo alcanzar el valor seleccionado, para no engañar al usuario, me gusta cambiar el valor a un valor "intermedio". – Rado

+0

Para agregar al comentario anterior, otra forma de pensar acerca de lo que hace este setter es pensarlo como un "deseo". Como en, "Deseo establecer este valor, pero si no puedes, lo más cerca posible" – Rado

+1

No estoy de acuerdo. El objetivo de tener una propiedad "setter" es proteger el campo de respaldo (que podría hacerse público). La propiedad DEBERÍA realizar comprobaciones y validación del valor y garantizar que sea válido. Si no es así, lanzar una excepción no siempre es una solución práctica. –

2

He tenido problemas persistentes con la actualización adecuada del control al enlazar las propiedades SelectedValue y/o SelectedItem de un control ComboBox.

Para resolver esto, tuve que enlazar a la propiedad SelectedIndex en su lugar. Fue más complicado tratar con una propiedad de 'índice' en mi ViewModel, pero resolvió el problema con el ComboBox sin actualizar.

+0

Probé todos SelectedValue, SelectedItem y SelectedIndex con el mismo resultado. – Rado

Cuestiones relacionadas