Практическое руководство. Правильное позиционирование выделенной строки в дочерней таблице
Зачастую при работе с привязкой данных в формах Windows Forms, данные будут отображаться в виде представлений «родительский-дочерний» или «основной-подробности». Это относится к сценариям привязки данных, где отображаются данные из одного источника в двух элементах управления. Изменение выделения в одном элементе управления вызывает изменение данных, отображаемых во втором элементе управления. Например, первый элемент управления может содержать список клиентов, а второй — список заказов, связанных с выбранным клиентом из первого элемента управления.
Начиная с .NET Framework версии 2.0, при отображении данных в представлении «родительский-дочерний» может потребоваться предпринять дополнительные действия, чтобы убедиться в том, что выбранная строка в дочерней таблице не сбрасывается на первую строку таблицы. Для этого необходимо кэшировать позицию дочерней таблицы и сбрасывать ее после изменения родительской таблицы. Обычно сброс дочерних таблиц происходит после первого изменения поля в строке родительской таблицы.
Кэширование текущей позиции дочерней таблицы
Объявите целочисленную переменную для хранения позиции в дочернем списке и логическую переменную для хранения признака, следует ли кэшировать позицию дочерней таблицы.
private int cachedPosition = -1; private bool cacheChildPosition = true;
Обработайте событие ListChanged для привязки CurrencyManager и проверьте значение ListChangedType из Reset.
Проверьте текущую позицию CurrencyManager. Если она больше, чем первая запись в списке (обычно 0), сохраните ее в переменной.
void relatedCM_ListChanged(object sender, ListChangedEventArgs e) { // Check to see if this is a caching situation. if (cacheChildPosition && cachePositionCheckBox.Checked) { // If so, check to see if it is a reset situation, and the current // position is greater than zero. CurrencyManager relatedCM = sender as CurrencyManager; if (e.ListChangedType == ListChangedType.Reset && relatedCM.Position > 0) // If so, cache the position of the child table. cachedPosition = relatedCM.Position; } }
Обработайте событие CurrentChanged родительского списка для родительского диспетчера денежного формата. В обработчике задайте логическое значение, указывающее, что он не относится к сценарию кэширования. Если возникает CurrentChanged, то в родительском объекте происходит изменение, которое является изменением позиции в списке, а не изменением значения элемента.
void bindingSource1_CurrentChanged(object sender, EventArgs e) { // If the CurrentChanged event occurs, this is not a caching // situation. cacheChildPosition = false; }
Сброс позиции дочерней таблицы
Обработайте событие PositionChanged для дочерней привязкиCurrencyManager.
Установите позицию в дочерней таблице на кэшированную позицию, сохраненную в предыдущей процедуре.
void relatedCM_PositionChanged(object sender, EventArgs e) { // Check to see if this is a caching situation. if (cacheChildPosition && cachePositionCheckBox.Checked) { CurrencyManager relatedCM = sender as CurrencyManager; // If so, check to see if the current position is // not equal to the cached position and the cached // position is not out of bounds. if (relatedCM.Position != cachedPosition && cachedPosition > 0 && cachedPosition < relatedCM.Count) { relatedCM.Position = cachedPosition; cachedPosition = -1; } } }
Пример
В следующем примере показано, как сохранить текущую позицию в CurrencyManager для дочерней таблицы и сбросить позицию после завершения изменения родительской таблицы. Этот пример содержит два элемента управления DataGridView, которые привязаны к двум таблицам в DataSet с помощью компонента BindingSource. Между двумя таблицами установлено отношение, добавлено отношение к DataSet. В демонстрационных целях позиция дочерней таблицы изначально установлена на третью строку.
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace BT2
{
public class Form1 : Form
{
public Form1()
{
InitializeControlsAndDataSource();
}
// Declare the controls to be used.
private BindingSource bindingSource1;
private DataGridView dataGridView1;
private Button button1;
private DataGridView dataGridView2;
private CheckBox cachePositionCheckBox;
public DataSet set1;
private void InitializeControlsAndDataSource()
{
// Initialize the controls and set location, size and
// other basic properties.
this.dataGridView1 = new DataGridView();
this.bindingSource1 = new BindingSource();
this.button1 = new Button();
this.dataGridView2 = new DataGridView();
this.cachePositionCheckBox = new System.Windows.Forms.CheckBox();
this.dataGridView1.ColumnHeadersHeightSizeMode =
DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Dock = DockStyle.Top;
this.dataGridView1.Location = new Point(0, 20);
this.dataGridView1.Size = new Size(292, 170);
this.button1.Location = new System.Drawing.Point(18, 175);
this.button1.Size = new System.Drawing.Size(125, 23);
button1.Text = "Clear Parent Field";
this.button1.Click += new System.EventHandler(this.button1_Click);
this.dataGridView2.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView2.Location = new System.Drawing.Point(0, 225);
this.dataGridView2.Size = new System.Drawing.Size(309, 130);
this.cachePositionCheckBox.AutoSize = true;
this.cachePositionCheckBox.Checked = true;
this.cachePositionCheckBox.Location = new System.Drawing.Point(150, 175);
this.cachePositionCheckBox.Name = "radioButton1";
this.cachePositionCheckBox.Size = new System.Drawing.Size(151, 17);
this.cachePositionCheckBox.Text = "Cache and restore position";
this.ClientSize = new System.Drawing.Size(325, 420);
this.Controls.Add(this.dataGridView1);
this.Controls.Add(this.cachePositionCheckBox);
this.Controls.Add(this.dataGridView2);
this.Controls.Add(this.button1);
// Initialize the data.
set1 = InitializeDataSet();
// Set the data source to the DataSet.
bindingSource1.DataSource = set1;
//Set the DataMember to the Menu table.
bindingSource1.DataMember = "Customers";
// Add the control data bindings.
dataGridView1.DataSource = bindingSource1;
// Set the data source and member for the second DataGridView.
dataGridView2.DataSource = bindingSource1;
dataGridView2.DataMember = "custOrders";
// Get the currency manager for the customer orders binding.
CurrencyManager relatedCM =
bindingSource1.GetRelatedCurrencyManager("custOrders");
// Set the position in the child table for demonstration purposes.
relatedCM.Position = 3;
// Handle the current changed event. This event occurs when
// the current item is changed, but not when a field of the current
// item is changed.
bindingSource1.CurrentChanged +=
new EventHandler(bindingSource1_CurrentChanged);
// Handle the two events for caching and resetting the position.
relatedCM.ListChanged += new ListChangedEventHandler(relatedCM_ListChanged);
relatedCM.PositionChanged
+= new EventHandler(relatedCM_PositionChanged);
// Set cacheing to true in case current changed event
// occured on set up.
cacheChildPosition = true;
}
// Establish the data set with two tables and a relationship
// between them.
private DataSet InitializeDataSet()
{
set1 = new DataSet();
// Declare the DataSet and add a table and column.
set1.Tables.Add("Customers");
set1.Tables[0].Columns.Add("CustomerID");
set1.Tables[0].Columns.Add("Customer Name");
set1.Tables[0].Columns.Add("Contact Name");
// Add some rows to the table.
set1.Tables["Customers"].Rows.Add("c1", "Fabrikam, Inc.", "Ellen Adams");
set1.Tables[0].Rows.Add("c2", "Lucerne Publishing", "Don Hall");
set1.Tables[0].Rows.Add("c3", "Northwind Traders", "Lori Penor");
set1.Tables[0].Rows.Add("c4", "Tailspin Toys", "Michael Patten");
set1.Tables[0].Rows.Add("c5", "Woodgrove Bank", "Jyothi Pai");
// Declare the DataSet and add a table and column.
set1.Tables.Add("Orders");
set1.Tables[1].Columns.Add("CustomerID");
set1.Tables[1].Columns.Add("OrderNo");
set1.Tables[1].Columns.Add("OrderDate");
// Add some rows to the table.
set1.Tables[1].Rows.Add("c1", "119", "10/04/2006");
set1.Tables[1].Rows.Add("c1", "149", "10/10/2006");
set1.Tables[1].Rows.Add("c1", "159", "10/12/2006");
set1.Tables[1].Rows.Add("c2", "169", "10/10/2006");
set1.Tables[1].Rows.Add("c2", "179", "10/10/2006");
set1.Tables[1].Rows.Add("c2", "189", "10/12/2006");
set1.Tables[1].Rows.Add("c3", "122", "10/04/2006");
set1.Tables[1].Rows.Add("c4", "130", "10/10/2006");
set1.Tables[1].Rows.Add("c5", "1.29", "10/14/2006");
DataRelation dr = new DataRelation("custOrders",
set1.Tables["Customers"].Columns["CustomerID"],
set1.Tables["Orders"].Columns["CustomerID"]);
set1.Relations.Add(dr);
return set1;
}
private int cachedPosition = -1;
private bool cacheChildPosition = true;
void relatedCM_ListChanged(object sender, ListChangedEventArgs e)
{
// Check to see if this is a caching situation.
if (cacheChildPosition && cachePositionCheckBox.Checked)
{
// If so, check to see if it is a reset situation, and the current
// position is greater than zero.
CurrencyManager relatedCM = sender as CurrencyManager;
if (e.ListChangedType == ListChangedType.Reset && relatedCM.Position > 0)
// If so, cache the position of the child table.
cachedPosition = relatedCM.Position;
}
}
void bindingSource1_CurrentChanged(object sender, EventArgs e)
{
// If the CurrentChanged event occurs, this is not a caching
// situation.
cacheChildPosition = false;
}
void relatedCM_PositionChanged(object sender, EventArgs e)
{
// Check to see if this is a caching situation.
if (cacheChildPosition && cachePositionCheckBox.Checked)
{
CurrencyManager relatedCM = sender as CurrencyManager;
// If so, check to see if the current position is
// not equal to the cached position and the cached
// position is not out of bounds.
if (relatedCM.Position != cachedPosition && cachedPosition
> 0 && cachedPosition < relatedCM.Count)
{
relatedCM.Position = cachedPosition;
cachedPosition = -1;
}
}
}
int count = 0;
private void button1_Click(object sender, EventArgs e)
{
// For demo purposes--modifies a value in the first row of the
// parent table.
DataRow row1 = set1.Tables[0].Rows[0];
row1[1] = DBNull.Value;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Чтобы проверить пример кода, выполните следующие действия.
Запустите пример.
Убедитесь, что установлен флажок Кэшировать и сбрасывать позицию.
Нажмите кнопку Очистить родительское поле, чтобы произвести изменение поля родительской таблицы. Обратите внимание, что выбранная строка в дочерней таблице не изменяется.
Закройте и снова запустите пример. Это необходимо сделать, поскольку сброс происходит только при первом изменении в родительской строке.
Сбросьте флажок Кэшировать и сбрасывать позицию.
Нажмите кнопку Очистить родительское поле. Обратите внимание, что выбранная строка в дочерней таблице становится первой строкой.
Компиляция кода
Для этого примера требуются:
- ссылки на сборки System, System.Data, System.Drawing, System.Windows.Forms и System.XML.
Сведения о выполнении сборки этого примера из командной строки для Visual Basic или Visual C#, см. в разделе построение из командной строки или командной строки построение с помощью csc.exe. Можно также сборке этого примера в Visual Studio путем вставки кода в новый проект.