在winform程序设计中经常会遇到一些设置性问题,在此记录总结一些经常遇到的小问题。
1.MDI多窗体程序中,子窗口点击最大化,会出现看到子窗口的icon显示在菜单中,而实际上正常状态下子窗口的showIcon属性是false的。但最大化之后就是出显示在父窗口的菜单栏上,默认icon看起来不太好看。 这情况怎么样出掉icon.就是子窗口最大化不是显示icon。
做法:
在父窗体的MenuStrip控件中添加一个ItemAdd事件
menuStrip_ItemAdded事件中添加如下代码:
/// 子窗口最大化时去掉主窗体菜单的icon图标
/// 在mdi子窗体最大化的时候,会发生如下动作:
/// 分别为Icon,最大化,恢复跟最小化。
/// 其中除了Icon之外,其他三个的Text属性都赋予了文本值。
/// 另外,Icon作为MenuStrip的第一项Item,它的索引为0.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void menuStrip1_ItemAdded(object sender, ToolStripItemEventArgs e)
{
if (e.Item.Text == "")
{
menuStrip1.Items.RemoveAt(0);
}
}
OK 问题解决。
2. 在winform程序中如果我们添加一个默认MDI窗体,那么会有一些默认菜单选项。实际当中很多菜单是不需要的,我们可以删除。
如果我们添加一个单文档窗体,同时修改了isMdiContainer设置为ture,也能实现MDI窗体效果,如果这时候得自己添加菜单menuStrip然后逐个设置,添加系统默认MDI窗体还一些有功能菜单,比如窗口菜单,这个是比较通用的,基本上所有程序都可以用的。 自己添加单个文档窗口再修改成MDI窗体的这些功能就没有了。那么可以添加个别代码就可以实现相关功能。如有实现默认窗口菜单功能,除了水平平铺/垂直平铺/重叠.几个选项外,最主要一个功能还能根据一当前子窗口打开情况,显示成列表形式。
其实只设置menuStrip一个属性MdiWindowListItem,指向窗口菜单项。 代码如下:
this.menuStrip1.MdiWindowListItem = this.windowTSMI;
OK,运行程序打开多个子窗口的时候就是排列出来了,且当前活动窗口就会在前面打勾。
3. 多次打开子窗口只显示一次。
一般情况下,打开子窗口多次我们代码如下:
/// 环境参数配置
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void EnvirnomentForm_Click(object sender, EventArgs e)
{
FrmEnvirnomentSet envie = new FrmEnvirnomentSet();
envie.MdiParent = this;
envie.Show();
}
那么重复点击多次菜单就会生成多个相同的窗口,因为每一次都是new 出来的。并不是运行同一个实例。
/// 通过窗口标题判断窗口是否存在,目的是为了只打开一次
/// </summary>
/// <param name="mdiFrm">包含多个子窗口的主窗口</param>
/// <param name="caption">子窗口标题</param>
/// <returns></returns>
private static bool IsWinExist(Form mdiFrm, string caption)
{
bool isExist = false;
foreach (Form frm in mdiFrm.MdiChildren)
{
if (frm.Text.Equals(caption))
{
isExist = true;
frm.Activate();
frm.Show();
break;
}
}
return isExist;
}
/// <summary>
/// 环境参数配置
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void EnvirnomentForm_Click(object sender, EventArgs e)
{
string caption = otherQueryTSMI.Text;//用菜单名称
if (!IsWinExist(this, caption))
{
FrmEnvirnomentSet envie = new FrmEnvirnomentSet();
envie.MdiParent = this;
envie.Text = caption;
envie.Show();
}
}
这样通过判断标题Title的名称,很容易实现重复操作只打开一次窗口。当然也有其他很办法来实现这个功能,但我觉得这个最简单的办法。
4.实现动态菜单功能
有时程序需要动态菜单去配置,把菜单项写在数据库或者XML文件中,然后根据不同配置显示不同的菜单项
我这里只是为了简单实现动态配置菜单功能,通过配置数据库相关字段来实现动态菜单
做法如下:
/// 根据数据库仪器表创建动态菜单
/// </summary>
/// <param name="parent">ToolStripMenuItem根,如第一个菜单根名称为:CollectTSMI</param>
private void CreateMenu(ToolStripMenuItem parent)
{
//操作数据库获取一个DataTable
clsMachineOpr bll= new clsMachineOpr();
DataTable dtbl = bll.GetColumnDataTable(0, "IsShow=True ORDER BY OrderId ASC", "MachineName,MachineModel,IsSupport");
if (dtbl != null && dtbl.Rows.Count > 0)
{
htbl = new Hashtable();
string code = string.Empty;
string text = string.Empty;
ToolStripMenuItem tsmi = null;
//菜单部分
for (int i = 0; i < dtbl.Rows.Count; i++)
{
code = dtbl.Rows[i]["MachineModel"].ToString().Trim();
text = dtbl.Rows[i]["MachineName"].ToString().Trim();
htbl.Add(code, text);
{
defaultMethodTag = code;
}
//可以添加前缀或者后缀,根据实际需要设置吧
//code = "Auto_" + code;
tsmi = new ToolStripMenuItem();
tsmi.Name = code;
tsmi.Text = text;
tsmi.Click += new EventHandler(DynamicTSMI_Click);
parent.DropDownItems.Add(tsmi);
}
//加分隔符
ToolStripSeparator tStripSeparator1 = new ToolStripSeparator();
tStripSeparator1.Name = "tStripSeparator1";
tStripSeparator1.Size = new System.Drawing.Size(217, 6);
parent.DropDownItems.Add(tStripSeparator1);
}
//如果还有其他固定的子菜单项,继续添加
parent.DropDownItems.Add(otherDataHandTSMI);
}
/// <summary>
/// 动态菜单事件,
/// 采用反射技术。
/// 注意:菜单click事件函数只能用public访问权限
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DynamicTSMI_Click(object sender, EventArgs e)
{
// MenuClickMethod menuClickMethod = new MenuClickMethod();
//创建菜单调用方法类的实例,MenuClickMethod 的方法以子菜单名字命名,预先定义好每个
//子菜单的操作,采用外部类的方法可以实现更加动态,接近插件形式进行封装.
//但本次时间过紧,不考虑用外部类和插件形式实现。使用内部公有方法即可实现
Type type = this.GetType(); //menuClickMethod.GetType();如果用外部类采用注释的代码
try
{
//动态获取方法对象
MethodInfo mi = type.GetMethod(((ToolStripMenuItem)sender).Name);
mi.Invoke(this, null); //menuClickMethod //如果用外部类调用,则用注解代码替换this。指定方法
}
catch (Exception ex)
{
// throw ex;
MessageBox.Show(ex.Message + ex.Source);
}
}
//调用方法
/// <summary>
/// 这里菜单点击操作函数
/// 函数名称要跟前面数据字段相同
/// 且要设置成public
/// </summary>
public void XXXX()
{
//相当于点击菜单事件,操作你要实现的功能。。。。
}
5.限制程序只能打开一个,如果有应用正在运行,再次打开相同程序会给予用户提示
做法如下:
/// 实现程序启动唯一实例的一种方法
/// </summary>
private static void GetSingleThread()
{
string name = Process.GetCurrentProcess().ProcessName;
int id = Process.GetCurrentProcess().Id;
Process[] prc = Process.GetProcesses();
foreach (Process pr in prc)
{
if ((name == pr.ProcessName) && (pr.Id != id))
{
MessageBox.Show("对不起,本机已经有XXX统正在运行!\n.", caption, MessageBoxButtons.OK, MessageBoxIcon.Warning);
System.Environment.Exit(0);
}
}
}
6.关闭窗口给予提示信息
/// 重写系统消息函数。
/// 即在关闭系统时提示!
/// </summary>
/// <param name="msg">消息对象</param>
protected override void WndProc(ref Message msg)
{
const int syscommand = 0x0112;
const int close = 0xF060;
if (msg.Msg == syscommand && ((int)msg.WParam == close))
{
if (MessageBox.Show("程序正在运行,是否确定退出?", "系统提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
{
return;
}
}
base.WndProc(ref msg);
}