3. Общее представление о привязке данных в Windows Forms

Манипулирование данными посредством источника привязки

после того как вы установите

Источник привязки сам по себе предоставляет вам косвенный доступ к данным, хранящимся в нижележащем источнике данных на стороне клиента. После того, как вы установите свойство DataSource Источника привязки, вам более не потребуется хранить ссылку на сам источник данных, поскольку она будет сохраняться источником привязки. И вы всегда можете получить ссылку на источник данных путем приведения значения свойства DataSource К ожидаемому типу. Единственный недостаток подобного подхода заключается в том, что в той точке кода, где вы используете ссылку из DataSource, Вы должны точно знать тип коллекции данных, к которому прикрепляется источник привязки. Ключ ко всей концепции — в понимании того, что в качестве источника данных для родительского источника привязки необходимо установить родительскую коллекцию объектов, а в качестве источника данных для дочернего источника привязки — ссылку на родительский источник привязки. Тогда свойство DataMember Дочернего источника привязки должно быть именем свойства в родительском объекте, которое является ссылкой на дочернюю коллекцию. Здесь это свойство Children, Принадлежащее к типу Binding — List<ChildObject>. При этом BindingList Является обобщенным типом коллекций, предусмотренным в. NET 2.0 специально для привязки данных в Windows Forms, a ChildObject — это тип-параметр коллекции, специфицирующий тип объектов, которые будет содержать коллекция. Используя эту методику каскадирования источников привязки, вы можете поддерживать иерархии родительских и дочерних данных произвольной глубины. Вы, кроме того, всегда можете получить текущий элемент данных в коллекции через свойство Current Источника привязки. Свойство Current Возвращает ссылку на объект, и, если вы знаете типы всех элементов коллекции, вы опять же можете выполнить соответствующее приведение типа и работать с элементами данных. Помните, что тип объекта, возвращаемого свойством Current, Всегда будет DataRowView Во всех случаях, когда вы привязываетесь к DataTable Или DataView, И это будет экземпляр какого-то объектного типа, хранящегося в коллекции, если вы работаете со специальными объектами.

Закрепление элементов управления

возможно вы хотите чтобы элемент

Для многих видов элементов управления и планировок формы вам хотелось бы, чтобы элементы управления сохраняли одно и то же положение относительно одной или нескольких сторон формы. Возможно, вы хотите, чтобы элемент управления сохранял свой размер, но оставался в том же положении относительно двух смежных сторон формы, например, левого верхнего или правого нижнего угла формы. Либо вы хотите, чтобы внешние кромки элемента управления сохраняли свое положение относительно противоположных сторон формы, т. е. чтобы он растягивался или сжимался при расширении или сокращении формы. Взгляните, например, на рис. В.14 — это одна и та же форма, но она показана при различных размерах. Для подобных форм вы, вероятно, захотите, чтобы при изменении размера сетка данных заполняла все наличное пространство формы, не используемое кнопками. Вы также, вероятно, хотели бы, чтобы кнопка в левом нижнем углу оставалась в фиксированном положении относительно этого угла, и то же самое касается кнопки справа внизу. Наконец, вы, вероятно, хотели бы, чтобы средняя кнопка оставалась по центру формы и в фиксированном положении относительно ее нижней кромки, можно видеть, Посмотрев на размеры сетки и положение кнопок на рис. В. 14, вы увидите, что можно достичь именно такого поведения, не написав вручную ни единой строки кода, а просто установив соответствующим образом свойство Anchor Каждого из элементов управления в окне Properties. Свойство Anchor Принимает перечисление типа AnchorStyles, Которое имеет флаговые значения Top, Bottom, Left И Right. Поскольку это флаговое перечисление, вы можете объединять эти значения посредством булевой операции «или» . Конструктор Visual Studio предоставляет вам удобный графический редактор для этого свойства, показанный на рис. В. 15, который позволяет визуально выбрать стороны, у которых вы хотите закрепить элемент управления. Получающийся при этом код эквивалентен следующему коду для закрепления сетки у всех четырех сторон, чтобы она изменяла размер так, что каждая из ее четырех сторон сохраняла бы свое положение относительно соответствующей стороны формы:

Набор данных с установленным родительско-дочерним отношением

вы можете либо просто прочитать

