Контролирайте незабавното актуализиране на обвързаното свойство с INotifyPropertyChanged

Имам контроли, които не актуализират съответните свойства на своя обвързан обект, докато фокусът не бъде изгубен. Има подобни въпроси с приети отговори, препращащи към DataSourceUpdateMode.OnPropertyChange, които са декларирани, което правя, но поведението продължава. Ето примерна реализация. Ще се опитам да бъда изчерпателен, но същевременно кратък. Класът MyConfig е достъпен чрез свойство в клас Singleton, което наричам Configuration.

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

Не съм сигурен, предвид следното кратко внедряване, какво бих могъл да пренебрегна, за да осигуря незабавни промени в свойствата. Мога да променя в този случай от Foo на Bar в ComboBox, но освен ако не премахна фокуса от ComboBox, нищо не се променя. Някой има ли идеи?


person Josh Clayton    schedule 05.12.2011    source източник


Отговори (2)


WinForms ComboBox е нестабилен по отношение на OnPropertyChanged. Ето малко код от стар проект, който използвах, за да накарам OnPropertyChanged да работи по начина, по който очаквам за свойството SelectedItem. Това работи за моя конкретен случай, но обикновено понякога се боря да накарам този сценарий да работи. Късмет!

/// <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();
        }
    }
}
person Nicholas Piasecki    schedule 05.12.2011
comment
Работи като чар. Благодаря милиони! И ти ме научи малко за персонализирането на съществуващите контроли :) - person Josh Clayton; 06.12.2011
comment
Това е единственото решение, което открих, че наистина работи. Благодаря ти. - person Wayne Bloss; 06.03.2013
comment
Използвах това като поправка за случаите, когато падащото поле с падащ списък беше избрано с мишката и обвързаните данни не бяха актуализирани/променени, но го направиха при втори опит. В моя случай го промених на свойството SelectedValue.. Засега изглежда, че работи. Онлайн конверторът на Telerik (converter.telerik.com) също преобразува успешно горния код във VB, в случай че някой има нужда да знам как. Благодаря за това Николас - Комбинираните кутии са кошмар за работа. - person Tim F.; 12.05.2020

@Nicholas Piasecki заслужава признание за това, че ме доведе до моето решение, така че, освен ако не можете да стигнете до решение въз основа на отговора му, моля, гласувайте за неговия отговор.


Имаше три основни промени, които трябваше да направя, за да работи тази корекция в моята ситуация.

  • Опитвах се да осъществя достъп до свойството на обекта, свързан със свойството SelectedValue на ComboBox. Следователно трябваше да включа името на свойството "SelectedValue" в клаузата Where на Linq.

  • Ако настройвате обвързването на данни чрез дизайнера на формуляри във Visual Studio и просто задавате към какво е свързан SelectedValue или SelectedItem, режимът на актуализиране на източника на данни по подразбиране е „OnValidation“. Можете да видите това, ако отидете на настройките "(Разширени)" за свързване на данни в ComboBox. Така че трябва да включите и този режим на актуализиране на източника на данни, ако това е, което използвате.

  • В моя случай също трябваше да повдигна събитието OnSelectionChangeCommitted след преминаване през обвързванията и извършване на извикванията Write/ReadValue. Тъй като се абонирах за събитието SelectionChangeCommitted на ComboBox във формуляра, извикването на base.OnSelectionChangeCommitted преди преминаване през обвързванията и принуждаването им да се актуализират доведе до това, че свойството на обвързания обект все още не е зададено.

И така, ето моята версия на отговора на @Nicholas Piasecki (също преобразуван във 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
person jgreg311    schedule 21.05.2013
comment
+1 Както по-горе, също поставих MyBase.OnSelectionChangeCommitted в края и добавих SelectcedValue. - person Tim F.; 12.05.2020