Для организации просмотра информации, сохраняемой объектом-представителем класса DataTable через объект – представитель класса DataView, этот объект необходимо связать с таблицей.
Таким образом, в приложении создается (независимый!) вьюер, который связывается с таблицей.
Итак, имеем
DataTable tbl = new DataTable("XXX"); // Объявлен и определен объект "таблица". DataView vie; // Ссылка на вьюер. vie = new DataView(); // Создали... vie.Table = tbl; // Привязали таблицу к вьюеру. // Можно и так... vie = new DataView(tbl); // Создали и сразу привязали...Управление вьюером также осуществляется посредством различных достаточно простых манипуляций, включая изменение свойств объекта. Например, сортировка включается следующим образом:
// Предполагается наличие кнопочных переключателей, // в зависимости от состояния которых в свойстве Sort // вьюера выставляется в качестве значения имя того или // иного столбца. Результат сортировки становится виден // непосредственно после передачи информации объекту, // отвечающему за демонстрацию таблицы (rpDataGreed). if (rBN.Checked) rd.view.Sort = "nRoll"; if (rBX.Checked) rd.view.Sort = "X"; if (rBY.Checked) rd.view.Sort = "Y"; this.rpDataGrid.DataSource = rd.view;Следующий пример кода демонстрирует возможности поиска, реализуемые объектом – представителем класса DataView. Сортировка обеспечивается вариантами методов Find и FindRows, которые способны в различной реализации воспринимать отдельные значения или массивы значений.
Поиск информации проводится по столбцам, предварительно перечисленным в свойстве Sort. При неудовлетворительном результате поиска метод Find возвращает отрицательное значение, методFindRows – нулевое.
В случае успеха метода Sort возвращается индекс первой найденной записи. По этому индексу можно получить непосредственный доступ к записи.
|
// Массив для получения результатов поиска DataRowView[] rows;::::: // Поиск в строке по полю "nRoll" (целочисленный столбец) rd.view.Sort = "nRoll"; try { // Проверка на соответствие типа. int.Parse(this.findTtextBox.Text); // Сам поиск. Возвращается массив rows. rows = rd.view.FindRows(this.findTtextBox.Text); } catch (Exception e1) { this.findTtextBox.Text = "Integer value expected..."; } }::::: // Проверка результатов. if (rows.Length == 0) { this.findTtextBox.Text = "No rows found: " + this.findTtextBox.Text; } else { foreach (DataRowView row in rows) { this.findTtextBox.Text = row["nRoll"].ToString() + "," + row["X"].ToString() + "," + row["Y"].ToString(); } } |
Учебный проект using System; using System.Data; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; namespace Lights01 { public class DataViewForm: System.Windows.Forms.Form { private System.ComponentModel.Container components = null; DataTable dt; // Таблица. DataColumn c1, c2; // Столбцы таблцы. DataRow dr; // Строка таблицы. DataView dv; // Вьюер таблицы. DataRowView rv; // Вьюер строки таблицы. int currentCounter; // Счетчик текущей строки для вьюера таблицы. private System.Windows.Forms.DataGrid dG; private System.Windows.Forms.DataGrid dGforTable; private System.Windows.Forms.Button buttPrev; private System.Windows.Forms.Button buttFirst; private System.Windows.Forms.Button buttLast; private System.Windows.Forms.Button buttonFind; private System.Windows.Forms.TextBox demoTextBox; private System.Windows.Forms.TextBox findTextBox; private System.Windows.Forms.Button buttonAdd; private System.Windows.Forms.Button buttonAcc; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.Button buttNext; public DataViewForm() { InitializeComponent(); CreateTable(); dG.DataSource = dv; dGforTable.DataSource = dt; currentCounter = 0; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } protected override void Dispose(bool disposing) { if(disposing) { if(components!= null) { components.Dispose(); } } base.Dispose(disposing); } #region Windows Form Designer generated code // Required method for Designer support – do not modify // the contents of this method with the code editor. private void InitializeComponent() { this.dG = new System.Windows.Forms.DataGrid(); this.demoTextBox = new System.Windows.Forms.TextBox(); this.buttPrev = new System.Windows.Forms.Button(); this.buttNext = new System.Windows.Forms.Button(); this.buttFirst = new System.Windows.Forms.Button(); this.buttLast = new System.Windows.Forms.Button(); this.findTextBox = new System.Windows.Forms.TextBox(); this.buttonFind = new System.Windows.Forms.Button(); this.buttonAdd = new System.Windows.Forms.Button(); this.dGforTable = new System.Windows.Forms.DataGrid(); this.buttonAcc = new System.Windows.Forms.Button(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.groupBox2 = new System.Windows.Forms.GroupBox();((System.ComponentModel.ISupportInitialize)(this.dG)).BeginInit();((System.ComponentModel.ISupportInitialize)(this.dGforTable)).BeginInit(); this.groupBox1.SuspendLayout(); this.groupBox2.SuspendLayout(); this.SuspendLayout(); // // dG // this.dG.DataMember = ""; this.dG.HeaderForeColor = System.Drawing.SystemColors.ControlText; this.dG.Location = new System.Drawing.Point(8, 80); this.dG.Name = "dG"; this.dG.Size = new System.Drawing.Size(280, 128); this.dG.TabIndex = 0; this.dG.MouseDown += new System.Windows.Forms.MouseEventHandler(this.dG_MouseDown); // // demoTextBox // this.demoTextBox.Location = new System.Drawing.Point(152, 48); this.demoTextBox.Name = "demoTextBox"; this.demoTextBox.Size = new System.Drawing.Size(128, 20); this.demoTextBox.TabIndex = 1; this.demoTextBox.Text = ""; // // buttPrev // this.buttPrev.Location = new System.Drawing.Point(189, 16); this.buttPrev.Name = "buttPrev"; this.buttPrev.Size = new System.Drawing.Size(25, 23); this.buttPrev.TabIndex = 2; this.buttPrev.Text = "<–"; this.buttPrev.Click += new System.EventHandler(this.buttPrev_Click); // // buttNext // this.buttNext.Location = new System.Drawing.Point(221, 16); this.buttNext.Name = "buttNext"; this.buttNext.Size = new System.Drawing.Size(25, 23); this.buttNext.TabIndex = 3; this.buttNext.Text = "–>"; this.buttNext.Click += new System.EventHandler(this.buttNext_Click); // // buttFirst // this.buttFirst.Location = new System.Drawing.Point(157, 16); this.buttFirst.Name = "buttFirst"; this.buttFirst.Size = new System.Drawing.Size(25, 23); this.buttFirst.TabIndex = 4; this.buttFirst.Text = "<<"; this.buttFirst.Click += new System.EventHandler(this.buttFirst_Click); // // buttLast // this.buttLast.Location = new System.Drawing.Point(253, 16); this.buttLast.Name = "buttLast"; this.buttLast.Size = new System.Drawing.Size(25, 23); this.buttLast.TabIndex = 5; this.buttLast.Text = ">>"; this.buttLast.Click += new System.EventHandler(this.buttLast_Click); // // findTextBox // this.findTextBox.Location = new System.Drawing.Point(8, 48); this.findTextBox.Name = "findTextBox"; this.findTextBox.Size = new System.Drawing.Size(128, 20); this.findTextBox.TabIndex = 6; this.findTextBox.Text = ""; // // buttonFind // this.buttonFind.Location = new System.Drawing.Point(88, 16); this.buttonFind.Name = "buttonFind"; this.buttonFind.Size = new System.Drawing.Size(48, 23); this.buttonFind.TabIndex = 7; this.buttonFind.Text = "Find"; this.buttonFind.Click += new System.EventHandler(this.buttonFind_Click); // // buttonAdd // this.buttonAdd.Location = new System.Drawing.Point(8, 16); this.buttonAdd.Name = "buttonAdd"; this.buttonAdd.Size = new System.Drawing.Size(40, 23); this.buttonAdd.TabIndex = 8; this.buttonAdd.Text = "Add"; this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click); // // dGforTable // this.dGforTable.DataMember = ""; this.dGforTable.HeaderForeColor = System.Drawing.SystemColors.ControlText; this.dGforTable.Location = new System.Drawing.Point(8, 24); this.dGforTable.Name = "dGforTable"; this.dGforTable.Size = new System.Drawing.Size(272, 120); this.dGforTable.TabIndex = 9; // // buttonAcc // this.buttonAcc.Location = new System.Drawing.Point(8, 152); this.buttonAcc.Name = "buttonAcc"; this.buttonAcc.Size = new System.Drawing.Size(40, 23); this.buttonAcc.TabIndex = 10; this.buttonAcc.Text = "Acc"; this.buttonAcc.Click += new System.EventHandler(this.buttonAcc_Click); // // groupBox1 // this.groupBox1.Controls.Add(this.buttonAcc); this.groupBox1.Controls.Add(this.dGforTable); this.groupBox1.Location = new System.Drawing.Point(6, 8); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(298, 184); this.groupBox1.TabIndex = 11; this.groupBox1.TabStop = false; this.groupBox1.Text = "Таблица как она есть "; // // groupBox2 // this.groupBox2.Controls.Add(this.buttPrev); this.groupBox2.Controls.Add(this.buttonFind); this.groupBox2.Controls.Add(this.buttFirst); this.groupBox2.Controls.Add(this.buttLast); this.groupBox2.Controls.Add(this.demoTextBox); this.groupBox2.Controls.Add(this.buttNext); this.groupBox2.Controls.Add(this.dG); this.groupBox2.Controls.Add(this.buttonAdd); this.groupBox2.Controls.Add(this.findTextBox); this.groupBox2.Location = new System.Drawing.Point(8, 200); this.groupBox2.Name = "groupBox2"; this.groupBox2.Size = new System.Drawing.Size(296, 216); this.groupBox2.TabIndex = 12; this.groupBox2.TabStop = false; this.groupBox2.Text = "Вид через вьюер"; // // DataViewForm // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(312, 421); this.Controls.Add(this.groupBox2); this.Controls.Add(this.groupBox1); this.Name = "DataViewForm"; this.Text = "DataViewForm"; ((System.ComponentModel.ISupportInitialize)(this.dG)).EndInit();((System.ComponentModel.ISupportInitialize)(this.dGforTable)).EndInit(); this.groupBox1.ResumeLayout(false); this.groupBox2.ResumeLayout(false); this.ResumeLayout(false); } #endregion private void CreateTable() { // Создается таблица. dt = new DataTable("Items"); // Столбцы таблицы... // Имя первого столбца – id, тип значения – System.Int32. c1 = new DataColumn("id", Type.GetType("System.Int32")); c1.AutoIncrement=true; // Имя второго столбца – Item, тип значения – System.Int32. c2 = new DataColumn("Item", Type.GetType("System.Int32")); // К таблице добавляются объекты-столбцы... dt.Columns.Add(c1); dt.Columns.Add(c2); // А вот массив столбцов (здесь он из одного элемента) // для организации первичного ключа (множества первичных ключей). DataColumn[] keyCol= new DataColumn[1]; // И вот, собственно, как в таблице задается множество первичных ключей. keyCol[0]= c1; // Свойству объекта t передается массив, содержащий столбцы, которые // формируемая таблица t будет воспринимать как первичные ключи. dt.PrimaryKey=keyCol; // В таблицу добавляется 10 rows. for(int i = 0; i <10;i++) { dr=dt.NewRow(); dr["Item"]= i; dt.Rows.Add(dr); } // Принять изменения. // Так производится обновление таблицы. // Сведения о новых изменениях и добавлениях будут фиксироваться // вплоть до нового обновления. dt.AcceptChanges(); // Здесь мы применим специализированный конструктор, который // задаст значения свойств Table, RowFilter, Sort, RowStateFilter // объекта DataView в двух операторах кода... //dv = new DataView(dt); // Вместо этого... // Определение того, что доступно через объект - представитель DataView. // Задавать можно в виде битовой суммы значений. И не все значения сразу! // Сумма всех значений – противоречивое сочетание! // А можно ли делать это по отдельности? DataViewRowState dvrs = DataViewRowState.Added | DataViewRowState.CurrentRows | DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent | //DataViewRowState.ModifiedOriginal | //DataViewRowState.OriginalRows | //DataViewRowState.None | // Записи не отображаются. DataViewRowState.Unchanged; // Вот такое хитрое объявление... // Таблица, // | значение, относительно которого проводится сортировка, // | | // | | имя столбца, значения которого сортируются, // | | | // | | | составленное значение DataViewRowState. // | | | |dv=new DataView(dt, "", "Item", dvrs); } private void buttNext_Click(object sender, System.EventArgs e) { if (currentCounter+1 < dv.Count) currentCounter++; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } private void buttPrev_Click(object sender, System.EventArgs e) { if (currentCounter–1 >= 0) currentCounter––; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } private void buttFirst_Click(object sender, System.EventArgs e) { currentCounter = 0; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } private void buttLast_Click(object sender, System.EventArgs e) { currentCounter =dv.Count – 1; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } private void dG_MouseDown (object sender, System.Windows.Forms.MouseEventArgs e) { currentCounter = dG.CurrentRowIndex; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } // Реализация поиска требует специального опеделения порядка // сортировки строк, который должен задаваться в конструкторе. private void buttonFind_Click(object sender, System.EventArgs e) { int findIndex = –1; // Сначала проверяем строку на соответствие формату отыскиваемого // значения. // В нашем случае строка должна преобразовываться в целочисленное // значение. try { int.Parse(findTextBox.Text); } catch { findTextBox.Text = "Неправильно задан номер..."; return; } findIndex = dv.Find(findTextBox.Text); if (findIndex >= 0) { currentCounter = findIndex; rv = dv[currentCounter]; demoTextBox.Text = rv["Item"].ToString(); } else { findTextBox.Text = "Не нашли."; } } private void buttonAdd_Click(object sender, System.EventArgs e) { // При создании новой записи средствами вьюера таблицы, // связанный с ним вьюер строки переходит в состояние rv.IsNew. // При этом в действиях этих объектов есть своя логика. // И если туда не вмешиваться, при создании очередной записи // предыдущая запись считается принятой и включается в таблицу // автоматически. // Контролируя состояния вьюера строки (rv.IsEdit || rv.IsNew), // мы можем предотвратить процесс последовательного автоматического // обновления таблицы. Все под контролем. if (rv.IsEdit || rv.IsNew) return; rv = dv.AddNew(); rv["Item"] = dv.Count–1; } private void buttonAcc_Click(object sender, System.EventArgs e) { // И вот мы вмешались в процесс. // Добавление новой записи в таблицу становится возможным лишь // после явного завершения редактирования предыдущей записи. // Без этого попытки создания новой записи блокируются. // Завершить редактирование. rv.EndEdit(); // Принять изменения. // Так производится обновление таблицы. // Сведения о новых изменениях и добавлениях будут фиксироваться // вплоть до нового обновления. dt.AcceptChanges(); } } }
|
|
Компонент BindingSource выступает в качестве источника данных для некоторых или всех элементов управления в форме. В Visual Studio компонент BindingSource можно привязать к элементу управления с помощью свойства DataBindings, которое можно настроить в окне Свойства.
Можно привязать компонент BindingSource как к простым источникам данных, например к одному свойству объекта или базовой коллекции, как то ArrayList, так и к сложным источникам данных, например к таблице базы данных. Компонент BindingSource действует в качестве посредника, обеспечивающего привязку и работу служб оперативного управления. Во время разработки или выполнения можно привязать компонент BindingSource к сложному источнику данных путем присвоения его свойствам DataSource и DataMember значений базы данных и таблицы соответственно. В следующем примере описывается использование компонента BindingSource в существующей архитектуре привязки данных.
[ComplexBindingPropertiesAttribute("DataSource", "DataMember")]public class BindingSource: Component, IBindingListView, IBindingList, IList, ICollection, IEnumerable, ITypedList, ICancelAddNew, ISupportInitializeNotification, ISupportInitialize, ICurrencyManagerProvider
Свойства
Имя | Описание |
AllowEdit | Возвращает значение, показывающее, можно ли редактировать элементы в базовом списке. |
AllowNew | Возвращает или задает значение, указывающее, может ли быть использован метод AddNew для добавления элементов в список. |
AllowRemove | Возвращает значение, показывающее, можно ли удалять элементы из базового списка. |
CanRaiseEvents | Возвращает значение, показывающее, может ли компонент вызывать событие. (Унаследовано от Component.) |
Container | Возвращает контейнер IContainer, содержащий компонент Component. (Унаследовано от Component.) |
Count | Возвращает общее количество элементов в базовом списке, отфильтрованных в соответствии со значением свойства Filter. |
CurrencyManager | Получает диспетчер валюты, связанный с данным BindingSource. |
Current | Возвращает текущий элемент в списке. |
DataMember | Возвращает или задает список в источнике данных, к которому привязан соединитель в настоящее время. |
DataSource | Возвращает или задает источник данных, который привязывается к соединитель. |
DesignMode | Возвращает значение, указывающее, находится ли данный компонент Component в режиме конструктора в настоящее время. (Унаследовано отComponent.) |
Events | Возвращает список обработчиков событий, которые прикреплены к этому объекту Component. (Унаследовано от Component.) |
Filter | Возвращает или задает выражение, используемое для фильтрации, отображаемых строк. |
IsBindingSuspended | Возвращает значение, показывающее, приостановлена ли привязка списка. |
IsFixedSize | Возвращает значение, указывающее, имеет ли базовый список фиксированный размер. |
IsReadOnly | Возвращает значение, показывающее, является ли базовый список только для чтения. |
IsSorted | Получает значение, указывающее, сортируются ли элементы в базовом списке. |
IsSynchronized | Возвращает значение, показывающее, синхронизирован ли доступ к коллекции (safe потока). |
Item | Возвращает или задает элемент в список по указанному индексу. |
List | Получает список, к которому привязан соединитель. |
Position | Возвращает или задает индекс текущего элемента в базовом списке. |
RaiseListChangedEvents | Возвращает или задает значение, определяющее, будут ли события ListChanged вызываться. |
Site | Получает или задает экземпляр ISite для компонента Component. (Унаследовано от Component.) |
Sort | Возвращает или задает имена столбцов, используемые для сортировки и порядок сортировки для просмотра строки в источнике данных. |
SortDescriptions | Возвращает коллекцию описаний сортировки, применяемых к источнику данных. |
SortDirection | Возвращает направление сортировки элементов в списке. |
SortProperty | Инфраструктура. Возвращает PropertyDescriptor, используемый для сортировки списка. |
SupportsAdvancedSorting | Возвращает значение, показывающее, поддерживает ли источник данных сортировку по нескольким столбцам. |
SupportsChangeNotification | Возвращает значение, показывающее, поддерживает ли источник данных уведомление об изменениях. |
SupportsFiltering | Возвращает значение, показывающее, поддерживает ли источник данных фильтрацию. |
SupportsSearching | Возвращает значение, показывающее, поддерживает ли источник данных поиск с Find метод. |
SupportsSorting | Возвращает значение, показывающее, поддерживает ли источник данных сортировку. |
SyncRoot | Получает объект, который позволяет синхронизировать доступ к базовому списку. |
Методы
Имя | Описание |
Add | Добавляет существующий элемент во внутренний список. |
AddNew | Добавляет новый элемент в базовый список. |
ApplySort(ListSortDescriptionCollection) | Сортирует источник данных, используя заданные описания сортировки. |
ApplySort(PropertyDescriptor, ListSortDirection) | Сортирует источник данных, используя дескриптор, и направление сортировки указанного свойства. |
CancelEdit | Отменяет текущую операцию редактирования. |
Clear | Удаляет все элементы из списка. |
Contains | Определяет, является ли объект элемента в списке. |
CopyTo | Копирует содержимое List в заданный массив, начиная с указанного значения индекса. |
CreateObjRef | Создает объект, который содержит всю необходимую информацию для создания прокси-сервера, используемого для взаимодействия с удаленным объектом. (Унаследовано от MarshalByRefObject.) |
Dispose() | Освобождает все ресурсы, используемые объектом Component. (Унаследовано от Component.) |
Dispose(Boolean) | Выпуски неуправляемые ресурсы, используемые BindingSource выпуски и, при необходимости, управляемые ресурсы. (ПереопределяетComponent.Dispose(Boolean).) |
EndEdit | Применяет ожидающие изменения к базовому источнику данных. |
Equals(Object) | Определяет, равен ли заданный объект текущему объекту. (Унаследовано от Object.) |
Finalize | Освобождает неуправляемые ресурсы и выполняет другие операции очистки, перед тем как объект Component будет удален при сборке мусора. (Унаследовано от Component.) |
Find(PropertyDescriptor, Object) | Выполняет поиск индекса элемента, который имеет заданный дескриптор свойства. |
Find(String, Object) | Возвращает индекс элемента в списке с указанным именем и значением свойства. |
GetEnumerator | Извлекает перечислитель List. |
GetHashCode | Служит хэш-функцией по умолчанию. (Унаследовано от Object.) |
GetItemProperties | Извлекает массив PropertyDescriptor объекты, представляющие связываемые свойства списка тип источника данных. |
GetLifetimeService | Извлекает объект обслуживания во время существования, который управляет политикой времени существования данного экземпляра.(Унаследовано от MarshalByRefObject.) |
GetListName | Получает имя списка, предоставляющего данные для привязки. |
GetRelatedCurrencyManager | Получает связанный диспетчер валют для указанного элемента данных. |
GetService | Возвращает объект, представляющий службу, обеспечиваемую компонентом Component или его контейнером Container. (Унаследовано отComponent.) |
GetType | Возвращает объект Type для текущего экземпляра. (Унаследовано от Object.) |
IndexOf | Осуществляет поиск указанного объекта и возвращает индекс первого вхождения в пределах всего списка. |
InitializeLifetimeService | Возвращает объект обслуживания во время существования для управления политикой времени существования данного экземпляра.(Унаследовано от MarshalByRefObject.) |
Insert | Вставляет элемент в список по указанному индексу. |
MemberwiseClone() | Создает неполную копию текущего объекта Object. (Унаследовано от Object.) |
MemberwiseClone(Boolean) | Создает неполную копию текущего объекта MarshalByRefObject. (Унаследовано от MarshalByRefObject.) |
MoveFirst | Переходит к первому элементу в списке. |
MoveLast | Переход к последнему элементу в списке. |
MoveNext | Переходит к следующему элементу в списке. |
MovePrevious | Переход к предыдущему элементу в списке. |
OnAddingNew | Вызывает AddingNew событие. |
OnBindingComplete | Вызывает BindingComplete событие. |
OnCurrentChanged | Вызывает CurrentChanged событие. |
OnCurrentItemChanged | Вызывает CurrentItemChanged событие. |
OnDataError | Вызывает DataError событие. |
OnDataMemberChanged | Вызывает DataMemberChanged событие. |
OnDataSourceChanged | Вызывает DataSourceChanged событие. |
OnListChanged | Вызывает ListChanged событие. |
OnPositionChanged | Вызывает PositionChanged событие. |
Remove | Удаляет указанный элемент из списка. |
RemoveAt | Удаляет элемент по указанному индексу. |
RemoveCurrent | Удаляет текущий элемент из списка. |
RemoveFilter | Удаляет фильтр, связанный с BindingSource. |
RemoveSort | Удаляет сортировку, связанная с BindingSource. |
ResetAllowNew | Инфраструктура. Повторно инициализируется AllowNew свойство. |
ResetBindings | Заставляет элемент управления, связанный BindingSource, перечитать все элементы в списке и обновить их отображаемые значения. |
ResetCurrentItem | Заставляет элемент управления, связанный с BindingSource, перечитать выделенный элемент и обновить его отображаемое значение. |
ResetItem | Вызывает элемент управления к прыгнуть BindingSource перечитать элемент по указанному индексу и обновить его отображаемое значение. |
ResumeBinding | Возобновляет привязку данных. |
SuspendBinding | Приостанавливает привязки данных для предотвращения изменения из обновить связанный источник данных. |
ToString | Возвращает строку String, содержащую имя компонента Component, если таковое имеется. Этот метод не следует переопределять.(Унаследовано от Component.) |
Пример использования
…
private void buFirst_Click(object sender, EventArgs e)
{
bsAuto.MoveFirst();
buPrior.Enabled = false;
buFirst.Enabled = false;
buNext.Enabled = true;
buLast.Enabled = true;
}
private void buLast_Click(object sender, EventArgs e)
{
bsAuto.MoveLast();
buPrior.Enabled = true;
buFirst.Enabled = true;
buNext.Enabled = false;
buLast.Enabled = false;
}
private void buNext_Click(object sender, EventArgs e)
{
buPrior.Enabled = true;
buFirst.Enabled = true;
bsAuto.MoveNext();
if (bsAuto.Position == bsAuto.Count - 1)
{
buPrior.Enabled = true;
buFirst.Enabled = true;
buNext.Enabled = false;
buLast.Enabled = false;
}
}
private void buPrior_Click(object sender, EventArgs e)
{
buNext.Enabled = true;
buLast.Enabled = true;
bsAuto.MovePrevious();
if (bsAuto.Position == 0)
{
buPrior.Enabled = false;
buFirst.Enabled = false;
buNext.Enabled = true;
buLast.Enabled = true;
}
}
private void buDelete_Click(object sender, EventArgs e)
{
if (MessageBox.Show("", "", MessageBoxButtons.OKCancel) == System.Windows.Forms.DialogResult.Cancel) { return; }
bsAuto.RemoveCurrent();
bsAuto_PositionChanged(this, null);
bsAuto.EndEdit();
WorkWithBase.GetDataAdapter().Update((DataTable)bsAuto.DataSource);
//WorkWithBase.myDataAdapter.Update((DataTable)bsAuto.DataSource);
}
private void bsAuto_PositionChanged(object sender, EventArgs e)
{
if (dataGridView1.DataSource == null) { return; }
if (bsAuto.Position > bsAuto.Count - 1) { return; }
if (bsAuto.Position == -1) { return; }
using (MemoryStream memoryStream = new MemoryStream())
{
//if (dataGridView1.CurrentRow.Cells["SizeOfShot"].Value == DBNull.Value) { pbShotOfNumber.Image = null; return; }
DataRowView dr = (DataRowView)bsAuto.Current;
if (dr["SizeOfShot"] == DBNull.Value) { pbShotOfNumber.Image = null; return; }
//memoryStream.Write((byte[])dataGridView1.CurrentRow.Cells["ShotOfNumber"].Value, 0, (int)dataGridView1.CurrentRow.Cells["SizeOfShot"].Value);
memoryStream.Write((byte[])dr["ShotOfNumber"], 0, (int)dr["SizeOfShot"]);
pbShotOfNumber.Image = new Bitmap(memoryStream);
}
}
...
Ход работы
- Для выполнения данной работы необходимо использовать проект приложения БД, разработанный в ходе выполнения предыдущей лабораторной работы.
- В наборе таблиц, разработанных и реализованных вами ранее, выбрать одну таблицу, наиболее пригодную, с вашей точки зрения, для демонстрации результатов решения типичных задач приложения БД.
- Создать объект класса DataView на основе выбранной вами таблицы, и с его помощью выполнить следующие действия: сортировку записей, фильтрацию записей, поиск записей по указанному значению одного (нескольких) полей. Отобразить результаты выполненных действий с использованием компонента DataGridView.
- Добавить на форму кнопки и в обработчиках событий для этих кнопок обеспечить с использованием методов компонента BindingSource навигацию, добавление и удаление записей (см. теоретические сведения и пример использования по компоненту в методических указаниях к данной лабораторной работе).
Лабораторная работа 10
Настройка табличных форм для отображения и редактирования данных из БД под пользовательские требования на примере компонента DataGridView Теоретические сведенияРассмотрим элементный фундамент, на котором основывается функциональная мощь DataGridView. В своей простейшей форме DataGridView имеет базисные компоненты, представленные на рисунке 1.
Рисунок 1.
Помимо базисных элементов и базисного внешнего вида у этого control-а есть базовое поведение. Иными словами, если поместить новый DataGridView на форму и не производить никаких спецнастроек, то control будет:
- Автоматически показывать заголовки колонок и заголовки строк. И те, и другие остаются видимыми при любом скроллинге.
- Ставить на одном из заголовков строк маркер (черный треугольничек) текущей строки.
- Выбирать целую строку, если пользователь щелкнет по заголовку строки.
- Выбирать сразу несколько строк, если щелчок по заголовку строки производится с зажатым Ctrl или Shift. При этом текущая строка (помеченная треугольничком) всегда будет единственной.
- Удалять все выбранные строки по нажатию на Delete.
- Отображать ячейку, имеющую фокус ввода, особым образом.
- Если пользователь выполнит двойной щелчок по разделителю колонок, будет произведена автоподборка ширины левой колонки.
- Если в методе Main приложения был вызван метод EnableVisualStyles, будет применяться стиль Windows XP, выбранный в настройках рабочего стола.
Помимо этого control будет поддерживать редактирование содержимого:
- Если пользователь выполнит двойной щелчок по ячейке (или нажмет на ней F2), данная ячейка будет переведена в режим редактирования.
- Если пользователь изменит хотя бы один символ в редактируемой ячейке, на заголовке соответствующей строки появится спецсимвол (пишущий карандашик), и будет отображаться до тех пор, пока фокус ввода не покинет редактируемую ячейку, или пока пользователь не нажмет Esc. Последнее действие восстановит то значение ячейки, которое она содержала до входа в режим редактирования.
- Если пользователь прокрутит control вниз до последней строчки, будет отображена дополнительная, специальная строчка для внесения новой записи. Такая строчка всегда помечена символом звездочки на заголовке. Когда пользователь любым способом перемещается в эту строку, DataGridView добавляет новую запись со значениями по умолчанию. Если фокус ввода находится в этой строке, и пользователь нажимает Esc, новая запись пропадает, а фокус ввода перемещается на строчку выше.
Если DataGridView привязан через свойство DataSource к источнику данных, то по умолчанию выполняется следующее.
- Каждая колонка, получаемая из источника данных, вызовет добавление соответствующей колонки в control-е.
- Названия колонок источника отобразятся в заголовках колонок.
- Если пользователь щелкнет по заголовку колонки, строки будут автоматически отсортированы.
Излишне говорить, что практически все из перечисленного выше может быть разрешено/запрещено/настроено.
Привязка данных
Как известно, прежде чем начать усиленно и красиво отображать данные, эти самые данные надо получить. DataGridView поддерживает три режима работы с данными:
- Первый, основной – отображение данных из внешних коллекций (например, ListView, DataTable).
- Специальный режим отображения свободных (unbound) данных, то есть данные хранятся в самом control-е.
- Еще один особый режим работы – виртуальный (Virtual mode). В нем control посылает событие, при поступлении которого прикладной код возвращает некоторые данные. Так как данные при этом не обязаны где-то храниться, виртуальный режим может оперировать миллионами строк без каких-либо проблем с производительностью или нехваткой памяти.
80% времени control будет работать в основном режиме, так как в большинстве случаев данные будут поступать из СУБД, при этом копируясь в промежуточные коллекции, например, DataTable.
Привязывать элементы пользовательского интерфейса можно отнюдь не исключительно к таблично представленным данным. Практически любая структура данных может выступить в роли их источника – обычные объекты, массивы, коллекции и т.д. Хотя вопрос привязки данных в мире WinForms (Windows Forms Data Binding) совершенно выходит за рамки данной статьи ввиду его масштабности, не упомянуть ключевые моменты этой технологии было бы несомненным упущением. Сжато исследуем вопрос – как рекомендуется привязывать DataGridView к данным, и чем Framework 2.0 может нас порадовать при сравнении с версиями 1.x.
В Framework 2.0 процедура привязки данных упростилась. Чтобы продемонстрировать это, разберем, как осуществлялась привязка данных во Framework 1.x (см рисунок 2).
Рисунок 2.
А что сегодня? Сегодня у нас новый герой – BindingSource (см. рисунок 3).
Рисунок 3.
Приведенные выше иллюстрации показывают, что BindingSource представляет собой промежуточный слой между источником данных и control-ом, к нему привязанным. Поэтому в мире Framework 2.0 вполне возможны подобные привязки:
public partial class Form1: Form{ public Form1() { InitializeComponent(); //_biSour - объект типа BindingSource _biSour.DataSource = new PersonCollection(); //_grid - обычный, без настроек, DataGridView _grid.DataSource = _biSour; }} public class PersonCollection: System.Collections.IEnumerable{ public System.Collections.IEnumerator GetEnumerator() { for(uint i = 0; i <= 5; i++) { yield return new Person("Name_" + i.ToString(), 20 + i, 'M'); } }} public class Person{ private string _name; private uint _age; private char _gender;....// свойства, инкапсулирующие эти три поля public Person(string name, uint age, char gender) {... }} |
Отображает grid с шестью записями.
Общий подход при привязке любого WinForms-control к BindingSource следующий. Допустим, у нас есть DataSet NorthwindDataSet с единственной таблицей Products. Тогда первым шагом будет привязка BindingSource к этому источнику данных:
// _biSour - объект типа BindingSource_biSour.DataSource = this.NorthwindDataSet;// сразу привязываемся к конкретной таблице_biSour.DataMember = "Products"; |
Теперь BindingSource сам становится полноценным источником данных. Единственное, что отличает его от "нормального" источника вроде того же DataTable, – отсутствие "собственных" данных, т.к. данные BindingSource – это данные нижележащего источника данных. Таким образом, при необходимости привязки свойства Text к колонке ProductName таблицы Products мы можем смело писать:
this.label1.DataBindings.Add(new Binding("Text", _biSour, "ProductName", true)); |
Так же обстоит дело со сложной привязкой той же колонки к control-у, поддерживающему подобную привязку:
this.comboBox1.DataSource = _biSour;this.comboBox1.DisplayMember = "ProductName"; |
Привязка DataGridView ко всей таблице Products:
//_grid - обычный, без настроек, DataGridView_grid.DataSource = _biSour; |
Обратите внимание, что свойство DataMember в последнем случае остается незадействованным. BindingSource уже привязан к интересующей нас таблице, и уточнять путь к ней внутри DataSet-а необходимости нет.
Отметим, что формальное требование к источнику данных у нового control-а осталось практически неизменным по сравнению с его предшественником, DataGrid. Единственное исключение выражается в требовании того, что свойства DataSource и DataMember теперь должны в сочетании определять некоторый список, то есть коллекцию, реализующую IEnumerable или интерфейсы, унаследованные от него, также возможно использовать в качестве источника данных компонент, реализующий IListSourse. Два свойства нужны, например, для того, чтобы указать некоторый DataTable, входящий в состав DataSet.
На рисунке 4 приведена блок-схема привязки.
Рисунок 4.
С практической точки зрения все это означает, что DataGridView следует привязывать исключительно к BindingSource, который сам реализует один из требуемых интерфейсов (именно – IBindingListView) и позволяет привязываться к широкому диапазону источников данных. Вариант с источником, реализующим IEnumerable был рассмотрен выше. Если в процессе исполнения приложения нужно отслеживать изменения значений свойств DataSource и DataMember, можно воспользоваться событиями DataSourceChanged и DataMemberChanged.
Рассмотрим также событие DataGridView. DataBindingComplete. Оно будет сгенерировано как при изменении значения любого из двух упомянутых свойств, так и при наполнении control-а новыми данными (например, методом Fill адаптера данных). Нужно помнить только, что это все – события DataGridView, к самому источнику данных они никакого отношения не имеют. На практике же, чаще всего, интересны изменения данных именно в источнике, а не в control-е, эти данные отображающем. Для такого сценария пригодится объект BindingSource с его замечательными событиями AddingNew, BindingComplete, CurrentChanged, CurrentItemChanged, ListChanged и целым рядом других.
Общая архитектура
Одной из особенностей DataGridView является обилие у него классов-компаньонов. Таким образом, изучаемый control имеет расширяемую архитектуру, где значительную часть функциональности, доступной конечному пользователю, обеспечивают сторонние классы-компаньоны (или классы-плагины, если хотите). При таком подходе к вопросу, создавая свои собственные классы-плагины (или наследуя их от существующих и расширяя готовую функциональность), мы можем наращивать и видоизменять интересные нам аспекты поведения или внешнего вида конечного control-а практически до бесконечности. На рисунке 3 приведен фрагмент дерева классов.
Рисунок 5.
Какие выводы можно сделать, просматривая эту диаграмму? Классы-плагины именуются строго по шаблону DataGridView< Назначение_Плагина >.
Базовым классом для всех плагинов DataGridView является класс DataGridViewElement. Любой элемент-плагин должен уметь сообщить, к какому DataGridView он "приписан", а также уметь сообщить, в каком состоянии он находится. В данном случае "состояние" – это комбинация потенциально возможных режимов отображения элемента. К примеру, элемент может сказать, что он доступен только для чтения (ReadOnly), и что он «заморожен» (Frozen, скроллинг запрещен). Так вот, первое из двух "умений" обеспечивается свойством DataGridView (тип DataGridView) базового класса, а второе – его же свойством State, возвращающим комбинацию значений перечисления DataGridViewElementStates. Оба свойства, разумеется, доступны только для чтения.