使用单个输入控件的主从解决方案
展示了一种使用文本框创建主从关系的解决方案。
引言
这是我在这里的第一篇文章,尽管我曾多次利用这个伟大网站上的丰富知识。所以请耐心阅读。
**这是一个免责声明,让大家知道无论我了解多少,我总是在学习!!!所以评论、提问、抱怨都会被愉快地接受!!!**
问题
本文旨在解决的问题是,使用单个输入控件(如 TextBox、DateTimePicker 等)创建数据的“主-从”视图,而不是使用多个输入控件(如 ComboBox 和 ListBox)。
这是我试图实现的一个快速预览。这是在当前显示的 DataGrid 中选择一行之前的程序状态:

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

基本上,该程序根据其合作对象和具体日期,从数据库中提取一份工作列表。当用户选择一个已安排的工作(那些没有标题的是程序为安排生成的空白工作,并且在主从关系中被忽略)时,左侧和下方 DataGrid 的 TextBox 会更新以显示选定的工作和客户信息。
这是用于填充 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 似乎只会导致我的代码崩溃。
解决方案
首先,我们设置一些初步工作,包括:
- 添加三个单独的 SqlDataAdapter(一个用于工作,一个用于客户信息,一个用于工作详情)。
- 生成一个强类型数据集(VS 自动化太棒了!)。
- 添加一个 DataView对象(用于排序目的)并将其链接到DataSet的 GetJobsByArtistAndDate 表。
- 添加一个 DataGrid控件,并将DataSource属性链接到DataView对象。
- 添加 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 的相应列来填充 GetClientDetailsByID 和 GetJobDetails 的 SelectCommand 参数。
((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 日 - 添加了历史记录部分。



