Gewusst wie: Sicherstellen, dass die ausgewählte Zeile in einer untergeordneten Tabelle an der richtigen Position verbleibt
Wenn Sie die Datenbindung in Windows Forms verwenden, zeigen Sie Daten häufig in einer hierarchischen oder Master-/Detailansicht an. Dies bezieht sich auf ein Datenbindungsszenario, in dem Daten aus derselben Quelle in zwei Steuerelementen angezeigt werden. Wenn die Auswahl in einem Steuerelement geändert wird, führt dies dazu, dass sich die im zweiten Steuerelement angezeigten Daten ändern. Das erste Steuerelement kann beispielsweise eine Liste mit Kunden und das zweite eine Liste mit Aufträgen enthalten, die sich auf den ausgewählten Kunden im ersten Steuerelement beziehen.
Zur Anzeige von Daten in einer hierarchischen Ansicht müssen Sie seit .NET Framework, Version 2.0, zusätzliche Schritte unternehmen, um sicherzustellen, dass die aktuell ausgewählte Zeile in der untergeordneten Tabelle nicht auf die erste Tabellenzeile zurückgesetzt wird. Dazu müssen Sie die Position der untergeordneten Tabelle zwischenspeichern und zurücksetzen, nachdem sich die übergeordnete Tabelle geändert hat. Das Zurücksetzen der untergeordneten Tabelle findet normalerweise erstmalig statt, sobald sich ein Feld in einer Zeile der übergeordneten Tabelle ändert.
So wird die aktuelle untergeordnete Position zwischengespeichert
Deklarieren Sie eine ganzzahlige Variable für das Speichern der untergeordneten Listenposition und eine boolesche Variable, der zu entnehmen ist, ob die untergeordnete Position zwischengespeichert werden soll.
Private cachedPosition As Integer = - 1 Private cacheChildPosition As Boolean = True
private int cachedPosition = -1; private bool cacheChildPosition = true;
Behandeln Sie das ListChanged-Ereignis für CurrencyManager der Bindung, und suchen Sie nach einem ListChangedType von Reset.
Überprüfen Sie die aktuelle Position von CurrencyManager. Wenn sie höher als der erste Eintrag in der Liste (normalerweise 0) ist, speichern Sie sie in einer Variablen.
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
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; } }
Behandeln Sie das CurrentChanged-Ereignis der übergeordneten Liste für den übergeordneten Währungs-Manager. Legen Sie den booleschen Wert im Handler fest, um anzuzeigen, dass es sich nicht um ein Zwischenspeicherungsszenario handelt. Wenn CurrentChanged auftritt, stellt der Wechsel zum übergeordneten Element eine Änderung der Listenposition und keine Änderung des Elementwerts dar.
' 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
void bindingSource1_CurrentChanged(object sender, EventArgs e) { // If the CurrentChanged event occurs, this is not a caching // situation. cacheChildPosition = false; }
So setzen Sie die untergeordnete Position zurück
Behandeln Sie das PositionChanged-Ereignis für CurrencyManager der untergeordneten Bindung.
Setzen Sie die Position der untergeordneten Tabelle auf die zwischengespeicherte Position zurück, die in der vorherigen Prozedur gespeichert wurde.
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
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; } } }
Beispiel
Im folgenden Beispiel wird veranschaulicht, wie die aktuelle Position im CurrencyManager für eine untergeordnete Tabelle gespeichert und die Position nach Ende der Bearbeitung in der übergeordneten Tabelle zurückgesetzt wird. Dieses Beispiel enthält zwei DataGridView-Steuerelemente, die mit einer BindingSource-Komponente an zwei Tabellen in einem DataSet gebunden werden. Eine Beziehung zwischen den beiden Tabellen wird hergestellt und dem DataSet hinzugefügt. Die Position in der untergeordneten Tabelle ist anfänglich zu Demonstrationszwecken auf die dritte Zeile festgelegt.
Imports System
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
' occured on set up.
cacheChildPosition = True
End Sub 'InitializeControlsAndDataSource
' 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
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());
}
}
}
Um das Codebeispiel zu testen, führen Sie die folgenden Schritte aus:
Führen Sie das Beispiel aus.
Stellen Sie sicher, dass das Kontrollkästchen Position zwischenspeichern und zurücksetzen aktiviert ist.
Klicken Sie auf die Schaltfläche Übergeordnetes Feld löschen, um eine Änderung in einem Feld der übergeordneten Tabelle zu bewirken. Beachten Sie, dass sich die ausgewählte Zeile in der untergeordneten Tabelle nicht ändert.
Schließen Sie das Beispiel, und führen Sie es erneut aus. Dieser Schritt ist wichtig, da das Rücksetzungsverhalten nur bei der ersten Änderung in der übergeordneten Zeile auftritt.
Deaktivieren Sie das Kontrollkästchen Position zwischenspeichern und zurücksetzen.
Klicken Sie auf die Schaltfläche Übergeordnetes Feld löschen. Beachten Sie, dass die ausgewählte Zeile in der untergeordneten Tabelle in die erste Zeile geändert wird.
Kompilieren des Codes
Dieses Beispiel setzt Folgendes voraus:
- Verweise auf die Assemblys System, System.Data, System.Drawing, System.Windows.Forms und System.XML.
Informationen zum Erstellen dieses Beispiels über die Befehlszeile für Visual Basic oder Visual C# finden Sie unter Erstellen von der Befehlszeile aus (Visual Basic) oder Erstellen über die Befehlszeile mit csc.exe. Sie können dieses Beispiel auch in Visual Studio erstellen, indem Sie den Code in ein neues Projekt einfügen. Weitere Informationen finden Sie unter Gewusst wie: Kompilieren und Ausführen eines vollständigen Windows Forms-Codebeispiels mit Visual Studio und Gewusst wie: Kompilieren und Ausführen eines vollständigen Windows Forms-Codebeispiels mit Visual Studio und Gewusst wie: Kompilieren und Ausführen eines vollständigen Windows Forms-Codebeispiels mit Visual Studio und Gewusst wie: Kompilieren und Ausführen eines vollständigen Windows Forms-Codebeispiels mit Visual Studio und Gewusst wie: Kompilieren und Ausführen eines vollständigen Windows Forms-Codebeispiels mit Visual Studio.
Siehe auch
Aufgaben
Konzepte
Datenbindung und Windows Forms