2011-12-05 19 views
18

Tengo controles que no actualizan las propiedades respectivas de los objetos vinculados hasta que se pierde el foco. Hay preguntas similares con declaraciones aceptadas que hacen referencia a DataSourceUpdateMode.OnPropertyChange siendo declaradas, lo que hago, pero el comportamiento persiste. Aquí hay una implementación de ejemplo. Trataré de ser minucioso, pero conciso. Se accede a la clase MyConfig a través de una propiedad en una clase de Singleton que llamo al Configuration.Control no actualizando inmediatamente la propiedad vinculada con INotifyPropertyChanged

[Serializable] 
public class MyConfig : INotifyPropertyChanged 
{ 
    public enum MyEnum 
    { 
     Foo, 
     Bar 
    } 

    public MyConfig() 
    { 
     MyProperty = MyEnum.Foo; 
    } 

    private MyEnum _MyProperty; 
    public MyEnum MyProperty 
    { 
     get { return _MyProperty; } 
     set { if (value != _MyProperty) { _MyProperty = value; OnPropertyChanged("MyProperty"); } } 
    } 

    [field: NonSerialized] 
    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) 
    { 
     if (string.IsNullOrEmpty(propertyName)) 
      throw new ArgumentNullException(propertyName); 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

public partial class ConfigForm : Form 
{ 
    public ConfigForm() 
    { 
     InitializeComponent(); 
     MyComboBox.Items.AddRange(Enum.GetNames(typeof(MyConfig.MyEnum))); 
    } 

    private void ConfigForm_Load(object sender, EventArgs e) 
    { 
     MyComboBox.DataSource = Enum.GetValues(typeof(MyConfig.MyEnum)); 
     MyComboBox.DataBindings.Add("SelectedItem", Configuration.Instance.MyConfig, "MyProperty", false, DataSourceUpdateMode.OnPropertyChanged); 
    } 
} 

No estoy seguro, dada la breve implementación siguiente, lo que podría pasar por alto para garantizar cambios inmediatos a la propiedad. Puedo cambiar, en este caso desde Foo a Bar en el ComboBox, pero a menos que elimine el foco del ComboBox, nada cambia. ¿Alguien tiene alguna idea?

Respuesta

23

El WinForms ComboBox es wonky con respecto a OnPropertyChanged. Aquí hay un código de un proyecto anterior que usé para obtener OnPropertyChanged trabajando de la manera que espero para la propiedad SelectedItem. Esto funciona para mi instancia específica, pero a veces me cuesta trabajar este escenario algunas veces. ¡Buena suerte!

/// <summary> 
/// A modification of the standard <see cref="ComboBox"/> in which a data binding 
/// on the SelectedItem property with the update mode set to DataSourceUpdateMode.OnPropertyChanged 
/// actually updates when a selection is made in the combobox. 
/// </summary> 
public class BindableComboBox : ComboBox 
{ 
    /// <summary> 
    /// Raises the <see cref="E:System.Windows.Forms.ComboBox.SelectionChangeCommitted"/> event. 
    /// </summary> 
    /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param> 
    protected override void OnSelectionChangeCommitted(EventArgs e) 
    { 
     base.OnSelectionChangeCommitted(e); 

     var bindings = this.DataBindings 
      .Cast<Binding>() 
      .Where(x => 
       x.PropertyName == "SelectedItem" && 
       x.DataSourceUpdateMode == DataSourceUpdateMode.OnPropertyChanged); 
     foreach (var binding in bindings) 
     { 
      // Force the binding to update from the new SelectedItem 
      binding.WriteValue(); 

      // Force the Textbox to update from the binding 
      binding.ReadValue(); 
     } 
    } 
} 
+1

funciona como un encanto. ¡Un millón de gracias! Y me has enseñado un poco sobre la personalización de los controles existentes :) –

+1

Esta es la única solución que he encontrado que realmente funciona. Gracias. –

2

@Nicholas Piasecki merece crédito por que me lleva a mi solución es así, a menos que no se podía llegar a una solución sobre la base de su respuesta, por favor voten por su respuesta.


Hubo tres cambios principales que tuve que hacer para que esta solución funcionara en mi situación.

  • Estaba intentando acceder a la propiedad del objeto vinculado a la propiedad SelectedValue del ComboBox. Por lo tanto, tuve que incluir el nombre de la propiedad "SelectedValue" en la cláusula Linq where.

  • Si va a configurar el enlace de datos a través del diseñador de formularios en Visual Studio, y simplemente establecer lo que el SelectedValue o SelectedItem está obligado a, el modo de actualización de origen de datos predeterminado es "onvalidation". Puede ver esto si va a la configuración "(Avanzado)" para el enlace de datos en el ComboBox. Entonces, debe incluir ese modo de actualización de fuente de datos también si eso es lo que está usando.

  • En mi caso, yo también tenía que provocar el evento OnSelectionChangeCommitted después de bucle a través de los enlaces y hacer la escritura/ReadValue llama. Como me estaba suscribiendo al evento SelectionChangeCommitted de ComboBox en el formulario, llamar a base.OnSelectionChangeCommitted antes de recorrer las vinculaciones y forzarlas a actualizar causó que la propiedad del objeto enlazado no se estableciera todavía.

tanto, aquí está mi versión de la respuesta de @Nicholas Piasecki (también convertido a VB.NET):

''' <summary> 
''' Raises the <see cref="E:System.Windows.Forms.ComboBox.SelectionChangeCommitted"/> event _after_ forcing any data bindings to be updated. 
''' </summary> 
''' <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param> 
Protected Overrides Sub OnSelectionChangeCommitted(e As EventArgs) 
    Dim bindings As List(Of Binding) = (_ 
     From x In Me.DataBindings.Cast(Of Binding)() 
     Where (x.PropertyName = "SelectedItem" OrElse x.PropertyName = "SelectedValue" OrElse x.PropertyName = "SelectedIndex") AndAlso 
       (x.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged OrElse x.DataSourceUpdateMode = DataSourceUpdateMode.OnValidation) 
    ).ToList() 

    For Each b As Binding In bindings 
     ' Force the binding to update from the new SelectedItem 
     b.WriteValue() 
     ' Force the Textbox to update from the binding 
     b.ReadValue() 
    Next 

    MyBase.OnSelectionChangeCommitted(e) 
End Sub 
Cuestiones relacionadas