Привязка данных (Data Binding) Механизм привязки данных – программная модель для связывания данных элемента управления и данных приложения. Приемник данных (data consumer) – элемент управления. Источник данных (data provider) – данные приложения. Связь устанавливается между некоторым свойством элемента управления и источником данных или одним из его свойств.
Два типа привязки данных Simple Data Binding Используется в элементах управления, отображающих отдельные значения, например, TextBox или NumericUpDown. Свойство элемента управления связывается с отдельным элементом данных или текущим элементом в списке, например, значением в столбце таблицы DataTable. Свойство Enabled любого элемента управления можно связать со свойством булевского типа. Complex Data Binding (List Based Binding) Используется в элементах управления для работы со списками или таблицами, например, ListBox или DataGridView. Свойство элемента управления связывается со списочными или табличными данными, например, с таблицей DataTable или с ее столбцом. Источником данных может быть любой объект, реализующий интерфейс IList.
Класс Binding С помощью объекта типа Binding можно описать связь между свойством элемента управления и свойством отдельного объекта приложения; между свойством элемента управления и свойством текущего элемента в списке объектов; Свойство DataBindings класса Control поддерживает Simple Data Binding: System.Object System.Windows.Forms.Binding Коллекция ControlBindingsCollection состоит из элементов типа Binding. public ControlBindingsCollection DataBindings {get;}
Конструкторы класса Binding В классе Binding определены 6 конструкторов, в том числе конструкторы: public Binding( string propertyName, - свойство элемента управления object dataSource,- источник данных string dataMember ); - конкретизация свойства в источнике данных public Binding( string propertyName, object dataSource, string dataMember, bool formattingEnabled,- выполняется форматирование (умолчание true) DataSourceUpdateMode dataSourceUpdateMode); - режим обновления данных в источнике;
Перечисление DataSourceUpdateMode Значение OnValidation Источник данных обновляется при проверке корректности значения свойства элемента управления. OnPropertyChanged Источник данных обновляется при изменении значения свойства элемента управления. Never Источник данных не обновляется. Данные, введенные в элемент управления не разбираются (parsed), не проверяются (validated ) и не переформатируются( re-formatted).
Методы класса ControlBindingsCollection Класс ControlBindingsCollection и его базовые классы не реализуют интерфейс IList, но в нем определены методы с параметрами типа Binding для работы с коллекцией. Некоторые методы класса ControlBindingsCollection: public Binding this[ int index ] {get; set;} public int Add( Binding binding ); public Binding Add( string propertyName, object dataSource, string dataMember ); public int IndexOf( Binding binding ); public void Insert( int index, Binding binding ); Связь между элементом управления и данными приложения устанавливается, когда объект Binding, который описывает связь, добавляется к коллекции связей элемента управления.
Пример Binding bd = new Binding("Text", md.DataArray, "DTime"); textBox2_Form.DataBindings.Add(bd); К коллекции связей элемента управления TextBox добавляется связь между свойством Enabled элемента управления и свойством Able объекта md. Объект Binding bd описывает связь между свойством Text элемента управления и свойством DTime текущего элемента коллекции md.DataArray. Связь устанавливается, когда к коллекции DataBindings элемента управления TextBox textBox2_Form добавляется объект bd, который описывает связь. textBox2_Form.DataBindings.Add(new Binding("Enabled", md, "Able"));
Complex Data Binding Свойство DataSource элементов управления ListBox, ComboBox, DataGrid, DataGridView, DataProvider дает возможность связать элемент управления с любым объектом, реализующим интерфейс IList: public object DataSource {get; set;} После установления связи в источнике данных будет поддерживаться текущая позиция. Когда устанавливается связь данных приложения с элементами управления формы для каждого источника данных создается объект CurrencyManager – по одному для каждого источника данных. В объекте CurrencyManager хранится информация о текущей позиции в источнике данных и тем самым поддерживается синхронизация элементов управления, отображающих информацию из одного источника.
Классы CurrencyManager и BindingManagerBase В классе BindingManagerBase определены свойства: BindingsCollection Bindings {get;} Сcылка на коллекцию объектов Binding для источника данных. abstract int Count {get;} Число элементов в источнике данных. abstract object Current {get;} Ссылка на текущий элемент в источнике данных. abstract int Position {get; set;} Позиция текущего элемента в источнике данных. System.Object System.Windows.Forms.BindingManagerBase System.Windows.Forms.CurrencyManager System.Windows.Forms.PropertyManager Класс CurrencyManager является производным от абстрактного базового класса BindingManagerBase, который управляет всеми связями (объектами Binding) для одного источника данных
Класс BindingContext Все объекты CurrencyManager являются элементами коллекции BindingContext ( тип элементов коллекции BindingManagerBase). public class BindingContext : ICollection, IEnumerable {...} С каждой формой по умолчанию связан один объект BindingContext, который доступен через одноименное свойство класса Control: public virtual BindingContext BindingContext {get; set;} Для элементов управления - контейнеров (Panel, GroupBox, TabControl) можно создать свой объект BindingContext. Его объекты CurrencyManager будут поддерживать отдельную текущую позицию для контейнера. В классе BindingContext определен индексатор, который дает доступ к элементам коллекции – объектам CurrencyManager или PropertyManager и свойство, которое дает возможность проверить, поддерживается ли связь с конкретным источником данных public BindingManagerBase this[ object dataSource ] {get;} public bool Contains( object dataSource );
Классы CurrencyManager и BindingContext Схема из раздела MSDN Consumers of Data on Windows Forms (Visual Basic and Visual C# consepts)
Обмен данными Если значение свойства DataSourceUpdateMode объекта Binding не равно DataSourceUpdateMode.Never, обмен данными в направлении элемент управления -> источник происходит автоматически. Обмен данными в направлении источник -> элемент управления выполняется автоматически только для источников, реализующих интерфейс IBindingList ( DataView). Обновление элемента управления после изменения источника данных можно выполнить, вызвав из класса CurrencyManager метод public: void Refresh(); Элемент управления, который связан с отдельным объектом, управляется PropertyManager. Обновление элемента управления выполняет метод класса PropertyManager public override void ResumeBinding();
События Binding.Format и Binding.Parse В обработчиках событий Format и Parse можно реализовать нестандартное форматирование. Событие Parse происходит, когда данные из элемента управления передаются в источник. Данные передаются, когда элемент управления теряет фокус ввода. Событие Format происходит, когда данные из источника передаются в элемент управления. Например, когда изменяется значение свойства Position в CurrencyManager, или после события Parse.
Обработчики событий Binding.Format и Binding.Parse В классе ConvertEventArgs определены свойства public Type DesiredType {get;} public object Value {get; set;} В обработчике Format Value - значение из источника данных; DesiredType – тип свойства элемента управления. В обработчике Parse Value - значение из элемента управления; DesiredType – тип объекта в источнике данных. public delegate void ConvertEventHandler(object sender, ConvertEventArgs e ); События Binding.Format и Binding.Parse имеют тип
Класс Panel Класс Panel описывает элемент управления, который является контейнером для других элементов управления. Если свойству Enabled класса Panel присваивается значение false, то блокируются все элементы контейнера. Наследует от класса System.Windows.Forms.ScrollableControl свойство AutoScroll. Если свойство public virtual bool AutoScroll {get; set;} System.Windows.Forms.Control System.Windows.Forms.ScrollableControl System.Windows.Forms.Panel имеет значение true, то элемент-контейнер можно прокручивать вместе с расположенными на его поверхности элементами управления. По умолчанию не имеет рамки.
Компонент BindingSource В.NET Framework 2.0 появился класс BindingsSource. BindingsSource упрощает привязку данных и может выполнять как роль проводника, так и роль источника данных. Использование объекта BindingSource является универсальным способом связывания данных и элементов управления. public class BindingSource : Component, IBindingListView, IBindingList, IList, ICollection, IEnumerable, ITypedList, ICancelAddNew, ISupportInitializeNotification, ISupportInitialize, ICurrencyManagerProvider Данные BindingSource Элемент управления
Компонент BindingSource выполняет роль строго типизированного источника данных; инкапсулирует функциональность CurrencyManager; поддерживает направления обмена данными между источником и элементом управления; упрощает создание списка c интерфейсом IBindingList, предоставляя список уведомлений об изменении источников данных; взаимодействует с другими элементами управления Windows Forms, в частности с BindingNavigator и DataGridView; реализует добавление данных в источник в стиле транзакций (transactional creation); поддерживает источники данных с интерфейсом IEnumerable; на стадии проектирования позволяет установить связь элемента управления c типом (а не с объектом) и отложить связь с объектом до выполнения приложения.
Интерфейсы IBindingList и IBindingListView Класс, реализующий IBindingList, поддерживает сортировку; обеспечивает нотификацию об изменениях как в элементах списка (например, об изменении одного из полей некоторого элемента списка), так и самого списка ( например, изменилось число элементов списка). interface IBindingListView : IBindingList, IList, ICollection, IEnumerable Класс, реализующий IBindingListView, реализует всю функциональность интерфейса IBindingList поддерживает фильтрацию и более сложную сортировку - multicolumn sorting with property descriptor-direction pairs. interface IBindingList : IList, ICollection, IEnumerable
Интерфейс INotifyPropertyChanged Для того, чтобы элементы управления обновляли данные при изменении данных в источнике, в нем следует реализовать интерфейс INotifyPropertyChanged. public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; } public delegate void PropertyChangedEventHandler( Object sender, PropertyChangedEventArgs e ); Делегат PropertyChangedEventHandler public PropertyChangedEventArgs( string propertyName ); public virtual string PropertyName { get; } Методы класса PropertyChangedEventArgs предоставляют информацию о том, значения какого свойства источника изменилось. Значение null или Empty можно использовать, когда изменились значения всех свойств источника.
Интерфейс ITypedList Коллекция, реализующая ITypedList, дает возможность изменить порядок и множество свойств, доступных присоединенному элементу управления. По умолчанию все открытые (public) свойства объекта-источника данных доступны для связывания. public interface ITypedList { PropertyDescriptorCollection GetItemProperties( PropertyDescriptor[] listAccessors ); string GetListName( PropertyDescriptor[] listAccessors ); }
Интерфейс ICancelAddNew Класс, реализующий ICancelAddNew, дает возможность отказаться от изменений источника данных при вызове метода AddNew. Для источника данных, который реализует интерфейс IBindingList, рекомендуется реализовать интерфейс ICancelAddNew. public interface ICancelAddNew { void CancelNew ( int itemIndex ); void EndNew ( int itemIndex ) ; }
Интерфейс ISupportInitialize Метод BeginInit() вызывается, когда начинается процесс инициализации. Метод EndInit() сообщает, что процесс инициализации завершен. public interface ISupportInitialize { void BeginInit(); void EndInit(); }
Интерфейс ISupportInitializeNotification Интерфейс следует реализовать, если инициализация объекта связана с инициализацией других компонент. Свойство IsInitialized имеет значение true, если инициализация завершена. Свойству автоматически присваивается значение false при вызове метода BeginInit() и значение true при вызове метода EndInit(). Событие Initialized обычно происходит при вызове метода EndInit(), но может произойти позже, если источник связан с инициализацией других элементов управления, которые реализуют интерфейс ISupportInitializeNotification и инициализация которых еще не завершна. public interface ISupportInitializeNotification : ISupportInitialize { bool IsInitialized { get; } event EventHandler Initialized; }
Интерфейс ICurrencyManagerProvider Интерфейс дает возможность поддерживать свой собственный объект CurrencyManager для того, чтобы управлять связями объекта с элементами управления. Параметр dataMember в методе GetRelatedCurrencyManager – имя списка или столбца в источнике данных, для которого нужно получить ссылку на соответствующий объект. Если параметр имеет значение (""), GetRelatedCurrencyManager возвращает ссылку на основной объект CurrencyManager, который доступен через свойство CurrencyManager. public interface ICurrencyManagerProvider { CurrencyManager CurrencyManager { get; } CurrencyManager GetRelatedCurrencyManager(string dataMember); }
Создание компонента BindingSource В классе BindingsSource определены три конструктора. Конструктор без параметров присваивает следующие значения свойствам объекта BindingsSource: Свойства класса BindingSource public Object DataSource { get; set; }null public string DataMember { get; set; }Empty public string Sort { get; set; }null public virtual string Filter { get; set; }null public bool RaiseListChangedEvents { get; set; }true Данные для инициализации можно передать потом через свойства DataSource и DataMember. Конструктор public BindingSource( Object dataSource, string dataMember ); инициализирует объект BindingsSource заданными значениями.
Как связать источник данных и элемент управления с использованием BindingSource? Инициализированный объект BindingSource можно использовать как источник данных для элемента управления - его надо добавить в коллекцию связей элемента управления. После того, как установлена связь с элементом управления, все взаимодействие с источником данных выполняется через вызовы компоненты BindingSource.
Взаимодействие с источником : текущий элемент Object Current { get; } Ссылка на текущий элемент в списке virtual Object this [ int index ] { get; set; } Индексатор, который дает доступ к элементу в заданной позиции IList List { get; } Доступ ко всему присоединенному списку virtual CurrencyManager CurrencyManager { get; } Ссылка на объект CurrencyManager, связанный с BindingSource. int Position { get; set; } Индекс текущего элемента в присоединенном списке (от 0). При выходе за границы присваивается значение 0 или Count-1. Класс BindingsSource инкапсулирует функциональность CurrencyManager. Определенные в классе BindingsSource свойства упрощают доступ к текущему элементу источника данных.
Взаимодействие с источником данных: обновление void ResetBindings ( bool metadataChanged ); Элементы управления, связанные с BindingSource обновляют все свои значения. Параметр метода равен true, если изменилась структура данных, и false, если изменились только значения. void ResetCurrentItem () ; В элементах управления обновляется текущий элемент. void ResetItem ( int itemIndex ) ; В элементах управления обновляется элемент списка с заданным индексом. Методы передают информацию элементам управления через событие ListChanged. Через свойство ListChangedEventArgs.ListChangedType обработчику события передается дополнительная информация.
Взаимодействие с источником данных: навигация public void MoveFirst(); Свойству Position присваивается значение 0 void MoveLast(); Свойству Position присваивается значение Count-1 void MoveNext(); Значение свойства Position увеличивается на 1 void MovePrevious(); Значение свойства Position уменьшается на 1
Взаимодействие с источником данных: редактирование Класс BindingSource реализует интерфейс IList. Кроме того в классе определен метод public virtual Object AddNew () ; При вызове AddNew() новый элемент добавляется в список в обработчике события AddingNew через свойство NewObject параметра обработчика System.ComponentModel.AddingNewEventArgs. Если источник не реализует интерфейс IEditableObject, новый элемент добавляется в список и автоматически вызывается метод EndEdit (). Если реализован интерфейс IEditableObject, новый элемент не добавляется в список до тех пор, пока не будет явно вызван метод System.ComponentModel.ICancelAddNew.EndNew(). До вызова этого метода можно отказаться от операции добавления нового элемента в список, если вызвать метод CancelEdit(). Метод порождает событие ListChanged.
Свойство RaiseListChangedEvents. Cобытие ListChanged bool RaiseListChangedEvents { get; set; } Событие ListChanged происходит, когда изменяется присоединенный список или его элементы, включая операции добавления, удаления, вставки или изменения элементов списка. Свойство RaiseListChangedEvents управляет событием ListChanged. Если значение равно false, событие не порождается. Умолчание – true. Свойства класса ListChangedEventArgs: public event ListChangedEventHandler ListChanged ; public ListChangedType ListChangedType { get; } Значение перечисления ListChangedType с дополнительной информацией public int NewIndex { get; } Новый индекс элемента public int OldIndex { get; } Старый индекс элемента public PropertyDescriptor PropertyDescriptor { get; } PropertyDescriptor элемента
Взаимодействие с источником данных: события AddingNew Происходит перед добавлением элемента в список BindingComplete Происходит после присоединения к BindingSource CurrentChanged Происходит после изменения текущего элемента CurrentItemChanged Происходит при изменении значения свойства Current DataError Исключение обрабатывается BindingSource. DataMemberChanged Происходит при изменении значения свойства DataMember DataSourceChanged Происходит при изменении значения свойства DataSource Disposed Происходит при вызове метода Disposed для объекта BindingSource ListChanged Происходит при изменении списка или одного из его элементов PositionChanged Происходит при изменении значения свойства Position