65.9K
CodeProject 正在变化。 阅读更多。
Home

DataGridView 添加、编辑和删除(带分页)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (40投票s)

2007年1月25日

CPOL

4分钟阅读

viewsIcon

483333

downloadIcon

29073

本文介绍了在 DataGridView 中进行添加、编辑和删除操作,并结合分页和异步方法调用。

Sample Image - DataGridView_manipulation.png

引言

本文(或者说代码片段)演示了一个简单的应用程序,使用 DataGridView 进行插入、更新和删除操作。该应用程序对大部分数据库调用使用了异步架构,目的是在不阻塞 UI 的情况下,允许用户继续执行其他任务。该应用程序的结构如下:

  1. 从网络中获取所有 SQL Server 实例。
  2. 从选定的实例中获取所有数据库。
  3. 如果用户提供的用户名或密码为空,或者用户名或密码不正确,则会返回相同的 SQL Server 实例列表。代码可在此处找到[^]。

  4. 从选定的数据库中获取所有表。
  5. 从选定的表中获取所有记录。
  6. DataGridView 中添加、编辑、删除记录。
  7. 同时提供了分页功能,并可设置每页记录数。

异步架构

该应用程序使用异步调用机制进行数据库调用。由于 SQLEnumerator 不支持异步调用,我添加了委托(delegates)来异步调用这些方法。在 .NET 2.0 中,SqlCommand 对象支持对数据库的异步调用。我正在使用此功能从选定的数据库中获取所有表。委托是设计异步架构的最佳实践,因为 CLR 会在内部处理大部分事务,我们不必过多担心。

应用程序结构

当我们开始从头阅读代码时,可以看到一个名为 CallForenum。当调用 SQL Server 列表、数据库和表时,它用于设置一个 private 变量 called。这样做是因为只有一个回调方法处理来自异步调用的所有回调。一个 switch case 语句管理不同回调的行为。我使用 SqlCommandBuilder 设计了这个应用程序,因为我对如何使用 SqlCommandBuilder 有一些疑问。只要在你的 Select 查询中选择了主键,构建器就会自动生成插入、更新和删除命令。我遇到了一个非常常见的跨线程异常问题。但在我之前的项目中,我们实现了相同的架构,.NET 2.0 提供了 InvokeRequiredInvoke() 函数来克服这个问题。我们需要声明一个与回调方法具有相似签名的委托,并通过控件的 Invoke() 方法调用相同的方法。这将把调用堆栈从执行线程推到父线程,让父线程去处理。你需要遵循这样的顺序:

  1. 获取所有 SQL Server 实例。
  2. 从选定的实例中获取所有数据库。
  3. 从选定的数据库中获取所有表。
  4. 加载选定表的数据。
  5. 使用分页进行导航。
  6. 添加“添加/更新”、“提交”、“删除”等按钮来插入/更新或删除。您可以删除/更新/插入多条记录。

这里有一些代码块我将进行解释。这个函数设置了应用程序所需的数据库对象。sqlQuery 是动态构建的。

private void SetDataObjects()
{
    connection = new SqlConnection(connectionString);
    command = new SqlCommand(sqlQuery, connection);
    adapter = new SqlDataAdapter(command);
    builder = new SqlCommandBuilder(adapter);
    ds = new DataSet("MainDataSet");
    tempDataSet = new DataSet("TempDataSet");
}

下面的两个函数加载数据并将其绑定到 DataGridView。我曾尝试将一个临时的 DataTable 对象绑定到 DataGridView,然后再更新主表。但我未能做到。我使用了适配器的 Fill() 方法,该方法将起始记录和记录数作为输入参数与 DataSet 一起使用。我创建了一个临时的 DataSet 来获取总记录数。然后立即将其释放。我更喜欢手动添加列,然后让行绑定到这些列,而不是直接将数据源绑定到 DataGridView。这允许排序,并且在不希望显示某些列的情况下,可以在这里进行处理。

private void btnLoad_Click(object sender, EventArgs e)
{
    lblLoadedTable.Text = "Loading data from table " + cmbTables.Text.Trim();
    btnLoad.Enabled = false;
    this.Cursor = Cursors.WaitCursor;
    try
    {
        if (userTable != null)
        {
            userTable.Clear();
        }
        userDataGridView.DataSource = null;
        userDataGridView.Rows.Clear();
        userDataGridView.Refresh();
        sqlQuery = "SELECT * FROM [" + cmbTables.Text.Trim() + "]";
        SetDataObjects();
        connection.Open();
        ticker.Start();
        adapter.Fill(tempDataSet);
        totalRecords = tempDataSet.Tables[0].Rows.Count;
        tempDataSet.Clear();
        tempDataSet.Dispose();
        adapter.Fill(ds, 0, 5, cmbTables.Text.Trim());
        userTable = ds.Tables[cmbTables.Text.Trim()];
                        
        foreach (DataColumn dc in userTable.Columns)
        {
            DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
            column.DataPropertyName = dc.ColumnName;
            column.HeaderText = dc.ColumnName;
            column.Name = dc.ColumnName;
            column.SortMode = DataGridViewColumnSortMode.Automatic;
            column.ValueType = dc.DataType;
            userDataGridView.Columns.Add(column);
        }
        lblLoadedTable.Text = "Data loaded from table: " + userTable.TableName;
        lblTotRecords.Text = "Total records: " + totalRecords;
        CreateTempTable(0, int.Parse(cmbNoOfRecords.Text.Trim()));

        btnPrevious.Enabled = true;
        btnFirst.Enabled = true;
        btnPrevious.Enabled = true;
        btnNext.Enabled = true;
        btnLast.Enabled = true;
        btnAdd.Enabled = true;
        btnUpdate.Enabled = true;
        btnDelete.Enabled = true;
        cmbNoOfRecords.Enabled = true;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        connection.Close();
        btnLoad.Enabled = true;
        this.Cursor = Cursors.Default;
        prgProgress.Value = 0;
        prgProgress.Update();
        prgProgress.Refresh();
        ticker.Stop();
    }
}

此方法实际上是将数据绑定到 DataGridView,并为所有分页函数调用。根据当前索引等信息,获取所需的记录数。

private void CreateTempTable(int startRecord, int noOfRecords)
{   
    if (startRecord == 0 || startRecord < 0)
    {
        btnPrevious.Enabled = false;
        startRecord = 0;
    }
    int endRecord = startRecord + noOfRecords;
    if (endRecord >= totalRecords)
    {
        btnNext.Enabled = false;
        isLastPage = true;
        endRecord = totalRecords;
    }
    currentPageStartRecord = startRecord;
    currentPageEndRecord = endRecord;
    lblPageNums.Text = "Records from " + startRecord + " to " 
        + endRecord+ " of " + totalRecords;
    currentIndex = endRecord;

    try
    {
        userTable.Rows.Clear();
        if (connection.State == ConnectionState.Closed)
        {
            connection.Open();
        }
        adapter.Fill(ds, startRecord, noOfRecords, cmbTables.Text.Trim());
        userTable = ds.Tables[cmbTables.Text.Trim()];
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        connection.Close();
    }

    userDataGridView.DataSource = userTable.DefaultView;
    userDataGridView.AllowUserToResizeColumns = true;
}

这是一个获取 SQL Server 实例的异步方法的示例。在 BeginInvoke() 之后,用户可以自由执行任何任务。回调函数将捕获响应。这就是您可以使用委托异步调用方法的方式。

private void btnLoadSqlServers_Click(object sender, EventArgs e)
{
    ticker.Start();
    btnLoadSqlServers.Enabled = false;
    this.Cursor = Cursors.WaitCursor;
    cmbSqlServers.Items.Clear();
    called = CallFor.SqlServerList;
    intlDelg.BeginInvoke(new AsyncCallback(CallBackMethod), intlDelg);
}

这是将处理来自不同方法的各种回调的回调方法。当您使用异步架构时,回调总是在一个与父线程不同的新线程上进行。这个新线程无法访问父线程上的控件。因此,我们需要将调用堆栈转移到父线程,这可以通过 control.InvokeRequiredcontrol.Invoke() 来实现。以下方法展示了如何做到这一点。

private void CallBackMethod(IAsyncResult result)
{  
    if (this.InvokeRequired)
    {
        this.Invoke(new AsyncDelegate(CallBackMethod), result);
    }
    else
    {
        try
        {
            prgProgress.Value = prgProgress.Maximum;
            switch (called)
            {
                case CallFor.SqlServerList:
                    string[] sqlServers = intlDelg.EndInvoke(result);
                    cmbSqlServers.Items.AddRange(sqlServers);
                    if (cmbSqlServers.Items.Count > 0)
                    {
                        cmbSqlServers.Sorted = true;
                        cmbSqlServers.SelectedIndex = 0;
                    }
                    this.Cursor = Cursors.Default;
                    btnLoadSqlServers.Enabled = true;
                    txtUserName.Select();
                    txtUserName.Focus();
                    break;
                case CallFor.SqlDataBases:
                    string[] sqlDatabases = intlDelg.EndInvoke(result);
                    cmbAllDataBases.Items.AddRange(sqlDatabases);
                    if (cmbAllDataBases.Items.Count > 0)
                    {
                        cmbAllDataBases.Sorted = true;
                        cmbAllDataBases.SelectedIndex = 0;
                    }
                    this.Cursor = Cursors.Default;
                    btnGetAllDataBases.Enabled = true;
                    break;
                case CallFor.SqlTables:
                    reader = command.EndExecuteReader(result);
                    cmbTables.Items.Clear();
                    while (reader.Read())
                    {
                        cmbTables.Items.Add(reader[0].ToString());
                    }
                    if (cmbTables.Items.Count > 0)
                    {
                        cmbTables.Sorted = true;
                        cmbTables.SelectedIndex = 0;
                        grpDataManipulate.Enabled = true;
                    }
                    else
                    {
                        grpDataManipulate.Enabled = false;
                    }
                    break;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
        finally
        {
            if (called == CallFor.SqlTables)
            {
                btnGetAllTables.Enabled = true;
                this.Cursor = Cursors.Default;
            }
            prgProgress.Value = 0;
            prgProgress.Refresh();
            ticker.Stop();
        }
    }
}

结论

这样,我们就可以轻松地操作 DataGridView。唯一需要注意的是,我们必须使用直接绑定到 DataGridView 的数据源。在加载大量数据时,异步调用会非常高效。如果您有任何疑问,请告诉我。

© . All rights reserved.