2023年2月

本人做Winform开发多年,孜孜不倦,略有小成,其中收集或者自己开发一些常用的东西,基本上在各个项目都能用到的一些开发经验及知识积累,现逐步介绍一些,以飨读者,共同进步。

1、窗口【×】关闭按钮变为最小化,并在托盘提示信息

一般有些管理系统,为了防止客户随意关闭程序或者基于其他原因,一般会把 窗口【×】关闭按钮变为最小化,如大家熟悉的飞信、MSN等等,但是有些不是很熟悉的客户,最小化到托盘的时候,却不知道程序到了那里去了,因此,最小化的时候,伴随一个气泡提示信息,显得有一定的必要,如下截图所示。

首先在主窗体的设计界面中添加一个NotifyIcon控件,然后实现相关的代码即可。

下面列出一些关键的代码出来,大家看了应该就知道如何实现了


private

void
notifyMenu_Show_Click(
object
sender, EventArgs e)

{

if
(
this
.WindowState
==
FormWindowState.Minimized)
{

this
.WindowState
=
FormWindowState.Maximized;

this
.Show();

this
.BringToFront();

this
.Activate();

this
.Focus();
}

else

{

this
.WindowState
=
FormWindowState.Minimized;

this
.Hide();
}
}


private

void
notifyMenu_Exit_Click(
object
sender, EventArgs e)
{

try

{

this
.ShowInTaskbar
=

false
;
Portal.gc.Quit();
}

catch

{

//
Nothing to do.


}
}


private

void
notifyIcon1_MouseDoubleClick(
object
sender, MouseEventArgs e)
{
notifyMenu_Show_Click(sender, e);
}


private

void
MainForm_MaximizedBoundsChanged(
object
sender, EventArgs e)
{

this
.Hide();
}


///

<summary>


///
缩小到托盘中,不退出

///

</summary>



private

void
MainForm_FormClosing(
object
sender, FormClosingEventArgs e)
{



//
如果我们操作【×】按钮,那么不关闭程序而是缩小化到托盘,并提示用户.



if
(
this
.WindowState
!=
FormWindowState.Minimized)
{
e.Cancel

=

true
;
//
不关闭程序


//
最小化到托盘的时候显示图标提示信息,提示用户并未关闭程序



this
.WindowState
=
FormWindowState.Minimized;
notifyIcon1.ShowBalloonTip(

3000
,
"
程序最小化提示
"
,

"
图标已经缩小到托盘,打开窗口请双击图标即可。
"
,
ToolTipIcon.Info);
}
}


private

void
MainForm_Move(
object
sender, EventArgs e)
{

if
(
this

==

null
)
{

return
;
}


//
最小化到托盘的时候显示图标提示信息



if
(
this
.WindowState
==
FormWindowState.Minimized)
{

this
.Hide();
notifyIcon1.ShowBalloonTip(

3000
,
"
程序最小化提示
"
,

"
图标已经缩小到托盘,打开窗口请双击图标即可。
"
,
ToolTipIcon.Info);
}
}


2、只允许允许一个程序实例,即使是通过虚拟桌面方式连接过来的,也是只允许一个人运行。

这个已经封装好代码了,只需要在Main函数里面调用一下函数即可,允许多个实例会出现下面的对话框提示信息,提示不允许多实例运行,如下所示:

代码如下所示。


///

<summary>


///
应用程序的主入口点。

///

</summary>


[STAThread]

private

static

void
Main()
{
GlobalMutex();

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(

false
);


//
******启动代码**********


}


private

static
Mutex mutex
=

null
;

private

static

void
GlobalMutex()
{

//
是否第一次创建mutex



bool
newMutexCreated
=

false
;

string
mutexName
=

"
Global\\
"

+

"
WareHouseMis
"
;//系统名称,Global为全局,表示即使通过通过虚拟桌面连接过来,也只是允许运行一次

try

{
mutex

=

new
Mutex(
false
, mutexName,
out
newMutexCreated);
}

catch
(Exception ex)
{
Console.Write(ex.Message);
System.Threading.Thread.Sleep(

1000
);
Environment.Exit(

1
);
}


//
第一次创建mutex



if
(newMutexCreated)
{
Console.WriteLine(

"
程序已启动
"
);

//
todo:此处为要执行的任务


}

else

{
MessageUtil.ShowTips(

"
另一个窗口已在运行,不能重复运行。
"
);
System.Threading.Thread.Sleep(

1000
);
Environment.Exit(

1
);
//
退出程序


}

}

3、使用NotifyWindow给用户提示信息

可以通过NotifyWindow类(最后附件中有),做一些信息的提示,方便用户了解一些重要信息的提示,界面较为友好,如下所示:

提示信息的代码使用如下:


///

<summary>


///
弹出提示消息窗口

///

</summary>



public

void
Notify(
string
caption,
string
content)
{
Notify(caption, content,

400
,
200
,
5000
);
}


///

<summary>


///
弹出提示消息窗口

///

</summary>



public

void
Notify(
string
caption,
string
content,
int
width,
int
height,
int
waitTime)
{
NotifyWindow notifyWindow

=

new
NotifyWindow(caption, content);
notifyWindow.TitleClicked

+=

new
System.EventHandler(notifyWindowClick);
notifyWindow.TextClicked

+=

new
EventHandler(notifyWindowClick);
notifyWindow.SetDimensions(width, height);
notifyWindow.WaitTime

=
waitTime;
notifyWindow.Notify();
}


private

void
notifyWindowClick(
object
sender, EventArgs e)
{

//
SystemMessageInfo info = BLLFactory<SystemMessage>.Instance.FindLast();

//
if (info != null)

//
{

//

//
FrmEditMessage dlg = new FrmEditMessage();

//

//
dlg.ID = info.ID;

//

//
dlg.ShowDialog();

//
}

}

4、使用SearchCondion控件,简化查询条件的转化

不管在Winform或者在WebForm中,查询构造条件总是非常繁琐的事情,使用该控件能有效简化代码,提高操作的准确及方便行,这个控件我完成了几年了,一直伴随我处理各种查询操作。


private

string
GetConditionSql()
{
SearchCondition condition

=

new
SearchCondition();
condition.AddCondition(

"
ItemName
"
,
this
.txtName.Text, SqlOperator.Like)
.AddCondition(

"
ItemBigType
"
,
this
.txtBigType.Text, SqlOperator.Like)
.AddCondition(

"
ItemType
"
,
this
.txtItemType.Text, SqlOperator.Like)
.AddCondition(

"
Specification
"
,
this
.cmbSpecNumber.Text, SqlOperator.Like)
.AddCondition(

"
MapNo
"
,
this
.txtMapNo.Text, SqlOperator.Like)
.AddCondition(

"
Material
"
,
this
.txtMaterial.Text, SqlOperator.Like)
.AddCondition(

"
Source
"
,
this
.txtSource.Text, SqlOperator.Like)
.AddCondition(

"
Note
"
,
this
.txtNote.Text, SqlOperator.Like)
.AddCondition(

"
Manufacture
"
,
this
.txtManufacture.Text, SqlOperator.Like)
.AddCondition(

"
ItemNo
"
,
this
.txtItemNo.Text, SqlOperator.LikeStartAt);

string

where

=
condition.BuildConditionSql().Replace(
"
Where
"
,
""
);


return

where
;
}

可以构造条件后,传入查询函数,实现数据的查询。


string

where

=
GetConditionSql();
List

<
ItemDetailInfo
>
list
=
BLLFactory
<
ItemDetail
>
.Instance.Find(
where
,
this
.winGridViewPager1.PagerInfo);

this
.winGridViewPager1.DataSource
=

new
WHC.Pager.WinControl.SortableBindingList
<
ItemDetailInfo
>
(list);

this
.winGridViewPager1.PrintTitle
=
Portal.gc.gAppUnit
+

"
--
"

+

"
备件信息报表
"
;


最后呈上代码用到的一些类库及控件:
https://files.cnblogs.com/wuhuacong/WinformTips.rar

深田之星备件仓库管理系统2011


鼠标单击可查看大图



文件大小

5,500 KB
下载地址

文件大小:5MB

在线帮助:



产品说明如下:

★软件功能
深田之星备件仓库管理系统2011,是一款界面友好,方便易用的仓库管理系统,集备件信息管理、备件入库、备件出库、库存查询、库房管理、业务报表、权限管理、数据字典管理、备件及库存导入等功能于一体,系统界面优美大方,操作直观简单。软件覆盖整个仓库业务管理的方方面面,并具有丰富、强大的业务报表功能模块;软件操作具有严格的权限分配,操作数据更加放心。

★系统需求

深田之星备件仓库管理系统2011 使用C#语言开发 适运行在 Microsoft WindowsNT/2000/XP/2003 等平台,但必须安装有.Net2.0平台和SqlServer数据库.
该软件利用了微软.NET Framework2.0 优秀的框架和微软SQLServer数据库高性能的数据处理能力,因此在安装软件前,您需要花费一点时间来安装下面的组件(请您按照顺序安装即可):

(1)MicroSoft .NET Framework 2.0 官方下载地址:
http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=zh-cn&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe

(2)然后下载 深田之星备件仓库管理系统2011,进行安装即完成整个软件的安装。安装地址为:
http://www.iqidi.com/Download/WareHouseMisSetup.rar

(3)最后安装MS SQLServer数据库,然后从上面的安装目录中(一般默认为“C:\Program Files\广州爱启迪技术有限公司\备件仓库管理系统”)找到WareHouse.bak数据库备份文件,还原数据库为WareHouse,然后请修改安装目录下的数据库连接密码即可进行操作。

★软件注册

深田之星备件仓库管理系统2011是一款共享软件,您可以自由发布和传播软件,未注册版本有些功能受限制,并且在使用
30
天后过期。在带给您方便的同时, 希望您能考虑注册这款软件。

注册用户可以获得我们的技术支持以及其他服务,并且可以免费升级到今后所有的新版本,请务必保留第一次注册时使用机器码及电子邮件,免费升级下一版本时,我们需求此信息。更重要的是:您的注册还将鼓励我们不断改进和完善这个软件,给大家提供更好的服务。

如果您在电脑报刊发表了介绍这个软件的文章,将刊物名称和期号告诉我,我们将免费为您提供注册码。.或者您有好的建议/意见/程序错误,请写信告诉我们,如果我们采纳,也将免费为您提供注册码。

深田之星备件仓库管理系统2011
注意:此注册方式一个序列号对应一台机器,且根据管理终端的点数进行收费,每个点数的价格请来电或者邮件咨询。
该软件版权归伍华聪所有,作者保留该软件所有权利,未注册用户不得将该软件用于商业用途。

注册本的软件,您可以通过以下方法:



转帐或银行汇款注意事项

1. 请汇款或者转帐后,及时联系我们。可以使用邮件wuhuacong@163.com, 6966254@qq.com进行联系。
2. 银行汇款请先联系我们确认具体的汇款地址,避免耽误您的时间。
3. 建议使用支付宝进行支付,安全快捷且免手续费。





顶层
帐 号:9558 8236 0200 8692 135
户 名:伍华聪
地 址:广东省广州市

顶层
帐 号:wuhuacong@hotmail.com
户 名:伍华聪
地 址:广东省广州市

最近做了一个备件仓库管理软件,虽然只是一个不太复杂的仓库管理业务的软件,附带产出一些相关的报表,而且有之前做过的送水管理系统、酒店管理系统等软件的基础及技术储备,不过做起来发现还是有很多细节及新的东西,在客户不断提出修改意见以及改进建议的同时,逐步吸收优化新的知识,现大概总结一些相关的开发心得,以飨读者。

首先来看看整个软件的主体界面,如下所示,软件的功能主要集备件信息管理、备件入库、备件出库、库存查询、库房管理、业务报表、权限管理、数据字典管理、备件及库存导入等功能于一体。

鼠标单击可查看大图


现大致就主体界面介绍一些相关的知识:

1、使用OutLook样式的工具栏,提高界面的美观及易用性,这个在我的早期文章中已经有介绍,该控件集成美观的图标,可能很好提高界面的友好性及可操作性,可以令界面增色不少,详细请查看《
WinForm界面开发之“OutLookBar”工具条
》 在此不再赘述,只是介绍下并略为带过即可。

2、使用“WeifenLuo.WinFormsUI.Docking”控件来设计多文档界面效果,可以方便多个界面进行操作,这个控件也有相关的文章进行介绍使用了,请参考《
WinForm界面开发之布局控件"WeifenLuo.WinFormsUI.Docking"的使用
》 和《
再谈布局控件"WeifenLuo.WinFormsUI.Docking"的使用--如何控制自动停靠窗口的大小
》。

3、 尽可能使用已经在共享软件中广泛性使用我自己的分页控件,该分页控件集成了数据分页、内容提示、数据打印、数据导出、表头中文转义等很多功能,由于集成性很好,省却很多功夫,专注客户的业务及变化即可,否则一项表头的中文转换就够呛,还不说数据的分页,由于整合性、一致性、稳定性等特点,客户使用感觉比较好。最新的版本整合了优秀的Aspose.Cell控件来进行Excel数据的导出,速度非常快,而且默认表头冻结,非常方便。



4、 使用Apose.Cell控件的强大功能,实现自定义模板报表的定制导出。

使用普通的二维表,虽然能满足大多数的情况,不过在一般的业务中,自定义模板的报表根据贴近实际,符合客户的要求,虽然自定义模板的报表,比普通的二维报表复杂一些,不过利用Apose.Cell控件,并在预设模板中预设变量,可以生成很复杂的报表。详细历程可以参考我介绍Apose.Cell控件的使用文章,《
使用Aspose.Cell控件实现Excel高难度报表的生成(一)
》、《
使用Aspose.Cell控件实现Excel高难度报表的生成(二)
》。利用Apose.Cell可以生成下面几类自定义模板的报表:

其他
设计模板如下所示:


实际生成的报表如下所示:


5、利用现成独立的数据字典模块代码。由于一般复杂一点的系统,都需要有数据字典的模块,由于我在较早已经在这块已经做了一些开发,因此直接拿过来使用即可,该数据字典模块功能相对比较独立,因此是一个非常使用的模块,数据通过字典排序可以实现合理的排序,支持无限多级字典分类。

实际使用的时候,也是非常方便,首先我们封装一下获取字典项目的方法如下所示:


///

<summary>


///
根据字典类型获取对应的CListItem集合

///

</summary>


///

<param name="dictTypeName"></param>


///

<returns></returns>



public

static
CListItem[] GetDictByDictType(
string
dictTypeName)
{
List

<
CListItem
>
itemList
=

new
List
<
CListItem
>
();
Dictionary

<
string
,
string
>
dict
=
BLLFactory
<
DictData
>
.Instance.GetDictByDictType(dictTypeName);

foreach
(
string
key
in
dict.Keys)
{
itemList.Add(

new
CListItem(key, dict[key]));
}

return
itemList.ToArray();
}

然后再窗体初始化的时候,添加字典的初始化代码即可,如下所示:


private

void
InitDictItem()
{

this
.txtManufacture.Items.Clear();

this
.txtManufacture.Items.AddRange(DictItemUtil.GetDictByDictType(
"
供货商
"
));


this
.txtBigType.Items.Clear();

this
.txtBigType.Items.AddRange(DictItemUtil.GetDictByDictType(
"
备件属类
"
));


this
.txtItemType.Items.Clear();

this
.txtItemType.Items.AddRange(DictItemUtil.GetDictByDictType(
"
备件类别
"
));


this
.txtUnit.Items.Clear();

this
.txtUnit.Items.AddRange(DictItemUtil.GetDictByDictType(
"
单位
"
));


this
.txtSource.Items.Clear();

this
.txtSource.Items.AddRange(DictItemUtil.GetDictByDictType(
"
来源
"
));


this
.txtUsagePos.Items.Clear();

this
.txtUsagePos.Items.AddRange(DictItemUtil.GetDictByDictType(
"
使用位置
"
));

this
.txtUsagePos.SelectedIndex
=

0
;


this
.txtBelongDept.Items.Clear();

this
.txtBelongDept.Items.AddRange(DictItemUtil.GetDictByDictType(
"
部门
"
));


this
.txtBelongWareHouse.Items.Clear();

this
.txtBelongWareHouse.Items.AddRange(Portal.gc.ManagedWareHouse.ToArray());

this
.txtBelongWareHouse.SelectedIndex
=

0
;
}

通过数据字典大类无限级分类以及对字典内容的管理,基本上可以满足绝大多数的需要。

6、使用独立又具整合性的权限管理系统,既相互独立,有相互整合,方便重用,又不需重新开发,非常方便、更提高效率。 由于权限系统精简而又能满足日常绝大多数的需要,不会复杂的难于管理,而且也是基于角色的授权访问机制(RBAC),最重要是非常适合软件的整合使用。




用户角色功能维护界面如下:

编辑角色对应的权限界面如下:

如果我们在开发的系统中要集成现有的权限系统,操作代码如下所示:


///

<summary>


///
从数据库中列出相关用户

///

</summary>



private

void
InitLoginName()
{
User userBLL

=

new
User();
List

<
UserInfo
>
userList
=
userBLL.GetAll();

this
.cmbzhanhao.Items.Clear();

foreach
(UserInfo info
in
userList)
{

this
.cmbzhanhao.Items.Add(info.Name);
}
}


登录的时候,只需要把该客户能操作的功能点放到内存列表中,如下所示:


string
loginName
=

this
.cmbzhanhao.Text.Trim();
User userBLL

=

new
User();

string
identity
=
userBLL.VerifyUser(loginName,
this
.tbPass.Text, Guid.NewGuid().ToString());

if
(
!
string
.IsNullOrEmpty(identity))
{
UserInfo info

=
userBLL.GetUserByName(loginName);


#region
获取用户的功能列表


Function functionBLL

=

new
Function();
List

<
FunctionInfo
>
list
=
functionBLL.GetFunctionsByUser(info.ID,
"
WareMis
"
);

if
(list
!=

null

&&
list.Count
>

0
)
{

foreach
(FunctionInfo functionInfo
in
list)
{

if
(
!
Portal.gc.FunctionDict.ContainsKey(functionInfo.ControlID))
{
Portal.gc.FunctionDict.Add(functionInfo.ControlID, functionInfo);
}
}
}


#endregion

}


用户对某个功能点授权判断,只需要判断某功能点是否在已有集合中即可,如下所示:


///

<summary>


///
根据权限屏蔽功能

///

</summary>



private

void
InitAuthorizedUI()
{

this
.tool_Report.Enabled
=
Portal.gc.HasFunction(
"
Report
"
);

this
.tool_Dict.Enabled
=
Portal.gc.HasFunction(
"
Dictionary
"
);

this
.tool_ItemDetail.Enabled
=
Portal.gc.HasFunction(
"
ItemDetail
"
);

this
.tool_Purchase.Enabled
=
Portal.gc.HasFunction(
"
Purchase
"
);

this
.tool_StockSearch.Enabled
=
Portal.gc.HasFunction(
"
StockSearch
"
);

this
.tool_TakeOut.Enabled
=
Portal.gc.HasFunction(
"
TakeOut
"
);


this
.menu_WareHouse.Enabled
=
Portal.gc.HasFunction(
"
WareHouse
"
);

this
.menu_Dictionary.Enabled
=
Portal.gc.HasFunction(
"
Dictionary
"
);

this
.menu_run_systemLog.Enabled
=
Portal.gc.HasFunction(
"
LoginLog
"
);

this
.menu_Parameters.Enabled
=
Portal.gc.HasFunction(
"
Parameters
"
);

this
.menu_MonthlyStatistic.Enabled
=
Portal.gc.HasFunction(
"
MonthlyStatistic
"
);

this
.menu_AnnualStatistic.Enabled
=
Portal.gc.HasFunction(
"
AnnualStatistic
"
);

this
.menu_ClearAll.Enabled
=
Portal.gc.HasFunction(
"
ClearAllData
"
);

this
.menu_ImportItemDetail.Enabled
=
Portal.gc.HasFunction(
"
ImportItemDetail
"
);
}

因此,文章到这里先小结一下,就是利用现有成熟、稳定、集成性好的控件或者模块,或者利用合适易用的控件,既能事半功倍的完成任务,又能快速响应客户的需求变化 ,还能在界面整体上给客户留下好的印象,一举三得,何乐不为? 欢迎与大家一起探讨Winform开发的点点滴滴或者相互合作交流。

在我的共享软件-病人资料管理系统(该软件是是一款针对病人基本情况、手术情况、化疗情况、随访情况的存储、修改维护、条件查询、导出导出Excel、科室数据合并、图片查找、数据打印、高级查询等功能的软件)中,有时候资料管理者需要对多个条件进行组合查询,由于字段比较多,一般很难满足客户的要求,在一个窗体中列出所有的条件可供查询,而且一般情况下,很多条件很少用到,把它全部摆到主界面上也显得不够美观和实用。

我的做法是,只在主界面中列出比较常用的一些条件给客户进行普通的查询显示,如下图所示:


而需要更多条件查询的时候,把它做成一个高级查询的页面,列出所有的字段给用户选择,然后可以注册多个条件进行查询,如高级查询的界面如下所示:



这样客户一个可以方便在常用查询条件中查询数据,第二个可以当有需要的时候,在所有的字段中组合查询各种条件来查询内容,虽然高级查询界面做的不算很复杂,但满足一般的需求是没问题了,而且很多客户使用,也感觉不错,这就足够了。

说完一些总体的内容,我们下面开看看详细的代码实现,以便给大家提供思路及参考。

1、首先我们在界面上绑定相关的查询条件数据、字段列表等信息,具体代码如下所示,其中获取字段列表的时候,我们获取一条记录,取返回表格数据的表头信息即可:


private

void
BindData()
{

this
.txtCondtion.Items.Clear();

this
.txtCondtion.Items.Add(
new
CListItem(
"
模糊包含
"
, SqlOperator.Like.ToString()));

this
.txtCondtion.Items.Add(
new
CListItem(
"
等于
"
, SqlOperator.Equal.ToString()));

this
.txtCondtion.Items.Add(
new
CListItem(
"
不等于
"
, SqlOperator.NotEqual.ToString()));

PagerInfo pagerInfo

=

new
PagerInfo();
pagerInfo.CurrenetPageIndex

=

1
;
pagerInfo.PageSize

=

1
;
DataSet ds

=
BLLFactory
<
PatientCase
>
.Instance.GetAllToDataSet(pagerInfo);

if
(ds
!=

null
)
{
DataTable dt

=
ds.Tables[
0
];

this
.txtFieldName.Items.Clear();

foreach
(DataColumn col
in
dt.Columns)
{

this
.txtFieldName.Items.Add(col.ColumnName);
}
}
}

当响应添加条件的时候,我们存储相关的信息并显示在ListView里面即可,如下所示。

private

void
btnAddCondition_Click(
object
sender, EventArgs e)
{

if
(
this
.txtContent.Text.Length
==

0
)
{

if
(MessageUtil.ShowYesNoAndTips(
"
查询内容为空,您确认要继续么?
"
)
==
DialogResult.No)
{

return
;
}
}

CListItem selectItem

=

this
.txtCondtion.SelectedItem
as
CListItem;

if
(selectItem
!=

null
)
{
ListViewItem item

=

new
ListViewItem(
this
.txtFieldName.Text);
item.SubItems.Add(

this
.txtCondtion.Text);
item.SubItems.Add(

this
.txtContent.Text);
item.Tag

=
ConvertCondition(selectItem.Value);

this
.listView1.Items.Add(item);
}
}

而当用户确定的时候,我们只需要简单的处理一下,给窗体的DialogResult设置为OK即可。


private

void
btnOK_Click(
object
sender, EventArgs e)
{

if
(
this
.listView1.Items.Count
==

0
)
{

if
(MessageUtil.ShowYesNoAndTips(
"
条件组合为空,您确认要继续么?
"
)
==
DialogResult.No)
{

this
.DialogResult
=
DialogResult.None;

return
;
}
}


this
.DialogResult
=
DialogResult.OK;
}


而在弹出高级查询窗体的父窗口里面,我们这样操作,判断窗体返回值是DialogResult是OK的话,那么获取到相应的条件,然后再主界面中查询即可,代码如下所示:

private

void
tsbAdvanceSearch_Click(
object
sender, EventArgs e)
{
FrmAdvanceSearch dlg

=

new
FrmAdvanceSearch();

if
(dlg.ShowDialog()
==
DialogResult.OK)
{
SearchCondition condtion

=
dlg.
GetAdvanceCondition();
BindData(condtion);
}
}


其中
GetAdvanceCondition
函数是高级查询窗体里面的函数,主要是根据已有条件生成给父窗体调用的,返回响应的查询条件,实现如下所示:


public
SearchCondition GetAdvanceCondition()
{

if
(
this
.listView1.Items.Count
>

0
)
{
SearchCondition condition

=

new
SearchCondition();

foreach
(ListViewItem item
in

this
.listView1.Items)
{
SqlOperator op

=
(SqlOperator)item.Tag;
condition.AddCondition(item.Text, item.SubItems[

2
].Text, op);
}

return
condition;
}

return

null
;
}


private
SqlOperator ConvertCondition(
string
strSqlOperator)
{
SqlOperator sqlOperator

=
SqlOperator.Like;

try

{
sqlOperator

=
(SqlOperator)Enum.Parse(
typeof
(SqlOperator), strSqlOperator);
}

catch

{
}

return
sqlOperator;
}

其中的
SearchCondition 类是我封装的一个查询类,用来代替硬编码等相关操作,以便方便、准确、有效的实现查询条件的组合及转换的。

以上就是高级查询功能的实现思路及代码,在此抛砖引玉,希望和大家共同探讨。

在我的共享软件-病人资料管理系统(该软件是是一款针对病人基本情况、手术情况、化疗情况、随访情况的存储、修改维护、条件查询、导出导出Excel、科室数据合并、图片查找、数据打印、高级查询等功能的软件)中,客户需要实现对某个属性的图片进行管理维护,图片数量要求有多个,而且同一个资料编辑或者查看中,有很多这样的字段要求进行图片管理,效果如下所示。


基本实现方式就是在每个需要保存图片字段用GUID来记录,然后图片记录放到一个独立的表总,通过GUID来识别是属于那种类型的图片。

我们要保证在新建及编辑的时候,都可以正常的添加管理图片,那么我们就需要在加载信息的时候,设置对应按钮的Tag为一个独立的GUID字段,类似下面的代码:

新建的时候,设置类似下面的代码:


this
.btnSHCT.Tag
=
Guid.NewGuid().ToString();

this
.btnSHMRI.Tag
=
Guid.NewGuid().ToString();

this
.btnSHXRAY.Tag
=
Guid.NewGuid().ToString();

this
.btnSQCT.Tag
=
Guid.NewGuid().ToString();

this
.btnSQMRI.Tag
=
Guid.NewGuid().ToString();

this
.btnSQXRAY.Tag
=
Guid.NewGuid().ToString();

编辑的时候,类似i下面的代码:


this
.btnSHCT.Tag
=

string
.IsNullOrEmpty(info.术后CT)
?
Guid.NewGuid().ToString() : info.术后CT;

this
.btnSHMRI.Tag
=

string
.IsNullOrEmpty(info.术后MRI)
?
Guid.NewGuid().ToString() : info.术后MRI;

this
.btnSHXRAY.Tag
=

string
.IsNullOrEmpty(info.术后XRay)
?
Guid.NewGuid().ToString() : info.术后XRay;

this
.btnSQCT.Tag
=

string
.IsNullOrEmpty(info.术前CT)
?
Guid.NewGuid().ToString() : info.术前CT;

this
.btnSQMRI.Tag
=

string
.IsNullOrEmpty(info.术前MRI)
?
Guid.NewGuid().ToString() : info.术前MRI;

this
.btnSQXRAY.Tag
=

string
.IsNullOrEmpty(info.术前XRay)
?
Guid.NewGuid().ToString() : info.术前XRay;


查看图片的时候,实现就是下面所示的代码:


private

void
btnSHXRAY_Click(
object
sender, EventArgs e)
{
Button btn

=
sender
as
Button;

string
itemID
=
btn.Tag.ToString();
SetPictureButtonData(itemID);
}



private

void
SetPictureButtonData(
string
existItemID,
string
sfDate)

{

#region
验证条件


if
(
this
.txtName.Text.Length
==

0
)
{
MessageUtil.ShowTips(

"
查看管理图片需要先确定患者姓名
"
);

this
.txtName.Focus();

return
;
}

else

if
(
this
.txtHospitalNo.Text.Length
==

0
)
{
MessageUtil.ShowTips(

"
查看管理图片需要先确定患者住院号
"
);

this
.txtHospitalNo.Focus();

return
;
}

#endregion


FrmPictureView dlg

=

new
FrmPictureView();

if
(
!
string
.IsNullOrEmpty(existItemID))
{
dlg.ItemID

=
existItemID;
}

else

{
dlg.ItemID

=
Guid.NewGuid().ToString();
}

dlg.SFDate

=
sfDate;
dlg.PatienName

=

this
.txtName.Text;
dlg.HospitalNo

=

this
.txtHospitalNo.Text;
dlg.ShowDialog();
}


private

void
SetPictureButtonData(
string
existItemID)
{
SetPictureButtonData(existItemID,

""
);
}

而所有图片管理都是用一个窗体来实现,设计视图如下所示:

窗体相关的实现代码如下所示:


private

void
sortableListView1_MouseDoubleClick(
object
sender, MouseEventArgs e)
{

if
(
this
.sortableListView1.SelectedItems.Count
==

0
)

return
;

ListViewItem item

=

this
.sortableListView1.SelectedItems[
0
];

if
(item
!=

null

&&
item.Tag
!=
null
)
{
PatientPictureInfo info

=
item.Tag
as
PatientPictureInfo;

if
(info
!=

null

&&
File.Exists(info.PicturePath))
{
Process.Start(info.PicturePath);
}

else

{
MessageUtil.ShowTips(

"
图片文件路径不存在!
"
);
}
}
}


private

void
FrmPictureView_Load(
object
sender, EventArgs e)
{
BindData();
}


private

void
BindData()
{

if
(
!
string
.IsNullOrEmpty(ItemID))
{

this
.sortableListView1.Items.Clear();
List

<
PatientPictureInfo
>
list
=
BLLFactory
<
PatientPicture
>
.Instance.GetAllByItemID(
this
.ItemID);

foreach
(PatientPictureInfo info
in
list)
{
ListViewItem item

=

new
ListViewItem(info.FileName);
item.SubItems.Add(info.PicturePath);
item.Tag

=
info;


this
.sortableListView1.Items.Add(item);
}
}
}


private

void
btnClear_Click(
object
sender, EventArgs e)
{

if
(MessageUtil.ShowYesNoAndWarning(
"
您是否确定清除该项目的所有图片记录?\r\n确定后将清除记录但不删除本地图片文件!
"
)
==
DialogResult.Yes)
{

try

{

bool
success
=
BLLFactory
<
PatientPicture
>
.Instance.DeleteAllByItemID(
this
.ItemID);
}

catch
{ }

BindData();
}
}


private

void
btnAdd_Click(
object
sender, EventArgs e)
{

string
pictureRootDir
=
ConfigurationManager.AppSettings[
"
PictureRootDir
"
];

if
(
string
.IsNullOrEmpty(pictureRootDir))
{
MessageUtil.ShowError(

"
没有配置项PictureRootDir,请设置一个存放图片文件的主目录!
"
);

return
;
}


string
ImageFilter
=

"
Image Files(*.BMP;*.bmp;*.JPG;*.jpg;*.GIF;*.gif;*.PNG;*.png)|(*.BMP;*.bmp;*.JPG;*.jpg;*.GIF;*.gif;*.PNG;*.png)|All File(*.*)|*.*
"
;
OpenFileDialog dialog

=

new
OpenFileDialog();
dialog.Filter

=
ImageFilter;
dialog.Title

=

"
请选择图片
"
;
dialog.RestoreDirectory

=

true
;
dialog.InitialDirectory

=

"
D:\\
"
;
dialog.AddExtension

=

true
;
dialog.Multiselect

=

true
;

if
(dialog.ShowDialog()
==
DialogResult.OK)
{

string
[] filenameArray
=
dialog.FileNames;

foreach
(
string
filePath
in
filenameArray)
{

try

{

string
temp
=

string
.Format(
"
{0}-{1}
"
, PatienName, HospitalNo);

string
targetFilePath
=
Path.Combine(pictureRootDir, temp);

//
如果随访日期非空,那么增加一个随访目录(随访(2011-01-01))



if
(
!
string
.IsNullOrEmpty(SFDate))
{
targetFilePath

=
Path.Combine(targetFilePath,
string
.Format(
"
随访({0})
"
, SFDate));
}
DirectoryUtil.AssertDirExist(targetFilePath);


string
fileName
=
DirectoryUtil.GetFileName(filePath,
false
);

string
targetFilenamePath
=
Path.Combine(targetFilePath, fileName);
DirectoryUtil.Copy(filePath, targetFilenamePath);

PatientPictureInfo info

=

new
PatientPictureInfo();
info.ItemID

=
ItemID;
info.FileName

=
fileName;
info.PicturePath

=
targetFilenamePath;
BLLFactory

<
PatientPicture
>
.Instance.Insert(info);
}

catch
(Exception ex)
{
LogHelper.Error(ex);
MessageUtil.ShowTips(ex.Message);
}
}
BindData();
}
}

以上就是实现的相关逻辑,起主要思路就是为每个字段分配一个独立的GUID,然后新建或者编辑的时候,也要确保GUID的有效性,那样我们把图片存储复制到目录的时候,记录下来的图片地址就可以把它放到数据库记录总,然后查看图片(双击ListVIew选项),就调用图片浏览器显示图片即可。如果为了不让客户安装查看图片工具,也可以自己做一个查看图片的PictureBOx即可。这样就可以实现多个字段多个图片的管理,总体效果还是可以的。