다음을 통해 공유


방법: 자식 테이블에서 선택된 행이 올바른 위치에 유지되도록 설정

Windows Forms에서 데이터 바인딩을 사용할 때 부모/자식 또는 마스터/세부 정보 뷰에 데이터를 표시하는 경우가 많습니다. 이는 동일한 소스의 데이터가 두 컨트롤에 표시되는 데이터 바인딩 시나리오를 가리킵니다. 한 컨트롤에서 선택 항목을 변경하면 두 번째 컨트롤에 표시되는 데이터가 변경됩니다. 예를 들어 첫 번째 컨트롤에는 고객 목록이 포함되고 두 번째 컨트롤에는 첫 번째 컨트롤에서 선택한 고객과 관련된 주문 목록이 포함될 수 있습니다.

.NET Framework 버전 2.0부터 부모/자식 뷰에 데이터를 표시하는 경우 자식 테이블에서 현재 선택된 행이 테이블의 첫 번째 행으로 다시 설정되지 않도록 추가 단계를 수행해야 할 수도 있습니다. 이 작업을 수행하려면 자식 테이블 위치를 캐시하고 부모 테이블이 변경된 후 다시 설정해야 합니다. 일반적으로 자식 다시 설정은 부모 테이블의 행에 있는 필드가 처음 변경될 때 발생합니다.

현재 자식 위치를 캐시하려면

  1. 정수 변수를 선언하여 자식 목록 위치를 저장하고, 부울 변수를 선언하여 자식 위치를 캐시할지 여부를 저장합니다.

    private int cachedPosition = -1;
    private bool cacheChildPosition = true;
    
    Private cachedPosition As Integer = - 1
    Private cacheChildPosition As Boolean = True
    
    
  2. 바인딩의 CurrencyManager에 대한 ListChanged 이벤트를 처리하고 ResetListChangedType을 확인합니다.

  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;
        }
    }
    
    Private Sub relatedCM_ListChanged(ByVal sender As Object, _
        ByVal e As ListChangedEventArgs)
        ' Check to see if this is a caching situation.
        If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
            ' If so, check to see if it is a reset situation, and the current
            ' position is greater than zero.
            Dim relatedCM As CurrencyManager = sender
            If e.ListChangedType = ListChangedType.Reset _
                AndAlso relatedCM.Position > 0 Then
    
                ' If so, cache the position of the child table.
                cachedPosition = relatedCM.Position
            End If
        End If
    
    End Sub
    
  4. 부모 통화 관리자에 대한 부모 목록의 CurrentChanged 이벤트를 처리합니다. 처리기에서 캐싱 시나리오가 아님을 나타내는 부울 값을 설정합니다. CurrentChanged가 발생하는 경우 부모에 대한 변경 내용은 항목 값 변경이 아니라 목록 위치 변경입니다.

    void bindingSource1_CurrentChanged(object sender, EventArgs e)
    {
        // If the CurrentChanged event occurs, this is not a caching
        // situation.
        cacheChildPosition = false;
    }
    
    ' 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.
    Private Sub bindingSource1_CurrentChanged(ByVal sender As Object, _
        ByVal e As EventArgs) Handles bindingSource1.CurrentChanged
        ' If the CurrentChanged event occurs, this is not a caching 
        ' situation.
        cacheChildPosition = False
    
    End Sub
    

자식 위치를 다시 설정하려면

  1. 자식 바인딩의 CurrencyManager에 대한 PositionChanged 이벤트를 처리합니다.

  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;
            }
        }
    }
    
    Private Sub relatedCM_PositionChanged(ByVal sender As Object, ByVal e As EventArgs) 
        ' Check to see if this is a caching situation.
        If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
            Dim relatedCM As CurrencyManager = sender
            
            ' 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 AndAlso _
                cachedPosition > 0 AndAlso cachedPosition < _
                relatedCM.Count Then
                relatedCM.Position = cachedPosition
                cachedPosition = -1
            End If
        End If
    End Sub
    

예제

다음 예제에서는 CurrencyManager에서 자식 테이블의 현재 위치를 저장하고 부모 테이블에서 편집이 완료된 후 위치를 다시 설정하는 방법을 보여 줍니다. 이 예제에는 BindingSource 구성 요소를 사용하여 DataSet의 두 테이블에 바인딩된 두 개의 DataGridView 컨트롤이 포함되어 있습니다. 두 테이블 간에 관계가 설정되고 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
            // occurred 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());
        }
    }
}
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms



Public Class Form1
    Inherits Form
    
    Public Sub New() 
        InitializeControlsAndDataSource()
    
    End Sub
    
    ' Declare the controls to be used.
    Private WithEvents bindingSource1 As BindingSource
    Private dataGridView1 As DataGridView
    Private WithEvents button1 As Button
    Private dataGridView2 As DataGridView
    Private cachePositionCheckBox As CheckBox
    Public set1 As DataSet
    
    
    Private Sub InitializeControlsAndDataSource() 
        ' Initialize the controls and set location, size and 
        ' other basic properties.
        Me.dataGridView1 = New DataGridView()
        Me.bindingSource1 = New BindingSource()
        Me.button1 = New Button()
        Me.dataGridView2 = New DataGridView()
        Me.cachePositionCheckBox = New System.Windows.Forms.CheckBox()
        Me.dataGridView1.ColumnHeadersHeightSizeMode = _
            DataGridViewColumnHeadersHeightSizeMode.AutoSize
        Me.dataGridView1.Dock = DockStyle.Top
        Me.dataGridView1.Location = New Point(0, 20)
        Me.dataGridView1.Size = New Size(292, 170)
        Me.button1.Location = New System.Drawing.Point(18, 175)
        Me.button1.Size = New System.Drawing.Size(125, 23)
        
        button1.Text = "Clear Parent Field"

        Me.dataGridView2.ColumnHeadersHeightSizeMode = _
            System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
        Me.dataGridView2.Location = New System.Drawing.Point(0, 225)
        Me.dataGridView2.Size = New System.Drawing.Size(309, 130)
        Me.cachePositionCheckBox.AutoSize = True
        Me.cachePositionCheckBox.Checked = True
        Me.cachePositionCheckBox.Location = New System.Drawing.Point(150, 175)
        Me.cachePositionCheckBox.Name = "radioButton1"
        Me.cachePositionCheckBox.Size = New System.Drawing.Size(151, 17)
        Me.cachePositionCheckBox.Text = "Cache and restore position"
        Me.ClientSize = New System.Drawing.Size(325, 420)
        Me.Controls.Add(Me.dataGridView1)
        Me.Controls.Add(Me.cachePositionCheckBox)
        Me.Controls.Add(Me.dataGridView2)
        Me.Controls.Add(Me.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.
        Dim relatedCM As CurrencyManager = _
            bindingSource1.GetRelatedCurrencyManager("custOrders")

        ' Handle the two events for caching and resetting the position.
        AddHandler relatedCM.ListChanged, AddressOf relatedCM_ListChanged
        AddHandler relatedCM.PositionChanged, AddressOf relatedCM_PositionChanged
        
        ' Set the position in the child table for demonstration purposes.
        relatedCM.Position = 3

        ' Set cacheing to true in case current changed event
        ' occurred on set up.
        cacheChildPosition = True
      
    
    End Sub
    
    
    
    ' Establish the data set with two tables and a relationship
    ' between them.
    Private Function InitializeDataSet() As DataSet 
        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")
        
        Dim dr As New DataRelation("custOrders", _
            set1.Tables("Customers").Columns("CustomerID"), _
            set1.Tables("Orders").Columns("CustomerID"))
        set1.Relations.Add(dr)
        Return set1
    
    End Function '
    Private cachedPosition As Integer = - 1
    Private cacheChildPosition As Boolean = True
    
    Private Sub relatedCM_ListChanged(ByVal sender As Object, _
        ByVal e As ListChangedEventArgs)
        ' Check to see if this is a caching situation.
        If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
            ' If so, check to see if it is a reset situation, and the current
            ' position is greater than zero.
            Dim relatedCM As CurrencyManager = sender
            If e.ListChangedType = ListChangedType.Reset _
                AndAlso relatedCM.Position > 0 Then

                ' If so, cache the position of the child table.
                cachedPosition = relatedCM.Position
            End If
        End If

    End Sub

    ' 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.
    Private Sub bindingSource1_CurrentChanged(ByVal sender As Object, _
        ByVal e As EventArgs) Handles bindingSource1.CurrentChanged
        ' If the CurrentChanged event occurs, this is not a caching 
        ' situation.
        cacheChildPosition = False

    End Sub

    Private Sub relatedCM_PositionChanged(ByVal sender As Object, ByVal e As EventArgs) 
        ' Check to see if this is a caching situation.
        If cacheChildPosition AndAlso cachePositionCheckBox.Checked Then
            Dim relatedCM As CurrencyManager = sender
            
            ' 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 AndAlso _
                cachedPosition > 0 AndAlso cachedPosition < _
                relatedCM.Count Then
                relatedCM.Position = cachedPosition
                cachedPosition = -1
            End If
        End If
    End Sub

    Private count As Integer = 0
    
    Private Sub button1_Click(ByVal sender As Object, _
        ByVal e As EventArgs) Handles button1.Click
        ' For demo purposes--modifies a value in the first row of the
        ' parent table.
        Dim row1 As DataRow = set1.Tables(0).Rows(0)
        row1(1) = DBNull.Value
    End Sub
     
    <STAThread()>  _
    Shared Sub Main() 
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(New Form1())
    
    End Sub
End Class

코드 예제를 테스트하려면 다음 단계를 수행합니다.

  1. 예제를 실행합니다.

  2. Cache and reset position 확인란이 선택되었는지 확인합니다.

  3. Clear parent field 단추를 클릭하여 부모 테이블의 필드를 변경합니다. 자식 테이블에서 선택한 행은 변경되지 않습니다.

  4. 예제를 닫고 다시 실행합니다. 다시 설정 동작이 부모 행을 처음 변경할 때만 발생하기 때문에 이 작업을 수행해야 합니다.

  5. Cache and reset position 확인란을 선택 취소합니다.

  6. Clear parent field 단추를 클릭합니다. 자식 테이블에서 선택한 행이 첫 번째 행으로 변경됩니다.

코드 컴파일

이 예제에는 다음 사항이 필요합니다.

  • System, System.Data, System.Drawing, System.Windows.Forms and System.XML 어셈블리에 대한 참조

추가 정보