Управление не сразу обновляет связанное свойство с помощью 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, ничего не изменится. У кого-нибудь есть идеи?
Ответы
Ответ 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