Как только у вас будет набор данных с установленным родительско-дочерним отношением, можно строить форму. Вы можете либо просто прочитать нижеприведенные инструкции, либо, если захотите проделать все это сами, создайте сначала проект Windows Application и поместите на форму два элемента управления DataGridView, два BindingSource и один BindingNavigator. Одна сетка предназначена для родительской коллекции данных, вторая отображает дочерние строки. В приложении MasterDe — tailsSample, код которого можно загрузить, эти данные поступают соответственно из таблиц Customers и Orders. Первый источник привязки служит просто для заполнения родительской сетки из таблицы Customers, аналогично тому, что вы видели в предыдущих примерах. Трюк, заставляющий работать сценарий «ведущий-детализация», состоит в том, что в свойстве DataSource дочернего источника привязки вы устанавливаете родительский источник привязки — в MasterDetailsSample это m CustomersBindingSource. Затем вы специфицируете имя отношения данных, которое соотносит родительские строки с дочерними, в качестве значения свойства DataMember родительского источника привязки; в следующим Вот и все. Каскадирование дочернего источника привязки с родительским автоматически синхронизирует текущую строку в родительской коллекции с набором соответствующих дочерних строк в дочерней коллекции. Тем не менее, если вам требуется сохранить эти изменения из привязанного источника на уровне хранения данных, то придется написать соответствующий код доступа к данным. Обычно такой код выполняется в ответ на щелчок пользователя на кнопке панели инструментов, командной кнопке, на выбор команды в меню, и для этого достаточно вызвать метод Update соответствующего адаптера таблицы. В следующем листинге приведен обновляющий код, генерируемый конструктором для кнопки Save элемента управления Вызов Validate обеспечивает выполнение верификации для последнего элемента управления, который обладал фокусом ввода. Вызов EndEdit фиксирует все внесенные изменения в текущем источнике данных, если этот объект поддерживает транзакционные обновления через интерфейс

Объект SqlCommand

объекту команды можно передать либо

Как только у вас есть соединение, с которым можно работать, вы создаете объект SqlCommand, Передавая конструктору текст запроса SQL для исполнения и соединение, которое следует использовать. Объекту команды можно передать либо операторы SQL, либо имя сохраняемой процедуры. В реальных уровнях данных я рекомендую инкапсулировать доступ к данным на уровне базы данных в сохраняемых процедурах, и в своем уровне данных лишь потреблять эти процедуры. Это развяжет код вашего приложения от специфической схемы ваших таблиц, благодаря чему небольшие изменения в нижележащей схеме не будут влиять на код приложения. Сохраняемые процедуры позволят вам также использовать встроенные механизмы безопасности SQL Server для предотвращения прямого доступа к таблицам, и сохраняемые процедуры обеспечивают иногда лучшую производительность, чем динамические операторы SQL, исполняемые из кода в листинге Г. З. Во многих примерах кода в этой книге я нарушал эту рекомендацию, чтобы можно было использовать базу данных Northwind с минимальными модификациями, и чтобы вы ясно могли видеть, что именно извлекается запросами При выполнении текстовых запросов SQL Вы должны точно специфицировать столбцы, которые должны возвращаться вашим запросом. Спецификация * для столбцов может возвратить гораздо больше информации, чем вам нужно, влияя на производительность приложения. При этом объекту команды требуется проделать больше работы для определения точной схемы результирующего набора, который будет возвращен запросом Создает объект SqlDataAdapter, Являющийся мостом между нейтральным в смысле источника набором данных и специфическим источником данных, с которым он будет работать. В данном слу-чае я конструирую его, передавая объект команды.

События набора данных и адаптера данных

однако иногда вам нужно быть

До сих пор я обсуждал только программирование с методами и свойствами наборов данных и адаптеров данных, а также обработку исключений, когда что-то идет не так. Однако иногда вам нужно быть поближе к тому, что происходит в процессе обработки. Имеется ряд событий из классов DataSet, DataTable И SqlDataAdapter, К Которым вы можете подключиться, чтобы ваш код мог получать уведомления при изменениях в данных. Класс DataSet Имеет два события: MergeFailed, Которое запускается, если вы выполняете операцию Merge и имеет место конфликт первичного ключа между целевой и исходной таблицами. Initialized, Которое запускается, когда DataSet Завершит инициализацию содержащихся в нем объектов и состояния. Наиболее полезные события имеет DataTable; Они описаны в таблице Г.4.

События ColumnChanging/ColumnChanged Передают вашему обработчику события параметр типа DataColumnChangeEventArgs. Этот параметр содержит несколько значений, полезных для контроля над обновлением столбца. Свойство для чтения/записи с именем ProposedValue Содержит значение, которое будет иметь изменяемый столбец после того, как обработчик события завершится. Вы можете инспектировать это значение в своем обработчике ColumnChanging, И в комбинации со свойствами row и Column Параметра аргументов события верифицировать его и принять решение об изменении этого значения. Если предполагаемое значение не отвечает вашим ограничениям верификации, у вас есть две возможности. Во-первых, вы можете изменить его на некоторое приемлемое значение, присвоив другое значение соответствующего типа свойству ProposedValue Параметра аргументов события. Если вы делаете это, любое значение, которое вы присвоите свойству, станет тем, что будет действительно установлено для столбца, когда изменение будет сделано. Поэтому вы могли бы также изменить предполагаемое значение на первоначальное, которое можно извлечь через свойства Row И Column, Как показано здесь, чтобы фактически предотвратить изменение: