作者郑佐
适用于 Windows 操作系统
NET Framework x运行时环境
NET Windows开发Visual Studio
摘要本文阐述了在基于NET平台的Windows程序开发中使用模式窗体的诸多方面部分内容延伸到一般窗体的应用
单击此处下载本文的示例代码
内容索引
概述
模式窗体的属性设置
模式窗体中的按钮
模式窗体的打开与关闭
窗体的参数传递
Net Framework提供的模式窗体
更多资源
概述
何谓模式窗体?简单的可以理解为窗体对话框用户必须在完成该窗体上的操作或关闭窗体后才能返回打开此窗体的窗体本文不对模式窗体的定义特征功能做具体讨论主要把重点放在如何窗体应用程序中有效的使用模式窗体解决使用模式窗体中碰到的常见问题
模式窗体的属性设置
中一个SystemWindowsFormsForm类就表示一个窗体通过visual studio 设计器能够直接添加窗体切换到设计模式在属性窗口中会显示属于该窗体的属性和事件参照标准的模式窗体以visual studio 程序的菜单工具>选项打开的那个选项对话框为例对于设计器初始化的窗体还是需要进行一番设置才能达到专业化令人高兴的是这些设置都可以在设计器模式中通过属性设置实现笔者将通过代码来实现相应功能下面对其进行详细描述
FormStartPosition属性确定窗体第一次出现时的位置这里设置为在父窗体的中间显示
thisStartPosition = FormStartPositionCenterParent;
FormHelpButton属性确定窗体的标题栏上是否有帮助按钮设置显示看上去更人性化但实际不一定会对帮助功能进行实现
thisHelpButton = true;
FormMaximizeBox属性确定窗体标题栏的右上角是否有最大化框设置不让她显示
thisMaximizeBox = false;
FormMinimizeBox属性确定窗体标题栏的右上角是否有最小化框设置不让他显示
thisMinimizeBox = false;
FormShowIcon属性指示是否在窗体的标题栏中显示图标设置不显示
thisShowIcon = false;
FormShowInTaskbar属性确定窗体是否出现在Windows任务栏中这个当然要节省任务栏的宝贵空间
thisShowInTaskbar = false;
FormFormBorderStyle属性指示窗体的边框和标题栏的外观和行为设置这个属性将不允许拖动调整窗体的大小同时Icon属性将失效并不显示窗体标题栏图片
thisFormBorderStyle = FormBorderStyleFixedDialog;
FormControlBox属性确定窗体是否有控件/系统菜单框通过该设置可以隐藏标题栏的控制按钮在有些时候还是有必要设置为False标题栏就不会再有控制按钮
thisControlBox = false;
通过对以上属性的设置基本实现模式窗体的静态功能对于是否允许调整窗体的大小可根据实际情况而定
模式窗体中的按钮
模式窗体中(比如visual studio 中的选项对话框)一般会有两个基本按钮一个[确定]按钮用来提交另一个[取消]按钮用来撤销提交有时候会增加一个[应用]按钮不过像帮助菜单中的关于模式窗体可能就只有一个[确定]按钮Windows窗体为用户操作友好性提供了比较好的支持我们可以在Form设计界面的属性设置中找到AcceptButton和CancelButton两个属性默认值为空即显示(无)在属性中可以通过选择窗体上的按钮来设置值属性修改生成的代码如下
先定义两个Button
private SystemWindowsFormsButton buttonOK;
private SystemWindowsFormsButton buttonCancel;
窗体的接受按钮如果设置了此按钮则用户每次按Enter键都相当于单击了该按钮
thisAcceptButton = thisbuttonOK;
窗体的取消按钮如果设置了此按钮则用户每次按Esc键都相当于单击了该按钮
thisCancelButton = thisbuttonCancel;
可见可以通过快捷键来方便的访问特定按钮但这个有一些例外比如窗体焦点刚好在buttonCancel上当按{Enter}时实际按下的键会是buttonCancel而不是buttonOK如果焦点停在第三个按钮上那{Enter}按下相当于点击了该按钮另一个细节是通过鼠标点击按钮和快捷键操作按钮的表现行为不一样快捷键操作Button不会显示按钮被按下的显示效果看上去什么都没有发生
模式窗体的打开与关闭
谈到模式窗体的打开一般通过FormShowDialog ()方法或她的一个重载FormShowDialog (IWinWindow)来实现其中后一个方法将窗体显示为具有指定所有者的模式对话框如下代码所示
OptionForm form = new OptionForm();
//formShowDialog();
formShowDialog(this);
对于指定所有者方式打开的模式窗体可以在模式窗体内部获取主窗体的引用
//在模式窗体内部访问所属窗体
MainForm form = thisOwner as MainForm;
注意如果以FormShowDialog ()方式打开那FormOwner属性会是空引用
谈到模式窗体的关闭先来看一下模式窗体关闭后的返回值无论是调用FormShowDialog ()方法还是FormShowDialog (IWinWindow)方法都会在模式窗体关闭时返回SystemWindowsFormsDialogResult枚举值参考MSDN该枚举包含的值如下
DialogResultAbort对话框的返回值是 Abort(通常从标签为中止的按钮发送)
DialogResultCancel对话框的返回值是 Cancel(通常从标签为取消的按钮发送)
DialogResultIgnore对话框的返回值是 Ignore(通常从标签为忽略的按钮发送)
DialogResultNo对话框的返回值是 No(通常从标签为否的按钮发送)
DialogResultNone从对话框返回了 Nothing这表明有模式对话框继续运行
DialogResultOK对话框的返回值是 OK(通常从标签为确定的按钮发送)
DialogResultRetry对话框的返回值是 Retry(通常从标签为重试的按钮发送)
DialogResultYes对话框的返回值是 Yes(通常从标签为是的按钮发送)
由于某些原因在实际用户操作中比如选项数据无法保存输入的设置数据有问题点击[确定]按钮需要阻止窗体的关闭以对输入的设置进行调整对于一些开发者在技术社区贴的阻止模式窗体关闭的代码我认为不是很好的实现以下用代码来描述该实现注意其中用到了三个事件
//注册窗体关闭事件
thisFormClosing += new SystemWindowsFormsFormClosingEventHandler(thisOptionForm_FormClosing);
//注册确定按钮事件
thisbuttonOKClick += new SystemEventHandler(thisbuttonOK_Click);
//注册取消按钮事件
thisbuttonCancelClick += new SystemEventHandler(thisbuttonCancel_Click);
三个事件对应的事件处理程序如下
//确定按钮处理程序
private void buttonOK_Click(object sender EventArgs e)
{
//假设textBoxPath用来记录目录路径如果不存在要求用户重新设置
if (thistextBoxPathTextTrim()Length == )
{
MessageBoxShow(输入路径信息不对!);
thistextBoxPathFocus();
}
else
{
thisDialogResult = DialogResultOK;
}
}
//取消按钮处理程序
private void buttonCancel_Click(object sender EventArgs e)
{
thisDialogResult = DialogResultCancel;
}
//窗体关闭处理程序在关闭窗体时发生
private void OptionForm_FormClosing(object sender FormClosingEventArgs e)
{
if (thisDialogResult != DialogResultCancel && thisDialogResult != DialogResultOK)
eCancel = true;
}
上面的代码都正常就是事件写多了对上面代码进行修改去掉[取消]按钮事件和窗体关闭事件以及相关的事件处理程序首先需要在窗体构造函数中通过设置按钮的DialogResult属性来实现返回特定的DialogResult
thisbuttonOKDialogResult = SystemWindowsFormsDialogResultOK;
thisbuttonCancelDialogResult = SystemWindowsFormsDialogResultCancel;
注册确定按钮事件
//注册确定按钮事件
thisbuttonOKClick += new SystemEventHandler(thisbuttonOK_Click);
//确定按钮处理程序
private void buttonOK_Click(object sender EventArgs e)
{
if (thistextBoxPathTextTrim()Length == )
{
MessageBoxShow(输入的路径信息不对!);
thistextBoxPathFocus();
//设置文本框焦点
thisDialogResult = DialogResultNone;
}
}
可见新的实现方式代码减少了一半
窗体的参数传递
对于窗体间的数据传递是刚开始从事Net窗体应用程序开发人员碰到的一个常见问题在此讲几个常见的实现方式此节内容适用于模式窗体或非模式窗体部分方式延伸到一般类的操作
()构造函数参数传递
通过构造函数传递参数应该是比较基本的参数传递方式重载构造函数通过带参数的构造函数来实例化窗体
在窗体类内部定义参数变量
private object myParams;
实现构造函数
public OptionForm(object parameters)
{
InitializeComponent();
thismyParams = parameters;//设置参数引用
}
实例化窗体
OptionForm form = new OptionForm( myParams );
在实际使用过程中需要注意传入的是引用类型还是值类型处理方式会有所不同
()使用窗体的属性
说起属性关联上面已经提到过FormOwner属性下面笔者根据MSDN文档来比较完整的讲一下大部分的文字来自MSDN文档为保证其完整性对其中一些属性描述进行了扩展
FormOwner 属性获取或设置拥有此窗体的窗体
语法public Form Owner { get; set; }
若要使某窗体归另一个窗体所有可为其 Owner 属性分配一个对将成为所有者的窗体的引用当一个窗体归另一窗体所有时它便随着所有者窗体最小化和关闭例如如果 Form 归窗体 Form 所有则关闭或最小化 Form 时Form 也会关闭或最小化并且附属窗体从不显示在其所有者窗体后面可以将附属窗体用于查找和替换窗口之类的窗口当选定所有者窗体时这些窗口不应消失
FormOwnedForms 属性获取 Form 对象的数组这些对象表示此窗体拥有的所有窗体
语法public Form[] OwnedForms { get; }
此属性返回包含此窗体拥有的所有窗体的数组要使某窗体归另一个窗体所有可调用 AddOwnedForm 方法分配给所有者窗体的窗体将保持被拥有状态直到调用了 RemoveOwnedForm 方法如果窗体是多文档界面 (MDI) 父窗体则除了当前打开的所有 MDI 子窗体外此属性将返回所有显示的窗体
FormMdiChildren 属性获取窗体的数组这些窗体表示以此窗体作为父级的多文档界面 (MDI) 子窗体
语法public Form[] MdiChildren { get; }
此属性使您得以获取对当前在某 MDI 父窗体中打开的所有 MDI 子窗体的引用若要创建 MDI 子窗体请将要成为 MDI 父窗体的 Form 分配给该子窗体的 MdiParent 属性可以使用此属性依次通过所有 MDI 子窗体以执行一些操作如当 MDI 父窗体关闭时将数据保存到数据库中或者根据应用程序中执行的操作更新子窗体上的字段
FormMdiParent 属性获取或设置此窗体的当前多文档界面 (MDI) 父窗体
语法public Form MdiParent { get; set; }
若要创建 MDI 子窗体请将要成为 MDI 父窗体的 Form 分配给该子窗体的 MdiParent 属性可以从某 MDI 子窗体使用此属性来获取所有子窗体都需要的全局信息或者调用对所有子窗体执行操作的方法
FormActiveForm 静态属性获取此应用程序的当前活动窗体
语法public static Form ActiveForm { get; }
表示当前活动窗体或者如果没有活动窗体则为空引用可以使用此方法获得对当前活动窗体的引用以在该窗体或其控件上执行操作
FormActiveMdiChild 属性获取当前活动的多文档界面 (MDI) 子窗口
语法public Form ActiveMdiChild { get; }
返回表示当前活动的 MDI 子窗口的 Form或者如果当前没有子窗口则返回 空引用可使用此方法确定 MDI 应用程序中是否有任何打开的 MDI 子窗体也可使用此方法从 MDI 子窗口的 MDI 父窗体或者从应用程序中显示的其他窗体对该 MDI 子窗口执行操作
ContainerControlParentForm 属性获取将容器控件分配给的窗体
语法public Form ParentForm { get; }
将容器控件分配给的 Form
以上属性MSDN提供相应的代码事例可直接拿来调试使用
()使用公共属性
使用公共属性也是一种比较常用的方式通过窗体设计器添加的控件默认访问修饰符为private级别可以设置成public或Internal(在程序集内部可见)来对外公开比如对窗体中的Button进行公开那就可以访问Button的相关属性同时也可以注册事件或撤销事件注册如
OptionForm form = new OptionForm();
formbuttonOKClick += new EventHandler(buttonOK_Click);
formShowDialog();
对于只允许读取访问或修改访问的控件或变量可以通过属性来控制对()方式进行修改去除重载构造函数增加属性也可以实现同样的效果
public object MyParams
{
get { return thismyParams; }
set { thismyParams = value; }
}
()使用公共方法
使用公共方法类似于属性对上面的同等实现如下
//获取参数
public object GetParams()
{
return thismyParams;
}
//设置参数
public void SetParams(object myParams )
{
thismyParams = myParams;
}
()使用静态类该方式可以简单的理解为静态变量全局共享通过下面代码能够比较清楚的理解先来定义静态类
public static class ParameterSettings
{
//公共静态变量
public static string Username = Zhengzuo;
//私有静态变量
private static string userRole = Administrators;
//私有静态变量
private static string password = ;
//内部属性
internal static string UserRole
{
get { return userRole; }
}
//公共属性
public static string Password
{
get { return password; }
private set { password = value; }
}
}
在需要访问的地方通过以下方式进行
string username = ParameterSettingsUsername;
string password = ParameterSettingsPassword;
string userRole = ParameterSettingsUserRole;
ParameterSettingsUsername = 郑佐;//修改成新用户名
()窗体实现Singleton模式
Singleton模式是我们开发过程中最常用的模式之一在技术社区经常看到有人谈及对主窗体实现Singleton但个人认为这不是一种妥当的做法因为没有这个必要这里通过另一个自定义类来进行演示假设UserLoginInfo类用来保存登录系统后的用户凭据
/*==============================================
程序 郑佐
==============================================*/
public class UserLoginInfo
{
//实现Singleton模式线程安全
private readonly static UserLoginInfo currentUserInfo = new UserLoginInfo();
//提供全局访问点
public static UserLoginInfo CurrentUserInfo
{
get { return currentUserInfo; }
}
//阻止显式实例化但不能阻止反射方式调用
private UserLoginInfo()
{
}
//公共变量
public string Username;
//私有变量
private static string userRole;
//私有变量
private static string password;
//内部属性
internal string UserRole
{
get { return userRole; }
set { userRole = value; }
}
//公共属性
public string Password
{
get { return password; }
internal set { password = value; }
}
}
在其他代码中进行访问
UserLoginInfoCurrentUserInfoUsername =郑佐;
UserLoginInfoCurrentUserInfoUserRole = dotnetlover;
UserLoginInfoCurrentUserInfoPassword = ;
对于Singleton模式的实现方式有很多编写时需要考虑是否需要保证实例访问的线程安全问题以免引发不可预料的情况为了提高性能可以考虑惰性实例化关于Singleton模式的更多信息可以参考另一篇文章
()发布事件进行订阅
通过事件来传递参数应该说是一种推的实现方式在产生事件时进行被动的获取相关数据这里将通过一个自定义事件来演示数据的传输
在自定义事件时标准的做法都会先定义一个事件参数类要么直接使用基类EventArgs或者从EventArgs继承实现自己的参数类假设自定义基类取名为OptionSettingEventArgs
//选项设置事件参数类
public class OptionSettingEventArgs : EventArgs
{
private string changedPath;
//构造函数
public OptionSettingEventArgs(string changedPath)
{
thischangedPath = changedPath;
}
//读取参数
public string ChangedPath
{
get { return thischangedPath; }
}
}
以上参数类只包含一个修改后的路径参数接下去我们要对原先的OptionForm窗体增加事件定义这里使 中提供的泛型类来实现
//定义事件
public event EventHandler<OptionSettingEventArgs> OptionSettingChanged;
编写事件引发程序如下
//引发OptionSettingChanged事件
protected virtual void OnOptionSettingChanged(OptionSettingEventArgs e)
{
if (OptionSettingChanged != null)
{
OptionSettingChanged(this e);
}
}
对文件目录选择按钮事件处理程序进行修改来实现事件激发并没有考虑直接从文本框直接数据输入方式
//通过目录对话框设置新的路径
private void buttonBrowser_Click(object sender EventArgs e)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
DialogResult result = dialogShowDialog(this);
if (result == DialogResultOK)
{
if(thistextBoxPathText != dialogSelectedPath)
{
thistextBoxPathText = dialogSelectedPath;
OptionSettingEventArgs args = new OptionSettingEventArgs(dialogSelectedPath);
OnOptionSettingChanged(args);
}
}
}
好了一切准备工作完成调用代码如下
OptionForm form = new OptionForm();
//注册事件
formOptionSettingChanged += new EventHandler (form_OptionSettingChanged);
formShowDialog();
通过以下事件处理程序来验证其正确性
private void form_OptionSettingChanged(object sender OptionSettingEventArgs e)
{
string newPath = eChangedPath;
MessageBoxShow(this StringFormat(新路径为{} newPath) 提示);
}
在实际开发过程中合理的处理方式可能是以上几种方式的组合对于窗体间的参数传递我在另一篇文章中也有比较多的基础实例讲解
Net Framework提供的模式窗体 < Framework为我们提供了一些比较常用的对话框,在开发过程中省了不少事,以下对其进行介绍。tW.wingwit.cOM
MessageBox。显示可包含文本、按钮和符号(通知并指示用户)的消息框。通过MessageBox.Show 静态方法来打开模式对话框。
public static DialogResult Show ( string text );
该方法包含多个重载版本。复杂的一个方法如下,
public static DialogResult Show ( IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath, HelpNavigator navigator, Object param ) ;
根据不同的参数可以定制对话框的行为。
另外一些对话框提供了特定功能。
OpenFileDialog。打开文件对话框,从FileDialog类继承,提示用户打开文件,无法继承此类。对于文件的打开操作属于比较常见的。
SaveFileDialog。保存文件对话框,从FileDialog类继承,提示用户选择文件的保存位置。无法继承此类。
FolderBrowserDialog。目录浏览对话框,从CommonDialog类继承,提示用户选择文件夹。无法继承此类。 FontDialog。字体设置对话框,从CommonDialog类继承,提示用户从本地计算机上安装的字体中选择一种字体。可继承该类。
ColorDialog。颜色设置对话框,从CommonDialog类继承,表示一个通用对话框,该对话框显示可用的颜色以及允许用户定义自定义颜色的控件。可继承该类。
PageSetupDialog。打印页面设置对话框,从CommonDialog类继承,允许用户更改与页面相关的打印设置,包括边距和纸张方向。无法继承此类。
PrintDialog。打印对话框,从CommonDialog类继承,允许用户选择一台打印机并选择文档中要打印的部分。无法继承此类。
PrintPreviewDialog。打印预览对话框,从Form类继承,表示包含 PrintPreviewControl 的对话框窗体。可继承该类。由于该类从Form类继承,所以除了通过
PrintPreviewDialog.ShowDialog ();
PrintPreviewDialog.ShowDialog (IWin32Window);
方法以模式方式打开窗体外,还可以通过PrintPreviewDialog.Show ();或其重载PrintPreviewDialog.Show (IWin32Window);方法按正常非模式方式打开。
上面列举的文件对话框抽象基类FileDialog是从CommonDialog抽象类继承,因此所有从该类继承的对话框都可以通过CommonDialog.ShowDialog ();或其重载CommonDialog.ShowDialog (IWin32Window);方法以模式方式打开窗体。
更多资源
对于windowsforms的开发,这里推介几本大师写的书,
Charles Petzold着的《Programming Windows with C# (Core Reference)》,中文翻译版为《Microsoft c#Windows程序设计(上下册)》。
Chris Sells着的《Windows Forms Programming in C#》,中文翻译版为《Windows Forms程序设计》。在上看到该书好像已经出第二版了。