Элемент управления, не обновляющий связанное свойство немедленно с помощью 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. Пока что это работает. Конвертер Online Telerik (converter.telerik.com) также успешно преобразует приведенный выше код в VB на случай, если кому-то понадобится знать как. Спасибо за это Николас - Combo box - это кошмар. - person Tim F.; 12.05.2020

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


Было три основных изменения, которые мне пришлось внести, чтобы это исправление работало в моей ситуации.

  • Я пытался получить доступ к свойству объекта, привязанного к свойству SelectedValue ComboBox. Поэтому мне пришлось включить имя свойства SelectedValue в предложение Linq where.

  • Если вы настраиваете привязку данных через конструктор форм в 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