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

使用单个输入控件的主从解决方案

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.22/5 (3投票s)

2005年11月25日

CPOL

5分钟阅读

viewsIcon

50185

展示了一种使用文本框创建主从关系的解决方案。

引言

这是我在这里的第一篇文章,尽管我曾多次利用这个伟大网站上的丰富知识。所以请耐心阅读。

**这是一个免责声明,让大家知道无论我了解多少,我总是在学习!!!所以评论、提问、抱怨都会被愉快地接受!!!**

问题

本文旨在解决的问题是,使用单个输入控件(如 TextBoxDateTimePicker 等)创建数据的“主-从”视图,而不是使用多个输入控件(如 ComboBoxListBox)。

这是我试图实现的一个快速预览。这是在当前显示的 DataGrid 中选择一行之前的程序状态:

Sample image

这是在当前显示的 DataGrid 中选择一行之后的程序状态:

Sample image

基本上,该程序根据其合作对象和具体日期,从数据库中提取一份工作列表。当用户选择一个已安排的工作(那些没有标题的是程序为安排生成的空白工作,并且在主从关系中被忽略)时,左侧和下方 DataGridTextBox 会更新以显示选定的工作和客户信息。

这是用于填充 DataGrid 的简化存储过程:

GetJobByDateAndArtist (@ArtistID bigint, @Date datetime)
AS
SELECT 
Job.JobStartTime, Job.JobTitle, Job.ClientID 
FROM 
Job
WHERE 
Job.ArtistID = @ArtistID AND Jobs.JobDate = @Date

以下是用于填充 TextBox 的存储过程:

这是用于客户文本框的:

GetClientInfoByID(@ClientID bigint)
AS
SELECT
*
FROM
Clients
WHERE 
Clients.ClientID = @ClientID

这是用于工作详情文本框的:

GetAJobsDetails(@JobID bigint)
AS
SELECT
*
FROM
Job
WHERE 
Job.JobID = @JobID

到目前为止,我遇到的所有主从关系解决方案都涉及到将客户列在一个 datagrid 或列表控件中,然后有一个第二个 datagrid 显示选定客户的预约。它们通常只是在数据集中设置一个关系,然后程序就会处理其余的事情。

问题是,在我的程序中,工作是用户最想看到的项目,而用户只希望看到所选工作的客户详情。

为了最大限度地减少系统负载,程序被设计为一次只提取一个客户记录。

现在,尽管 Customers 表实际上是 Job 表的父表,但 Job datagrid 是程序的主要焦点,使用 Relationships 似乎只会导致我的代码崩溃。

解决方案

首先,我们设置一些初步工作,包括:

  1. 添加三个单独的 SqlDataAdapter(一个用于工作,一个用于客户信息,一个用于工作详情)。
  2. 生成一个强类型数据集(VS 自动化太棒了!)。
  3. 添加一个 DataView 对象(用于排序目的)并将其链接到 DataSet 的 GetJobsByArtistAndDate 表。
  4. 添加一个 DataGrid 控件,并将 DataSource 属性链接到 DataView 对象。
  5. 添加 TextBox 控件,并将其 DataBindings 链接到 DataSet 中 GetClientInfoByID 和 GetAJobDetails 表的相应列。

现在开始编写一些功能代码。

接下来,我使用 VS 为我的 Appointments DataGrid 创建了一个 MouseDown 事件处理程序。

这是完成后的方法的样子:

private void WeeklyDataGrids_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
    if (((DataGrid)sender).HitTest(e.X,e.Y).Row >= 0)
    {
        this.dataSet11.GetAJobDetails.Clear();
        this.dataSet11.GetClientInfoByID.Clear();
        if (((double)this.CurrentDailyDataView[((DataGrid)
                    sender).HitTest(e.X,e.Y).Row]["JobTotalHours"]) != 0)
        {
            this.GetClientByIDSqlDataAdapter.SelectCommand.Parameters["@ClientID"].Value =
                            this.CurrentDailyDataView[(
                            (DataGrid)sender).HitTest(e.X,e.Y).Row]["ClientID"].ToString();
            this.GetClientByIDSqlDataAdapter.Fill(this.dataSet11.GetClientInfoByID);
            this.GetJobDetailsSqlDataAdapter.SelectCommand.Parameters["@JobID"].Value = 
                            this.CurrentDailyDataView[(
                            (DataGrid)sender).HitTest(e.X,e.Y).Row]["JobID"].ToString();
            this.GetJobDetailsSqlDataAdapter.Fill(this.dataSet11.GetAJobDetails);        
        }
    }
}

详细说明

我想重点介绍这个方法的一些关键点。

if (((DataGrid)sender).HitTest(e.X,e.Y).Row >= 0)

第一行简单地测试以确保我们没有选择 DataGrid 的列标题。

this.dataSet11.GetAJobDetails.Clear();
this.dataSet11.GetClientInfoByID.Clear();

这两行是最重要的,因为 TextBox 控件和其他单个输入控件会自动绑定到 DataTable 的第一个可用记录。如果我们从不清除内存中的表,该方法将继续添加我们想要的记录,但仍然显示我们选择的第一条记录。

if (((double)this.CurrentDailyDataView[(
     (DataGrid)sender).HitTest(e.X,e.Y).Row]["JobTotalHours"]) != 0)

程序会自动使用未安排的工作槽填充 DataView 对象(链接到 DataSet 的 GetJobsByArtistAndDate 表)。如果我们刚刚选择了一个未安排的工作,返回的 JobID 将不会匹配数据库中的当前工作,并会导致代码崩溃。此“if”测试通过检查当前选定记录的 JobTotalHour 列是否不为零(换句话说,它是一项已安排的工作)来防止这种情况。

this.GetClientByIDSqlDataAdapter.SelectCommand.Parameters["@ClientID"].Value = 
   this.CurrentDailyDataView[((DataGrid)sender).HitTest(e.X,e.Y).Row]["ClientID"].ToString();

并且

this.GetJobDetailsSqlDataAdapter.SelectCommand.Parameters["@JobID"].Value = 
 this.CurrentDailyDataView[((DataGrid)sender).HitTest(e.X,e.Y).Row]["JobID"].ToString();

上面的两个代码片段都做了同样的事情。它们都使用 DataView 中对应于活动 DataGrid 的选定行的 DataRow 的相应列来填充 GetClientDetailsByIDGetJobDetailsSelectCommand 参数。

((DataGrid)sender)

这个小代码片段是我称之为“智能编码”的东西,它可以为您节省一些时间并减少您需要编写的代码量。基本上,它所做的就是将方法的 sender 参数强制转换为 DataGrid 对象,这使得我们可以访问 DataGrid 对象的任何属性(因为我们知道发送事件的是一个 DataGrid)。

这做了一件非常重要的事情,因为我的程序实际上包含 10 个不同的 DataGrid,代表两个人每周的工作日,并且每个工作日的工作都加载到每个 DataGrid 中。如果我使用

Person1MondayJobsDataGrid.HitTest(e.X,e.Y)

这对于 person 1 的周一工作的 DataGrid 将会正常工作,但是其他 9 个 DataGrid 都需要有自己的事件处理程序(那就是 54 行不必要的代码!!)。通过将 sender 变量强制转换为 DataGrid 类型,我可以为我需要此功能的每个 DataGrid 重用相同的事件处理程序。

结论

为了使用文本框或其他单个输入控件创建主从关系,需要一个单独的 DataTable,并且每次选择主 DataGrid 上的记录时,都必须清除 DataTable 并仅填充一条记录,这样文本框才能绑定到正确的记录。

历史

  • 2005 年 11 月 25 日 - 发布文章。
  • 2005 年 11 月 29 日 - 添加了屏幕截图。
  • 2005 年 11 月 29 日 - 修改了代码以使用 DataView 对象绑定到 DataGrid 控件。
  • 2005 年 11 月 29 日 - 将 DataGrid 控件捕获的事件从 CurrentCellChanged 事件更改为 MouseDown 事件(响应更快)。
  • 2005 年 11 月 29 日 - 修复了文章文本中的问题。
  • 2005 年 11 月 29 日 - 重新组织了每个部分,以便更好地解释问题、解决方案和细节。
  • 2005 年 11 月 29 日 - 删除了文章中任何不必要的内容(感谢 John)。
  • 2005 年 11 月 29 日 - 添加了历史记录部分。
© . All rights reserved.