Show / Hide Table of Contents

Практическое руководство. Правильное позиционирование выделенной строки в дочерней таблице

Зачастую при работе с привязкой данных в формах Windows Forms, данные будут отображаться в виде представлений «родительский-дочерний» или «основной-подробности». Это относится к сценариям привязки данных, где отображаются данные из одного источника в двух элементах управления. Изменение выделения в одном элементе управления вызывает изменение данных, отображаемых во втором элементе управления. Например, первый элемент управления может содержать список клиентов, а второй — список заказов, связанных с выбранным клиентом из первого элемента управления.

Начиная с .NET Framework версии 2.0, при отображении данных в представлении «родительский-дочерний» может потребоваться предпринять дополнительные действия, чтобы убедиться в том, что выбранная строка в дочерней таблице не сбрасывается на первую строку таблицы. Для этого необходимо кэшировать позицию дочерней таблицы и сбрасывать ее после изменения родительской таблицы. Обычно сброс дочерних таблиц происходит после первого изменения поля в строке родительской таблицы.

Кэширование текущей позиции дочерней таблицы

  1. Объявите целочисленную переменную для хранения позиции в дочернем списке и логическую переменную для хранения признака, следует ли кэшировать позицию дочерней таблицы.

    private int cachedPosition = -1;
    private bool cacheChildPosition = true;
    
  2. Обработайте событие ListChanged для привязки CurrencyManager и проверьте значение ListChangedType из Reset.

  3. Проверьте текущую позицию 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;
        }
    }
    
  4. Обработайте событие CurrentChanged родительского списка для родительского диспетчера денежного формата. В обработчике задайте логическое значение, указывающее, что он не относится к сценарию кэширования. Если возникает CurrentChanged, то в родительском объекте происходит изменение, которое является изменением позиции в списке, а не изменением значения элемента.

    void bindingSource1_CurrentChanged(object sender, EventArgs e)
    {
        // If the CurrentChanged event occurs, this is not a caching 
        // situation.
        cacheChildPosition = false;
    }
    

Сброс позиции дочерней таблицы

  1. Обработайте событие PositionChanged для дочерней привязкиCurrencyManager.

  2. Установите позицию в дочерней таблице на кэшированную позицию, сохраненную в предыдущей процедуре.

    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());
        }
    }

    
}

Чтобы проверить пример кода, выполните следующие действия.

  1. Запустите пример.

  2. Убедитесь, что установлен флажок Кэшировать и сбрасывать позицию.

  3. Нажмите кнопку Очистить родительское поле, чтобы произвести изменение поля родительской таблицы. Обратите внимание, что выбранная строка в дочерней таблице не изменяется.

  4. Закройте и снова запустите пример. Это необходимо сделать, поскольку сброс происходит только при первом изменении в родительской строке.

  5. Сбросьте флажок Кэшировать и сбрасывать позицию.

  6. Нажмите кнопку Очистить родительское поле. Обратите внимание, что выбранная строка в дочерней таблице становится первой строкой.

Компиляция кода

Для этого примера требуются:

  • ссылки на сборки System, System.Data, System.Drawing, System.Windows.Forms и System.XML.

Сведения о выполнении сборки этого примера из командной строки для Visual Basic или Visual C#, см. в разделе построение из командной строки или командной строки построение с помощью csc.exe. Можно также сборке этого примера в Visual Studio путем вставки кода в новый проект.

См. также

  • Практическое руководство. Синхронизация элементов управления, связанных с одним источником данных
  • Компонент BindingSource
  • Связывание данных и Windows Forms
Back to top Неофициальная документация по .NET на русском языке. Лицензия: CC-BY 4.0. Основано на документации по .NET с Microsoft Docs
Generated by DocFX