C# 工具栏按钮与菜单项的绑定实现(MSVS2005)
一 引言
一个成功的用户界面离不开工具栏,工具条的存在很大程度上减轻了用户的操作负担,特别是对于与用户交互非常频繁的界面。工具栏按钮通常需要有一个菜单项与之对应,要求该菜单项与工具栏按钮具有相同的行为(如Click事件响应)和状态(某些表示状态的属性,如最常用的Checked属性)。好了,废话少说,进入正题。
二 思路
从ToolStripButton派生一个类BuddiableToolStripButton,在派生类中新建一个ToolMenuItem,当实例化一个BuddiableToolStripButton(以下简称BTB)变量时,同时会得到一个ToolMenuItem的实例(以下简称TMI),该实例具有以下特性:
1. 当点击TMI时,会触发BTB的OnClick事件;
2. 当BTB的Checked,Enabled,Visible等表示状态的属性发生改变时,TMI的对应属性作出同样的变化;
3. 当BTB的Image,Text等提示性属性发生改变时,TMI的对应属性作出同样的变化。
第一个特点使得TMI与BTB有相同的行为,点击TMI与点击BTB的效果是没什么差别的。第二个特点这使得TMI与BTB维持同样的状态,这一点非常重要,如果要通过Enabled或Visible属性来控制工具栏按钮(BTB),使其不再响应Click事件时,如果仍然可以通过点击TMI来触发这部分响应代码,将可能带来一些没有预料到的应用程序错误,如果不对这些错误进行处理则应用程序存在崩溃的危险。BTB和TMI这些属性的同步变化避免了则一点。第三个特点是为了保持BTB与TMI的风格一致性而设,当BTB的Image和Text属性被改变时,调用者无须对TMI的相同属性作出显式赋值。当然,如果你需要TMI的这些属性与BTB不一样,你可以在BTB的这些属性该变之后对TMI重新显式赋值。
三 实战
下面是尝试建立派生类BuddiableToolStripButton并测试它。
1. 打开 Visual Studio2005,新建一个普通Windows应用程序(也可以是控件库等其他工程,凭个人喜好);
2. 在工程下新建一个组件类,取名“BuddiableToolStripButton.cs”;
3. 打开类BuddiableToolStripButton的代码编辑器,修改基类为“ToolStripButton”;
4. 定义一个ToolStripMenuItem类型的类级变量m_MenuItem,在类的构造函数中实例化该变量,并按构造函数的参数来初始化此菜单项的一些对应属性;
5. 为m_MenuItem的Click事件添加响应函数,在该函数中调用派生类的OnClick函数;
6. 重写基类的OnEnabledChanged,OnVisibleChanged,OnCheckedChanged等函数,在这些函数中同步m_MenuItem的对应属性;
7. 提供公开属性BuddyMenuItem让调用者可以获得该ToolStripMenuItem项。
完整的代码如下:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsApplication1
{
public partial class BuddiableToolStripButton : ToolStripButton
{
private ToolStripMenuItem m_MenuItem;
public BuddiableToolStripButton()
: base()
{
InitializeComponent();
m_MenuItem = new ToolStripMenuItem("", null, new EventHandler(this.OnMenuItemClick));
}
public BuddiableToolStripButton(string text)
: base(text)
{
InitializeComponent();
m_MenuItem = new ToolStripMenuItem(text, null, new EventHandler(this.OnMenuItemClick));
}
public BuddiableToolStripButton(string text, Image image, EventHandler onClick, string name)
: base(text, image, onClick, name)
{
InitializeComponent();
m_MenuItem = new ToolStripMenuItem(text, image, new EventHandler(this.OnMenuItemClick));
}
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
m_MenuItem.Enabled = this.Enabled;
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
m_MenuItem.Visible = this.Visible;
}
protected override void OnCheckedChanged(EventArgs e)
{
base.OnCheckedChanged(e);
m_MenuItem.Checked = this.Checked;
}
protected override void OnCheckStateChanged(EventArgs e)
{
base.OnCheckStateChanged(e);
m_MenuItem.CheckState = this.CheckState;
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
m_MenuItem.Text = this.Text;
}
public override Image Image
{
get
{
return base.Image;
}
set
{
base.Image = value;
m_MenuItem.Image = value;
}
}
protected void OnMenuItemClick(object sender, EventArgs e)
{
this.OnClick(e);
}
public ToolStripMenuItem BuddyMenuItem
{
get { return m_MenuItem; }
}
}
}
测试该组件:
在工程自动生成的窗体Form1中加入一个工具条ToolStrip1,在窗体设计器中,为该工具条添加一个BuddyToolStripButton类型的按钮(如果在设计器中,为ToolStrip1添加项目的下拉菜单中未包含BuddyToolStripButton项,你需要先编译一次本工程),一个ToolStripSeparator和一个ToolStripDropDownButton(此处方便起见,接受控件的默认名称),如下各图:
图(1)
图(2)
图(3)
完成上述步骤后,切换到Form1的代码编辑器,在其构造函数的InitializeComponent(); 句后添加语句将buddiableToolStripButton中包含的菜单项添加到toolStripDropDownButton1的下拉项目中,并在buddiableToolStripButton的Click事件响应函数中加入如下所示的测试代码:
public Form1()
{
InitializeComponent();
this.toolStripDropDownButton1.DropDownItems.Add(this.buddiableToolStripButton1.BuddyMenuItem);
}
private void buddiableToolStripButton1_Click(object sender, EventArgs e)
{
MessageBox.Show("BuddiableToolStripButton1 was clicked!");
buddiableToolStripButton1.Checked = !buddiableToolStripButton1.Checked;
}
一个简单的测试程序完成,启动调试,测试按钮及其包含的菜单项的工作情况:
图(4)
图(5)
图(6)
经测试,该派生的工具栏按钮及其包含的菜单项实现了我们预期的功能。
四 后记
组件类BuddiableToolStripButton通过继承ToolStripButton并包含一个ToolStripMenuItem,实现了工具栏按钮和菜单栏项目的行为和状态的同步,减少了项目应用时代码的编写和出错的可能性。
该组件类以工具栏按钮为主导,其状态的变化会导致包含于其中的菜单项状态变化,但这一路径不是可逆的,不可以通过对菜单项绑定Click事件响应函数来响应工具栏按钮的Click事件,也不可以通过设置菜单项的Enabled,Visible,Checked等属性来控制工具栏按钮的状态,这是此类的局限性所在。当然,上述的某些问题可以通过编写更多的代码来解决,这里不再赘述。
面向对象是一项强大而方便的技术,C# 对面向对象的忠实支持和很多人性化的设计使得我们在创建适合于自己使用的控件或组件时非常方便,这也是越来越多的程序员向C# 走来的重要原因。
最后,因为是第一次写作,内容组织得比较凌乱,感谢您耐心读完本文!本人也是一个C# 的初学者,犯错在所难免,感谢您指正文中的纰漏,欢迎联系我一起探讨C# 编程的知识。