WinForm界面开发集锦-超精典

2018-11-02 15:38

WinForm界面开发之模块化分合

本文继续WInform界面开发系列的介绍,主要针对性介绍Winform开发过程中,经常用到的模块:数据字典模块、参数配置模块、权限管理模块的模块化应用,以及各模块的分合之道。

1、数据字典模块

我们知道,一般程序基本上都涉及到了一个是数据参考用途的数据字典模块,不管是Web还是Winform的,这个模块基本上是必不可少的,Winform的界面效果大致如下所示:

本模块的数据字典支持无限级树形分层应用,可以添加大类,字典项目等数据, 系统集成只需要拷贝相关的字典表即可实现集成。

2、参数配置模块

在程序中,一般应用就是通过代码把相关的内容进行转义保存或者解析,以便呈现给用户更好的数据展示效果,这个模块比较通用,可分可合。

另外一个也比较常用的就是参数的配置管理模块,一般如果参数比较少,直接使用.NET的配置保存功能即可,如下图所示。

但这种方式只能保存比较少的内容,对于比较复杂的配置,一般很少采用这种模式存放程序的参数信息,这种方式存储的参数,如果不是放到一个独立的文件中,还存在一个不同步的现象。

其实我们还是可以把参数的配置功能作为一个独立的模块进行处理,我的程序就是经常这么干的,而且由于是相对比较独立,并在设计时候就支持参数的编辑及展示功能,因此效率大大提高,对使用用户来说,由于修改界面比较统一,而且参数的说明等很丰富,因此用户修改系统的配置参数友好性大大增强,界面效果如下所示: 使用用户看到的参数配置界面效果:

开发者在设计时刻的参数配置界面如下所示:

这些参数最终目的是为了程序的读取和调用,调用代码非常简单,而且由于是强类型的属性以及良好的提示功能,应该是比较方便的,示例代码如下所示:

代码

double hours = span.Hours;

hours = (hours < SystemConfig.Default.KFPartAtLeaseHoure) ?

SystemConfig.Default.KFPartAtLeaseHoure : hours;//最小的钟点房计费

if (span.Minutes < 60 && span.Minutes >= SystemConfig.Default.KFPartHourMinutes) {

hours += 1; }

else if (span.Minutes > SystemConfig.Default.KFPartHalfHourMinte &&

span.Minutes < SystemConfig.Default.KFPartHourMinutes)

{

hours += 0.5; }

其中的SystemConfig.Default.KFPartAtLeaseHoure就是参数的名称了,根据这个名称就可以读取和设置该参数的值。

该参数配置模块是一个Visual Studio的Addin插件,因此可以在设计时刻提供参数的添加、修改、删除等支持,非常方便,而且也能在发布后给用户界面提供修改,是一个独立的模块。

3、权限管理模块

最后说说第三个模块,权限管理模块,我们知道,一般对于进销存或者稍微复杂一点的系统,都需要最基本的权限控制模块,以便控制不同用户的访问功能,这个可以做成独立的应用程序进行管理,如下图所示:

程序是一个独立的程序,但是权限系统需要和业务应用系统结合一起才有价值,因此它们之间的结合,一个除了数据库的整合(把权限系统需要的表整合一起),还需要在系统的代码中进行整合(实现功能模块的登陆及权限控制等)。登陆验证如下所示: try {

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(logi

nName);

#region 获取用户的功能列表

Function functionBLL = new Function(); List list = functionBLL.GetFunctionsByUser(info.ID, \);

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

bLogin = true;

Portal.gc.LoginInfo = info;

this.DialogResult = DialogResult.OK; } else {

MessageUtil.ShowTips(\用户帐号密码不正确\); this.tbPass.Text = \; //设置密码为空 } }

catch (Exception err) {

MessageUtil.ShowError(err.Message); }

用户功能权限认证如下所示:

#region KTV包间管理

if (Portal.gc.HasFunction(\)) {

OutlookBarBand myBasicBand = new OutlookBarBand(\包间管理\);

myBasicBand.SmallImageList = this.imageList; myBasicBand.LargeImageList = this.imageList; if (Portal.gc.HasFunction(\)) {

myBasicBand.Items.Add(new OutlookBarItem(\KTV包间状态视图\, 0)); }

if (Portal.gc.HasFunction(\)) {

myBasicBand.Items.Add(new OutlookBarItem(\KTV包间设置\, 1)); }

if (Portal.gc.HasFunction(\)) {

myBasicBand.Items.Add(new OutlookBarItem(\KTV商品设置\, 3)); }

if (Portal.gc.HasFunction(\)) {

myBasicBand.Items.Add(new OutlookBarItem(\KTV服务生管理\, 4)); }

if (Portal.gc.HasFunction(\)) {

myBasicBand.Items.Add(new OutlookBarItem(\KTV预订管理\, 5)); }

if (Portal.gc.HasFunction(\)) {

myBasicBand.Items.Add(new OutlookBarItem(\KTV其他款项登记\, 6)); }

myBasicBand.Background = SystemColors.AppWorkspace;

myBasicBand.TextColor = Color.White; outlookBar1.Bands.Add(myBasicBand); }

#endregion

至此,三个常用而且比较独立模块介绍完毕,这几种场景你用了几个呢,如果觉得有价值,欢迎一起讨论切磋。

开发之道,顺手拈来;分合之需, 顺其自然;优码不语,润物无声; winform 复选框控件赋值的小技巧

Posted on 2010-02-07 16:36 伍华聪 阅读(1909) 评论(5) 编辑 收藏

前几天,有一位园友写了一篇不错的文章《WinForm 清空界面控件值的小技巧》,文章里面介绍了怎么清空界面各个控件值的一个好技巧,这个方法确实是不错的,在繁杂的界面控件值清理中,可谓省时省力。

本人在开发Winform程序中,也有一个类似的小技巧,不是清空控件值,而是赋值,给复选框赋值和获取值的小技巧,分享讨论一下。

应用场景是这样的,如果你有一些需要使用复选框来呈现内容的时候,如下面两图所示:

以上的切除部分的内容,是采用在GroupBox中放置多个CheckBox的方式;其实这个部分也可以使用Winform控件种的CheckedListBox控件来呈现内容。如下所示。

不管采用那种控件,我们都会涉及到为它赋值的麻烦,我这里封装了一个函数,可以很简单的给控件 赋值,大致代码如下。

CheckBoxListUtil.SetCheck(this.groupRemove, info.切除程度);

那么取控件的内容代码是如何的呢,代码如下:

info.切除程度 = CheckBoxListUtil.GetCheckedItems(this.groupRemove);

赋值和取值通过封装函数调用,都非常简单,也可以重复利用,封装方法函数如下所示。

代码

public class CheckBoxListUtil {

///

/// 如果值列表中有的,根据内容勾选GroupBox里面的成员. ///

///

///

public static void SetCheck(GroupBox group, string valueList) {

string[] strtemp = valueList.Split(','); foreach (string str in strtemp) {

foreach (Control control in group.Controls) {

CheckBox chk = control as CheckBox; if (chk != null && chk.Text == str) {

chk.Checked = true; } } } }

///

/// 获取GroupBox控件成员勾选的值 ///

///

/// 返回逗号分隔的值列表

public static string GetCheckedItems(GroupBox group) {

string resultList = \;

foreach (Control control in group.Controls) {

CheckBox chk = control as CheckBox; if (chk != null && chk.Checked) {

resultList += string.Format(\, chk.Text);

} }

return resultList.Trim(','); }

///

/// 如果值列表中有的,根据内容勾选CheckedListBox的成员. ///

/// ///

public static void SetCheck(CheckedListBox cblItems, string valueList) {

string[] strtemp = valueList.Split(','); foreach (string str in strtemp) {

for (int i = 0; i < cblItems.Items.Count; i++) {

if (cblItems.GetItemText(cblItems.Items[i]) == str)

{

cblItems.SetItemChecked(i, true); } } } }

///

/// 获取CheckedListBox控件成员勾选的值 ///

/// /// 返回逗号分隔的值列表

public static string GetCheckedItems(CheckedListBox cblItems) {

string resultList = \;

for (int i = 0; i < cblItems.CheckedItems.Count; i++)

{

if (cblItems.GetItemChecked(i)) {

resultList += string.Format(\, cblItems.GetItemText(cblItems.Items[i])); } }

return resultList.Trim(',');

}

}

以上代码分为两部分, 其一是对GroupBox的控件组进行操作,第二是对CheckedListBox控件进行操作。

这样在做复选框的时候,就比较方便一点,如我采用第一种GroupBox控件组方式,根据内容勾选的界面如下所示。

应用上面的辅助类函数,如果你是采用GroupBox方案,你就可以随便拖几个CheckBox控件进去就可以了,也犯不着给他取个有意义的名字,因为不管它是张三还是李四,只要它的父亲是GroupBox就没有问题了。

如何使用dotnetbar控件来构造多文档界面

Posted on 2010-12-17 22:20 伍华聪 阅读(2183) 评论(10) 编辑 收藏

在前段时间一篇随笔《利用优秀的.NET界面控件,打造新潮的界面效果》中介绍过Dotnetbar的界面效果,虽然引发不少关于该控件效果的争议,不过话说回来,使用该控件也不失为一个界面的解决方案,本文继续探寻该控件的使用,在QQ搜通天企业版软件中使用该控件做了一次完整的改造,碰到并解决了一些问题,本文主要总结介绍如何利用Dotnetbar控件来实现多文档界面的效果。首先我们先来看看软件的主体界面效果,如下图所示。

本界面主要利用Bar控件来实现多文档的界面效果,每个子窗体皆为一个用户控件,当然也包括容纳各种窗体的容器CtrlMdiBar类,也是一个用户控件,在主界面Ribbon控件的RibbonClientPanel区域放置容器,然后每次打开窗体,就动态创建或者激活一个窗体页,这样就实现了多文档界面的效果了。

bar = new CtrlMdiBar();

bar.DockTabClosed = new DockTabClosedDelegate(OnDockItemClosed);

SetDetailPanel(bar);

private CtrlMdiBar bar;

private Dictionary MdiDict = new Dictionary();

public void SetDetailPanel(UserControl uc) {

if (uc == null) {

throw new ArgumentNullException(\, @\用户控件uc不能为空\); }

uc.Dock = DockStyle.Fill;

ribbonDetailPanel.Controls.Clear(); ribbonDetailPanel.Controls.Add(uc); }

public void SetMdiForm(UserControl uc, string itemText) {

DockContainerItem item = null;

string type = itemText;//uc.GetType().Name; if (MdiDict.ContainsKey(type)) {

item = MdiDict[type]; } else {

PanelDockContainer panel = new PanelDockContainer();

item = new DockContainerItem(); item.Control = panel; item.Text = itemText;

uc.Dock = DockStyle.Fill; panel.Controls.Add(uc);

MdiDict.Add(type, item); bar.bar1.Items.Add(item); }

bar.bar1.SelectedDockContainerItem = item; this.Refresh(); }

private void OnDockItemClosed(DockContainerItem item) {

string type = item.Text;

if (MdiDict.ContainsKey(type)) {

MdiDict.Remove(type); } }

然后每次按钮打开一个窗体页的时候,只需要简单的调用函数即可,如下面几个窗体页的打开操作一样

private void btnMyQunUser_Click(object sender, EventArgs e) {

SetMdiForm(new CtrlGroupUser(), MDIForm.查询个人群成员.ToString()); }

private void btnMyQQUser_Click(object sender, EventArgs e)

{

SetMdiForm(new CtrlQQContact(), MDIForm.查询个人QQ好友.ToString()); }

private void btnXiaoyou_Click(object sender, EventArgs e)

{

SetMdiForm(new CtrlXiaoyou(), MDIForm.查询QQ校友.ToString()); }

其中CtrlMDIBar容器,主要是一个用户控件放置一个Dotnetbar的Bar控件,然后在该控件里面调用委托处理控件关闭的事件,主要代码如下所示。

public delegate void DockTabClosedDelegate(DockContainerItem item);

public partial class CtrlMdiBar : UserControl {

public DockTabClosedDelegate DockTabClosed;

public CtrlMdiBar() {

InitializeComponent(); }

private void bar1_DockTabClosed(object sender, DevComponents.DotNetBar.DockTabClosingEventArgs e) {

//MessageExUtil.ShowTips(\ if (DockTabClosed != null) {

DockTabClosed(e.DockContainerItem); } }

...............

这样,我们后面如果要增加一个窗体页放到容器里面,只需要再定义一个用户控件,并设计好界面等处理方式即可,动态增加一个界面窗体页将非常简单。

从Socket数据处理线程想到的普通Winform数据显示的应用

Posted on 2010-02-05 23:15 伍华聪 阅读(2213) 评论(8) 编辑 收藏

在前面介绍过Socket编程的文章中,有一篇是《Socket开发探秘--基类及公共类的定义》,其中介绍了一个独立线程处理类,专门在一个独立的线程中处理Socket的数据包的。摘录前面的内容介绍一下:

5、ThreadHandler,数据独立线程处理类

对每个不同类型的数据(不同的协议类型),可以用独立的线程进行处理,这里封装了一个基类,用于进行数据独立线程的处理

上面的工作原理是这样的,每次收到数据后,系统把数据扔给独立线程处理类,处理类放到一个队列Queue的列表中,每次从中弹出一个来处理,根据不同的协议头,分派到不同的线程来处理,这样可以提高响应速度,防止线程之间的阻塞,能够充分利用系统的资源。

其实我们还可以把这个思想应用到日常的Winform开发中,有时候我们可能在处理一些比较费时的操作,可能是需要做一部分显示一部分,类似日常生活中的项目周报、月周报的场景,因为不可能等一个几年的项目完成后,你才告诉老板你的工作情况吧。

借鉴Socket的数据处理方式,我在Winform程序中运用了这种数据处理方式,如我在采集赶集网的数据的时候,可以把采集到的部分数据扔给系统中的数据独立处理线程,让他们爱怎么显示就怎么显示,程序不中断,继续乐此不彼的去采集内容去,然后继续这样做(每采集一部分仍出去一部分),直到采集完毕。 代码

