Двусторонняя привязка данных в .NET не работает, даже с объектами INotifyPropertyChanged - PullRequest
2 голосов
/ 28 марта 2012

Я работаю над приложением Windows Forms, которое, конечно, включает привязку данных к объектам.

Из того, что я понял, двусторонняя привязка данных не работает "из коробки" с .NET.

Моя проблема в том, что я не могу найти непротиворечивую информацию о его реализации:

  • Согласно ".NET WinForms в двух словах", я должен создать событие для каждого свойства, которое называется Изменено, и запустить его в установщике связанного свойства.

  • Согласно онлайн-источникам (включая MSDN), мне просто нужно реализовать INotifyPropertyChanged в моем классе и запустить событие PropertyChanged во всех установщиках, передавая имя свойства.

Однако, предполагая, что у меня есть простой пользовательский интерфейс с привязкой данных текстового поля к свойству класса (реализующий любой из вышеуказанных методов), если я устанавливаю значение свойства базы данных в коде, пользовательский интерфейс не отражает изменения, используя любой из методов.

Так чего мне здесь не хватает?

Редактировать

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

Другое редактирование INotifyPropertyChanged правильно реализован инструментом генерации кода, я проверил эту точку, приведя экземпляр прокси-объекта к INotifyPropertyChanged, и зарегистрировал обработчик для события, которое вызывается при значение свойства изменено.

UpdateMode привязок данных установлен на NotifyPropertyChanged, а для CausesValidation установлено значение false для связанного элемента управления.

Итак, псевдокод выглядит так:

class MyForm {
    MyClass localObj;
    BindingSource mySource = new BindingSource();

    MyForm(MyClass obj) {
        localObj = obj.CreateProxy() // Ensures localObj is a proxy instance
        mySource.Datasource = localObj // After this, UI is updated accordingly

        localObj.someProperty = "I changed the value" // Here, the NotifyPropertyChanged event does get fired, but the UI does not update. Hence my question...
        Console.WriteLine(localObj.someProperty) // Output is 'I changed the value'
    }

}

Таким образом, единственный способ обновить пользовательский интерфейс - это вызвать ResetBindings для BindingSource ... что отрицательно сказывается на цели двухстороннего связывания IMHO.

Наконец-то !!

Проблема исходит от дизайнера. Во время разработки у меня нет доступа к реальному типу прокси бизнес-объекта, который является типом времени выполнения, сгенерированным прокси (который является функциональным, у меня нет сомнений в этой части). Таким образом, BindingSource инициализируется дизайнером с использованием известного типа данных бизнес-объекта, а не типа данных прокси (который является классом, унаследованным от класса BO)

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

Рабочая версия выглядит следующим образом (в псевдо-C #):

class MyForm {
    Object localObj;
    BindingSource mySource;

    MyForm(MyClass obj) {
        localObj = obj.CreateProxy() // Ensures localObj is a proxy instance

        ((ISupportInitialize) mySource).BeginInit()
        mySource.Datasource = localObj.GetType()
        ((ISupportInitialize) mySource).EndInit()

        mySource.Datasource = localObj // After this, UI is updated accordingly

        localObj.someProperty = "I changed the value" // UI updates ! :)
    }
}

Спасибо всем за советы и помощь, вы только что спасли мой день. Я бился головой об стену на этом!

Ответы [ 2 ]

2 голосов
/ 28 марта 2012

Я бы проверил, добавляет ли динамическая генерация кода INotifyChanged до формы, загруженной.Тогда я бы проверил, является ли свойство public объекта, который вы изменяете кодом, на самом деле тот же объект , что и тот, который в данный момент отображается на экране (это список илиодин объект?).Убедитесь, что привязка данных установлена ​​на OnPropertyChanged вместо OnValidation.Затем я бы проверил, работает ли он наоборот: отредактируйте значение текстового поля с привязкой к данным и посмотрите, получит ли объект новое значение после , выбрав другой элемент управления.Будьте осторожны при изменении свойства с помощью кода, подобного этому (псевдокод).

ctor(MyClass objParm){
  this.localobj = objParm;
  bindingsource1.datasource = objParm; // or this.localobj
}
SomeMethod(){
  this.localobj = new MyClass();
  this.localobj.MyProperty = 'new value';
}

Это не будет работать, кроме случаев, когда вы измените значение на

SomeMethod(){
  this.localobj = new MyClass();
  this.localobj.MyProperty = 'new value';
  bindingsource1.datasource = this.localobj
}

Обновление:
Вы говорите, что привязка данных не работает "из коробки", но я могу убедиться, что она работает.После просмотра предоставленного вами кода я подозреваю, что это связано с генерацией динамического прокси / CreateProxy () - методом.Поскольку ResetBindings работает, это, вероятно, связано с тем, что CreateProxy возвращает другой класс, чем MyClass.Привязка данных очень требовательна к типам.Например, если вы используете интерфейс, такой как List, для источника данных, привязка данных может выдавать ошибки, потому что они не одного типа.Вот почему вам нужно использовать новый BindingList () в качестве источника данных.Просто даю вам некоторую справочную информацию, которая может указать вам правильное направление.В вашем случае, метаданные, вероятно, изменяются из-за динамической генерации кода, смотрите документацию msdn по ResetBindings ().В качестве теста попробуйте это:

mySource.Datasource = new BindingList<MyClass>(new List<MyClass>(){this.localobj});
2 голосов
/ 28 марта 2012

Реализация INotifyPropertyChanged достаточно.Просто измените Data Source Update Mode TextBoxes с OnValidation на OnPropertyChanged в окне свойств -> (DataBindings) -> (Дополнительно) -> [...] диалоговое окно.


ОБНОВЛЕНИЕ

Вот как можно реализовать INotifyPropertyChanged.Сделайте это для всех ваших свойств

private string _city;
public string City
{
    get { return _city; }
    set
    {
        if (_city != value) {
            _city = value;
            OnPropertyChanged("City");
        }
    }
}

В вашем классе должен быть реализован интерфейс

public class Address : INotifyPropertyChanged
{
    // Your properties go here ...


    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...