Подтвердить что ты не робот

Управление не сразу обновляет связанное свойство с помощью INotifyPropertyChanged

У меня есть элементы управления, которые не обновляют соответствующие свойства соответствующих объектов до тех пор, пока фокус не будет потерян. Есть похожие вопросы с принятыми ответами, ссылающимися на DataSourceUpdateMode.OnPropertyChange, объявляемыми, что я и делаю, но поведение сохраняется. Вот пример реализации. Я постараюсь быть тщательным, но кратким. Доступ к классу MyConfig осуществляется через свойство в классе Singleton я call 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, ничего не изменится. У кого-нибудь есть идеи?

4b9b3361

Ответ 1

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

Ответ 2

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


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

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

  • Если вы настраиваете привязку данных через конструктор форм в Visual Studio, и просто установите, к чему привязан параметр SelectedValue или SelectedItem, значение по умолчанию Режим обновления данных - "OnValidation". Вы можете видеть это, если вы идете в "(Дополнительно)" для привязки данных в ComboBox. Таким образом, вы должны включить этот режим обновления данных и, если это то, что вы используете.

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

Итак, вот моя версия @Николас Пиасекки отвечает (также преобразован в 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