RFID会议签到系统总结(十九)――单数据窗体
所谓的单数据窗体是指那种在窗体只显示一张单表的数据的窗体,主要就是用于显示基本表,因为这个系统要显示的差不多就是基本表为主,这一篇主要讲这个数据显示窗体的基类。
既然是基类嘛,当然要提供一些虚方法给子类来改写,要有一些变量必须由子量来初始化。首先必须由子量来提供实例的变量主要是跟子类关联的数据表有关,因为窗体的始化及UI的呈现都是与数据有关的,所以上述变量的初始化必须是在构造函数的最开始的地方进行。
public frmData()
{
InitVariable();
if (!LoadData())
return;
InitializeComponent();
toolBar1.ButtonClick += new ToolBarButtonClickEventHandler(toolBar1_ButtonClick);
}
/// <summary>
/// 初始化一些由子类提供的变量
/// </summary>
protected virtual void InitVariable()
{
}
/// <summary>
/// 加载数据
/// </summary>
/// <returns></returns>
protected virtual bool LoadData()
{
try
{
if (stMgr != null && stMgr.LastState == StateEnum.Insert) //新增一笔记录后要清除以前查询条件,否则会看不到新增的那一笔记录
condition.ClearCondition();
DBCommandWrapper command = condition.BuildCondidtionCommand();
dataTable = recordAction.GetAll(command);
if (dataTable.Rows.Count == 0)
Msg.ShowInformation("没有可显示的记录");
return true;
}
catch(Exception)
{
return true;
}
}
在子窗体的InitVariable方法里,根据当前窗体关联的数据表初始化对应的变量,这里其实主要就是初始化数据对象及跟它有关的一些东西(比如表示查询条件的类等等),比如“会议基本表”这个窗体
protected override void InitVariable()
{
record = new MeetInfoBO();
recordAction = new MeetInfoBO();
condition = recordAction.GetQueryCondition();
}
上面的record、recordAction是数据对象的基类,为什么要提供呢?因为考虑到数据显示与数据操作的问题。虽说是基本表,但有些还是有外键关联的,数据显示的时候我们并不是直接从表本身上取数据,一般会从这个表衍生的视图上取数据(可以提供外键的友好显示)。而新增、修改的时候,操作的还是那个基本表,所以分了二个数据对象(这里的会议表没什么外键,所以二者是一致的)。
缺省情况下,数据窗体是从数据表里取出所有的数据,当然在子窗体里可以根据不同情况来取,也可以根据条件排序。
数据窗体最为繁琐的地方就是工具栏,数据窗体的工具栏一般就是一些导航及数据操作按钮,但.Net Framework带的这个ToolBar实在做得是不算好。虽然上几篇化了点功夫改造了一下,但这里的工具栏是要在设计期用的,要在ToolBar的设计器中加入自定义类型的ToolBarButton要费点力气,想想就算了,反正用原始的工具栏也能解决问题,虽然看起来比较难看点。
先来看一下ToolBar的ButtonClick事件:
protected virtual void toolBar1_ButtonClick(object sender, ToolBarButtonClickEventArgs e)
{
currPos = curM.Position;
switch(e.Button.ToolTipText)
{
case "First":
grid.UnSelect(curM.Position);
curM.Position = 0;
grid.Select(curM.Position);
break;
case "Previous":
grid.UnSelect(curM.Position);
curM.Position = (curM.Position == 0) ? 0 : curM.Position - 1;
grid.Select(curM.Position);
break;
。。。。。。
。。。。。。
case "Add":
stMgr.SetInsert();
grid.Enabled = false;
BeforeInsert();
break;
。。。。。。
。。。。。。
case "OK":
if (!BeforePost()) return;
if (stMgr.CurrentState != StateEnum.Query)
stMgr.btnOKClick();
else
CombineCondition();
switch (stMgr.CurrentState)
{
case StateEnum.Insert:
AfterInsert();
break;
case StateEnum.Edit:
AfterEdit();
break;
case StateEnum.Query:
AfterQuery();
break;
}
LoadData();
grid.Enabled = true;
ReBind();
stMgr.SetBrowse();
grid.Refresh();
grid.Focus();
if (curM.Count > 0)
{
switch (stMgr.LastState)
{
case StateEnum.Insert:
currPos = curM.Count - 1;
break;
case StateEnum.Query:
currPos = 0;
break;
}
currPos = (currPos >= curM.Count) ? curM.Count - 1 : currPos;
grid.UnSelect(curM.Position);
grid.Select(currPos);
curM.Position = currPos;
}
break;
。。。。。。
。。。。。。
}
}
上面这个方法为什么要声明成virtual呢?这是为了让子窗体可以有机会改写而做一些自己的事情,其实最后在子窗体中也没做什么大的事情,只是重新初始化了一下record这个变量,这个步骤比较好的方法本应该是在方法的入口处放一个Place Holder来让子窗体写,不过因为最开始时这么写,后来就没改了。
第二个要说的地方就是那个switch后面跟的条件了,找来找去实在找不出什么属性可以用来分清工具栏的各个按钮,用index之类的属性显然是不合适的,而Tag我已经有新的用途了,最后选了个ToolTipText属性,好歹也是个字符串的属性,看代码也还算看得明白。
上面当然是省略了很多的分支,不过处理都是大同小异的,所以就不一一列出来了。主要的技巧是放了大量的Place Holder,主要是那些After***、Before***方法,其实那些方法在基类里基本是空的虚方法,即使有点代码也就二三行,这些方法主要用途就是开放接口让子窗体能够做一些特殊处理。
那工具栏状态是如何控制的,及数据操作(新增、修改、删除、查询)是如何进行的呢,从上面看不出来,其实所有的奥秘都在stMgr状态管理器里,不过这个篇幅比较长,要放在下篇讲了。
最后记一个不太扣这一篇主旨的东西――点击grid选择整行,这是从网上查来的。其实其他的东西当然也有很多是从网上来的,但那些东西都比较大,要经过消化,这个小东西没什么可消化的,直接在这里记一笔了。
在grid的MouseUp事件里写:
private void grid_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
System.Drawing.Point pt = new Point(e.X, e.Y);
DataGrid.HitTestInfo hti = grid.HitTest(pt);
if(hti.Type == DataGrid.HitTestType.Cell)
{
grid.CurrentCell = new DataGridCell(hti.Row, hti.Column);
grid.Select(hti.Row);
}
}