Как иметь связанные свойства UserControl, которые работают с OnPropertyChanged

У меня есть простой пользовательский контроль (WinForms) с некоторыми общедоступными свойствами. Когда я использую этот элемент управления, я хочу привязать данные к этим свойствам с помощью параметра DataSourceUpdateMode, установленного в OnPropertyChanged. Источник данных - это класс, который реализует INotifyPropertyChanged.

Я знаю о необходимости создания привязок к свойствам, и я это делаю.

Я предположил, что мой пользовательский контроль должен будет реализовать интерфейс, или свойства должны быть украшены каким-то атрибутом или что-то в этом роде. Но мои исследования стали пустым.

Как это должно быть достигнуто? На данный момент я делаю это, вызывая OnValidating() в моем usercontrol всякий раз, когда свойство изменяется, но это кажется неправильным.

Я могу получить подтверждение, если я установлю CausesValidation на true в usercontrol, но это не очень полезно для меня. Мне нужно проверить каждое дочернее свойство по мере его изменения.

Обратите внимание, что это ситуация WinForms.

РЕДАКТИРОВАТЬ: Очевидно, у меня нет таланта для объяснения, поэтому, надеюсь, это прояснит, что я делаю. Это сокращенный пример:

// I have a user control
public class MyControl : UserControl
{
    // I'm binding to this property
    public string ControlProperty { get; set; }

    public void DoSomething()
    {
        // when the property value changes, the change should immediately be applied 
        // to the bound datasource
        ControlProperty = "new value";

        // This is how I make it work, but it seems wrong
        OnValidating();         
    }
}

// the class being bound to the usercontrol
public class MyDataSource : INotifyPropertyChanged
{
    private string sourceProperty;
    public string SourceProperty
    {
        get { return sourceProperty; }
        set
        {
            if (value != sourceProperty)
            {
                sourceProperty = value;
                NotifyPropertyChanged("SourceProperty");
            }
        }
    }

    // boilerplate stuff
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}

public class MyForm : Form
{
    private MyControl myControl;
    public MyForm()
    {
        // create the datasource 
        var dataSource = new MyDataSource() { SourceProperty = "test" };

        // bind a property of the datasource to a property of the usercontrol
        myControl.DataBindings.Add("ControlProperty", dataSource, "SourceProperty",
            false, DataSourceUpdateMode.OnPropertyChanged); // note the update mode
    }
}

(Я пробовал это с использованием BindingSource, но результат был тот же.)

Теперь я хочу, чтобы при изменении значения MyControl.ControlProperty это изменение немедленно распространилось на источник данных (экземпляр MyDataSource). Для этого я вызываю OnValidating() в usercontrol после изменения свойства. Если я этого не делаю, мне нужно подождать, пока проверка не будет вызвана изменением фокуса, что эквивалентно режиму обновления OnValidation, а не желаемому режиму проверки OnPropertyUpdate. Мне просто не хочется называть OnValidating() после изменения значения свойства - это правильная вещь, даже если она (вид) работает.

Действительно ли я предполагаю, что вызов OnValidating() - это не правильный способ сделать это? Если да, то как я могу уведомить источник данных об изменении ControlProperty?

Ответ 1

Думаю, я понял это. Я не понял, как уведомления об изменениях были отправлены из управления в связанный источник данных.

Да, вызов OnValidating() - неправильный путь.

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

Один из способов - реализовать элемент управления INotifyPropertyChanged. Я никогда не делал этого с контрольной стороны раньше, и я думал, что только сторона источника данных должна была его реализовать.

Когда я выполнил INotifyPropertyChanged в моем пользовательском элементе управления и поднял событие PropertyChanged в соответствующее время, он сработал.

Второй способ заключается в том, чтобы элемент управления мог поднять конкретное событие изменения для каждого свойства. Событие должно соответствовать соглашению об именах: <propertyname>Changed

например. для моего примера это будет

public event EventHandler ControlPropertyChanged

Если мое свойство было вызвано Foo, оно было бы FooChanged.

Я не заметил заметную часть документации MSDN где говорится:

Для уведомления об изменении в привязка между связанным клиентом и источника данных, ваш связанный тип должен либо:

Внедрить INotifyPropertyChanged интерфейс (предпочтительный).

Предоставить событие изменения для каждого свойство связанного типа.

Этот второй способ заключается в том, как работают все существующие средства управления WinForms, так вот как я это делаю сейчас. Я использую INotifyPropertyChanged на моем источнике данных, но я поднимаю события Changed на свой контроль. Это кажется обычным способом.

Ответ 2

Реализация интерфейса INotifyPropertyChanged очень проста. Вот пример, который показывает объект с одним открытым полем...

public class Demo : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(info));
    }

    private string _demoField;

    public string DemoField
    {
        get {return demoField; }

        set
        {
            if (value != demoField)
            {
                demoField = value;
                NotifyPropertyChanged("DemoField");
            }
        }
    }
}

Затем вы создадите экземпляр Binding для привязки свойства управления к свойству (DemoField) в исходном экземпляре (экземпляр Demo).