闲话休提~
一:自定义Tab按钮
如图所示
我们的tab按钮左部是文字;右部是关闭按钮;
此按钮有两种状态:选中和未选中
未选中的按钮鼠标滑上背景色会变为淡蓝色;
选中的按钮背景色是黄色
关闭按钮鼠标滑上去是深黄色
控件中涉及的属性和公开的事件属性
/// <summary>
/// Tab标题
/// </summary>
public string Caption;
/// <summary>
/// 是否选中
/// </summary>
bool IsSelected = true;
/// <summary>
/// 文字的颜色
/// </summary>
Color StrColor = Color.Black;
/// <summary>
/// 宽度
/// </summary>
int StrWidth;
/// <summary>
/// 选中事件
/// </summary>
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public event EventHandler OnSelect;
/// <summary>
/// 单击关闭按钮事件
/// </summary>
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public event EventHandler OnClose;
注释还是比较清楚的,就不多说了
接着看这个控件自己的事件
/// <summary>
/// 鼠标移入事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabBTN_MouseEnter(object sender, EventArgs e)
{
if (!IsSelected)
{
this.BackColor = ColorTranslator.FromHtml("#4D6082");
}
}
/// <summary>
/// 鼠标移出事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabBTN_MouseLeave(object sender, EventArgs e)
{
if (!IsSelected)
{
this.BackColor = ColorTranslator.FromHtml("#293955");
}
}
/// <summary>
/// 鼠标移动事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabBTN_MouseMove(object sender, MouseEventArgs e)
{
var flag = IsMouseOnClosePoint();
if (flag)
{
DrawControl(Color.Black, Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(232)))), ((int)(((byte)(166))))));
}
else
{
DrawControl(StrColor, this.BackColor);
}
}
/// <summary>
/// 重绘事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabBTN_Paint(object sender, PaintEventArgs e)
{
DrawControl(StrColor, this.BackColor);
}
移入和移出事件都是要触发移动事件的
移动事件要先判断鼠标所在的位置,是不是出于关闭按钮位置;
然后再根据鼠标的位置以不同的颜色绘制控件
下面看绘制控件和判断鼠标位置的相关方法
/// <summary>
/// 重写创建事件
/// </summary>
protected override void OnCreateControl()
{
base.OnCreateControl();
var g = this.CreateGraphics();
StrWidth = (int)g.MeasureString(Caption, SystemFonts.DefaultFont).Width;
g.Dispose();
this.Width = StrWidth + 24;
}
/// <summary>
/// 绘制控件
/// </summary>
/// <param name="fore"></param>
/// <param name="bg"></param>
void DrawControl(Color fore, Color bg)
{
var g = this.CreateGraphics();
g.DrawString(Caption, SystemFonts.DefaultFont, new SolidBrush(StrColor), new PointF(3, 8));
var p = new Pen(fore, (float)1);
g.FillRectangle(new SolidBrush(bg), new Rectangle(StrWidth + 6, 7, 13, 13));
g.TranslateTransform(StrWidth + 12, 13);
g.RotateTransform(45);
for (var i = 0; i < 4; i++)
{
g.RotateTransform(90);
g.DrawLine(p, 0, 0, 6, 0);
}
g.ResetTransform();
p.Dispose();
g.Dispose();
}
/// <summary>
/// 鼠标位置
/// </summary>
/// <returns></returns>
public bool IsMouseOnClosePoint()
{
var p = this.PointToClient(MousePosition);
var crx = new Rectangle(StrWidth + 3, 3, 16, 16);
return crx.Contains(p);
}
我们在创建控件的时候得到了文本的宽度
根据这个宽度来绘制控件文本和关闭按钮的位置
我们在属性里为这个控件定义了事件的handler
下面看看这些handler是怎么触发的
/// <summary>
/// 取消选中
/// </summary>
public void DisSelectMe()
{
IsSelected = false;
this.BackColor = ColorTranslator.FromHtml("#293955");
StrColor = Color.White;
DrawControl(StrColor, this.BackColor);
}
/// <summary>
/// 选择中
/// </summary>
public void SelectMe()
{
IsSelected = true;
this.BackColor = SystemColors.Info;
StrColor = Color.Black;
DrawControl(StrColor, this.BackColor);
}
/// <summary>
/// 触发自定义事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TabBTN_Click(object sender, EventArgs e)
{
var flag = IsMouseOnClosePoint();
if (flag)
{
OnClose(this, EventArgs.Empty);
}
else
{
if (IsSelected)
{
return;
}
OnSelect(this, EventArgs.Empty);
SelectMe();
}
}
到此为止完成了tab按钮的制作
可能有些地方还做的不是很完美~欢迎批评指正
二:业务窗体的基类
所有的业务窗体都继承自这个基类BaseForm
这个窗体基类有三个公开的属性
/// <summary>
/// 菜单数据
/// </summary>
public MenuModel FormMenu { get; set; }
/// <summary>
/// tab按钮
/// </summary>
public TabBTN FormTabBTN { get; set; }
/// <summary>
/// 子菜单
/// </summary>
public Label SubMenu { get; set; }
这三个属性在后面会用到
这里先不说了
/// <summary>
/// 构造函数
/// </summary>
public BaseForm()
{
InitializeComponent();
this.TopLevel = false;
}
一般顶层窗体是不允许被当作子控件放在容器控件中的
所以我们要设置窗体的TopLevel属性
/// <summary>
/// tab按钮选中事件;
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public virtual void tbn_OnSelect(object sender, EventArgs e)
{
this.Show();
}
/// <summary>
/// tab按钮关闭事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public virtual void tbn_OnClose(object sender, EventArgs e)
{
this.Close();
}
这是tab按钮的两个事件~
在创建tab按钮的时候注册的~
待会我们再说怎么创建的tab按钮和注册这两个事件~
因为并不是在baseForm里创建的tab按钮
private void BaseForm_VisibleChanged(object sender, EventArgs e)
{
if (Utils.IsInDesignMode())
{
return;
}
this.VisibleChanged -= new EventHandler(BaseForm_VisibleChanged);
var mf = Utils.GetMainForm();
if (this.Visible)
{
foreach (var hf in mf.FormHistory)
{
if (hf.FormMenu.Url.Equals(this.FormMenu.Url))
{
continue;
}
if (hf.Visible)
{
hf.Hide();
}
}
FormTabBTN.SelectMe();
mf.FormHistory.Remove(this);
mf.FormHistory.Insert(0, this);
mf.MainContainerP.Controls.Clear();
mf.MainContainerP.Controls.Add(this);
SubMenu.BackColor = SystemColors.Info;
//TODO:系统名称可以做到数据库里去
mf.Text = string.Format("XXX管理系统-{0}", FormMenu.MenuName);
}
else
{
FormTabBTN.DisSelectMe();
SubMenu.BackColor = Color.Transparent;
}
this.VisibleChanged += new EventHandler(BaseForm_VisibleChanged);
}
这是BaseForm的一个重要事件
隐藏和显示切换的时候被触发
如果从隐藏变为显示
先遍历所有打开过的业务窗体,如果有是显示状态的,那么就把他隐藏掉,因为当前系统只能有一个业务窗体是出于显示状态的 接着选中TAB按钮, FormHistory的Remove和Insert主要是为了让系统记住哪些窗体是最近显示过的; MainContainerP的Clear和Add是为了让窗体显示在容器控件内
如果从显示变为隐藏
TAB按钮取消选中, 子菜单的背景颜色变成透明的,(其实就是子菜单取消选中)
事件处理的开始取消了事件注册
事件处理的结束有把事件注册进去了
这样做主要是为了避免多次触发事件
Utils.GetMainForm();获取主窗口的代码如下:
/// <summary>
/// 主窗口
/// </summary>
private static MainForm mf;
/// <summary>
/// 获取主窗口
/// </summary>
/// <returns></returns>
public static MainForm GetMainForm()
{
if (mf == null)
{
mf = Application.OpenForms["MainForm"] as MainForm;
}
return mf;
}
当业务窗体关闭时要处理一些逻辑
代码如下
private void BaseForm_FormClosing(object sender, FormClosingEventArgs e)
{
this.VisibleChanged -= new EventHandler(BaseForm_VisibleChanged);
var mf = Utils.GetMainForm();
mf.FormHistory.Remove(this);
this.SubMenu.BackColor = Color.Transparent;
if (mf.FormHistory.Count > 0)
{
mf.FormHistory[0].Show();
}
foreach (var f in mf.FormHistory)
{
if (f.FormTabBTN.Left > FormTabBTN.Left)
{
f.FormTabBTN.Left -= (FormTabBTN.Width + 6);
}
}
mf.TabContainerP.Controls.Remove(FormTabBTN);
}
取消事件注册
移除历史记录
取消子菜单选中
打开最近一次打开的业务窗体(如果有的话)
重写设置tab按钮的位置(主要是被关闭的tab按钮的右边的tab按钮)
删除tab按钮
三:动态创建业务窗体
我们在上一节中只讲了子菜单的滑入和滑出事件,而没有讲单击事件
单击事件就是创建业务窗体的事件了
来看一下代码
/// <summary>
/// 子菜单弹起事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void sm_MouseUp(object sender, MouseEventArgs e)
{
var lb = sender as Label;
var m = lb.Tag as MenuModel;
if (string.IsNullOrEmpty(m.Url))
{
Utils.Alert("没有与此菜单相关的业务窗体");
return;
}
BaseForm f = null;
foreach(var hf in FormHistory)
{
if (hf.FormMenu.Url.Equals(m.Url))
{
f = hf;
break;
}
}
if (f == null)
{
f = CreateOneForm(m);
f.SubMenu = lb;
}
if (f != null&&!f.Visible)
{
f.Show();
}
}
其实这个方法里的业务逻辑不多
主要的还是f = CreateOneForm(m);这一句
/// <summary>
/// 创建一个业务窗体;包括tab按钮
/// </summary>
/// <param name="m"></param>
private BaseForm CreateOneForm(MenuModel m)
{
var ass = this.GetType().Assembly;
var url = string.Format("XL.Client.Forms.{0}", m.Url);
BaseForm f = null;
try
{
f = ass.CreateInstance(url) as BaseForm;
}
catch
{
Utils.Alert("没有与此菜单相关的业务窗体");
return null;
}
f.Dock = DockStyle.Fill;
f.FormMenu = m;
var tabBtn = new TabBTN();
tabBtn.OnClose += new EventHandler(f.tbn_OnClose);
tabBtn.OnSelect += new EventHandler(f.tbn_OnSelect);
tabBtn.Caption = m.MenuName;
int left = 6;
var tabCount = TabContainerP.Controls.Count;
if (tabCount > 0)
{
var lastTab = TabContainerP.Controls[tabCount - 1];
left += (lastTab.Left + lastTab.Width);
}
tabBtn.Left = left;
TabContainerP.Controls.Add(tabBtn);
f.FormTabBTN = tabBtn;
return f;
}
我们把菜单的URL字段拿出来,反射了一个业务窗体的实例
然后创建了tab按钮的实例,并让这个业务窗体持有这个实例
注意tab按钮的close和select事件是怎么注册的哦~ 亲~
好吧~就这些~
今天的内容比较多~
写的匆忙~有问题大家尽情的提吧~
接下来的内容是:登录、闪屏、客户端缓存数据、WCF安全验证