public class ThreadHandler {

///

/// 处理数据线程 ///

Thread _Handlehread = null;

private string _ThreadName = \;

private Fifo _DataFifo = new Fifo();

///

/// 线程名字 ///

public string ThreadName {

get { return _ThreadName; } set { _ThreadName = value; } }

///

/// 接收处理数据 ///

///

public virtual void AppendData(T data) {

if (data != null)

_DataFifo.Append(data); }

///

/// 数据处理 ///

protected virtual void DataThreadHandle() {

try {

while (true) {

T data = _DataFifo.Pop(); DataHandle(data); } }

catch(Exception ex) {

LogHelper.Error(ex); } }

///

/// 数据处理 ///

///

public virtual void DataHandle(T data) { }

///

/// 开始数据处理线程 ///

public virtual void StartHandleThread() {

if (_Handlehread == null) {

_Handlehread = new Thread(new ThreadStart(DataThreadHandle));

_Handlehread.IsBackground = true; _Handlehread.Start(); }

LogHelper.Info(string.Format(\线程

->{0}启动。。。。。。\, _ThreadName)); }

上面的是独立线程处理的基类,下面我们用一个子类继承他,方便代码逻辑的剥离封装: 在下面的代码中,我根据不同的Table表内容类型,放到不同的函数中进行处理,以便实现不同的显示方式。 代码

public class TestDataHandleThread : ThreadHandler {

public TestDataHandleThread() {

base.ThreadName = \测试数据操作处理线程\; }

public override void DataHandle(PreData data) {

try {

if (data.Key == KeyType.PostAticle) {

if (!string.IsNullOrEmpty(data.Content.TableName))

{

ThreadPool.QueueUserWorkItem(new WaitCallback(Portal.gc.MainDialog.DisplayForm), data.Content); } }

else if (data.Key == KeyType.ContactInfo) {

if (!string.IsNullOrEmpty(data.Content.TableName))

{

ThreadPool.QueueUserWorkItem(new WaitCallback(Portal.gc.MainDialog.DisplayContactForm), data.Content);

} } }

catch (Exception ex) {

LogHelper.Error(\测试数据操作处理线程异常:{0}\ + ex.ToString());

控件另外一项功能,也是集实用功能之所成,打印当前列表内容,如下图所示,该内容会保存用户在每个列表数据中的信息,打印不同的表头内容,如下图所示。

那么控件应该如何使用呢,下面介绍一下使用的相关代码。

1、首先在Form_Load事件中绑定相关的委托处理事件,默认有“新建”、编辑选定项、删除、刷新、打印几个按钮,您可以在此基础上增加更多的菜单。

private void FrmProduct_Load(object sender, EventArgs e)

{

BindData();

this.winGridViewPager1.ProgressBar = this.toolStripProgressBar1.ProgressBar;

this.winGridViewPager1.OnPageChanged += new EventHandler(winGridViewPager1_OnPageChanged);

this.winGridViewPager1.OnStartExport += new EventHandler(winGridViewPager1_OnStartExport);

this.winGridViewPager1.OnEditSelected += new EventHandler(winGridViewPager1_OnEditSelected);

this.winGridViewPager1.OnAddNew += new EventHandler(winGridViewPager1_OnAddNew);

this.winGridViewPager1.OnDeleteSelected += new EventHandler(winGridViewPager1_OnDeleteSelected);

this.winGridViewPager1.OnRefresh += new EventHandler(winGridViewPager1_OnRefresh);

this.winGridViewPager1.AppendedMenu = this.contextMenuStrip1; }

2。实现表头解析和上面的委托时间的例子代码如下.

private void winGridViewPager1_OnRefresh(object sender, EventArgs e) {

BindData(); }

private void winGridViewPager1_OnDeleteSelected(object sender, EventArgs e) {

if (MessageUtil.ShowYesNoAndTips(\您确定删除选定的记录么?\) == DialogResult.No) {

return; }

DataGridView grid = sender as DataGridView; if (grid != null) {

foreach (DataGridViewRow row in grid.SelectedRows)

{

BLLFactory.Instance.Delete(row.Cells[\].Value.ToString()); }

BindData(); } }

private void winGridViewPager1_OnEditSelected(object sender, EventArgs e) {

DataGridView grid = sender as DataGridView; if (grid != null) {

foreach (DataGridViewRow row in grid.SelectedRows)

{

FrmEditProduct dlg = new FrmEditProduct(); dlg.ID = row.Cells[\].Value.ToString(); if (DialogResult.OK == dlg.ShowDialog()) {

BindData(); }

break; } } }

private void winGridViewPager1_OnAddNew(object sender, EventArgs e) {

btnAddNew_Click(null, null); }

private void winGridViewPager1_OnStartExport(object sender, EventArgs e) {

PagerInfo pagerInfo = new PagerInfo(); pagerInfo.CurrenetPageIndex = 1; pagerInfo.PageSize = int.MaxValue;

this.winGridViewPager1.AllToExport = BLLFactory.Instance.GetAllToDataSet(pagerInfo).Tables[0];//product.GetAllToDataSet(pagerInfo).Tables[0]; }

private void winGridViewPager1_OnPageChanged(object sender, EventArgs e) {

BindData(); }

private void BindData() {

#region 添加别名解析

this.winGridViewPager1.AddColumnAlias(\, \编号\); this.winGridViewPager1.AddColumnAlias(\\, \产品类型\);

this.winGridViewPager1.AddColumnAlias(\\, \产品名称\);

this.winGridViewPager1.AddColumnAlias(\

on\, \产品规格\);

this.winGridViewPager1.AddColumnAlias(\, \产品型号\);

this.winGridViewPager1.AddColumnAlias(\\, \进货价\);

this.winGridViewPager1.AddColumnAlias(\\, \建议价\);

this.winGridViewPager1.AddColumnAlias(\\, \零售价\);

this.winGridViewPager1.AddColumnAlias(\\, \生产厂商\);

this.winGridViewPager1.AddColumnAlias(\_ID\, \厂商ID\);

this.winGridViewPager1.AddColumnAlias(\, \备注\);

this.winGridViewPager1.AddColumnAlias(\\, \更新日期\);

#endregion

SearchCondition condition = new SearchCondition(); condition.AddCondition(\, this.txtName.Text, SqlOperator.Like)

.AddCondition(\, this.cmbProductType.Text, SqlOperator.Like)

.AddCondition(\, this.cmbSpecNumber.Text, SqlOperator.Like)

.AddCondition(\, this.txtNote.Text, SqlOperator.Like)

.AddCondition(\, this.cmbManufacture.Text, SqlOperator.Like);

string where = condition.BuildConditionSql().Replace(\, \);

List list = BLLFactory.Instance.Find(where, this.winGridViewPager1.PagerInfo);

this.winGridViewPager1.DataSource = new WHC.Pager.WinControl.SortableBindingList(list); }

这样就实现了分页控件的内容展示以及相关功能的菜单挂接,实现后的菜单展示可能是这样子的,如下图所示,是否还可以呢,呵呵.


WinForm界面开发集锦-超精典.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:成人学位英语作文部分范文解析

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: