C#把MDI子窗体变为标签页面(不改写任何控件)

     先给大家看下最终效果图如下:

这个是用vs2005写的,感觉那个关闭按钮图片支持不是那么好,在vs2008及其以上版里使用,效果更佳。

 

    首先我们先新建一个项目,默认有个Form1窗体,将ShowIcon、ShowInTaskbar属性设置为False,这样这个窗体就没有最大化、最小化按钮,和不显示左上角的图标了。

    接着,新建一个MDI父窗体,应该默认会有设置好的菜单栏、工具栏和状态栏,这里去掉工具栏和状态栏,并删除了大部分菜单,只留下一个新建功能,修改新建功能代码如下:

private void ShowNewForm(object sender, EventArgs e)
{
    // 创建此子窗体的一个新实例。
    Form1 childForm = new Form1();
    // 在显示该窗体前使其成为此 MDI 窗体的子窗体。
    childForm.MdiParent = this;
    childForm.Text = "窗口" + childFormNumber++;
    childForm.WindowState = FormWindowState.Maximized;
    childForm.Show();
}


 

运行后点击“新建”效果如下:

到这里,想必很多人都不希望菜单旁边的那个图标和右边的那几个按钮存在吧,这个问题比较容易解决,在窗体在再添加一个MenuStrip控件,然后将下面那个MenuStrip控件设置为不可见即可:

再次运行程序,嘿嘿,不想要的没有了吧!到现在,前驱工作才算完成,下面就要开始给子窗体添加标签了,这里使用TabControl来实现。继续在MDI窗体上添加一个TabControl,然后设置SizeMode为Fixed,ItemSize的Width为140,Height:20,设置Size的Height为24,Dock属性为Top。然后添加一个Label标签,用作关闭按钮,设置AutoSize为False,Size:w 15,h 13,然后给他设置一个关闭的图标:

到此,界面工作已经差不多完成了,现在开始代码实现子窗体的标签控制了。

 

在MDI的Form_Load中先把TabControl的页面全部清理了,然后把Label1隐藏:

private void MDIParent1_Load(object sender, EventArgs e)
{
    tabControl1.TabPages.Clear();
    tabControl1.Visible = false;    // 没有元素的时候隐藏自己
    label1.Visible = false;
}

 

接着,我们要做的是:当新建一个窗体的时候同时多一个TabPage:

/// <summary>
/// 添加一个标签
/// </summary>
/// <param name="frm"></param>
private void AddTabPage(Form frm)
{
    TabPage tp = new TabPage();
    tp.Tag = frm;  // 当前标签控制的窗体对象记录在Tag属性中
    tp.Text = frm.Text;
    tabControl1.TabPages.Add(tp);
    tabControl1.SelectedIndex = tabControl1.TabCount - 1;  // 默认选中最后一个新建的标签
    if (!tabControl1.Visible) tabControl1.Visible = true;  // 如果自己是隐藏的则显示自己
}

 

好了,接着修改原来新建的代码为:

private void ShowNewForm(object sender, EventArgs e)
{
    // 创建此子窗体的一个新实例。
    Form1 childForm = new Form1();
    // 在显示该窗体前使其成为此 MDI 窗体的子窗体。
    childForm.MdiParent = this;
    childForm.Text = "窗口" + childFormNumber++;
    childForm.WindowState = FormWindowState.Maximized;
    childForm.Show();
    AddTabPage(childForm);  // 新建窗体同时新建一个标签
}

 

这时候,虽然可以新建窗体,同时也有对应标签显示,但是标签的切换却没能同时将窗体切换到相应的窗体,因此,还需要一个方法来控制,添加TabControl的IndexChange事件,代码如下:

private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
    if (tabControl1.SelectedIndex > -1)
        (tabControl1.TabPages[tabControl1.SelectedIndex].Tag as Form).Focus();
}

到此,效果图如下:




好了,新建窗体同时也可以新建标签了,许多人习惯了现在浏览器的操作,双击标签可以关闭当前页,每个标签的右边还有一个关闭按钮,所以咱们还需要给TabControl添加双击事件和关闭按钮,由于多处使用到,因此把关闭功能单独出一个函数:

/// <summary>
/// 删除一个标签
/// </summary>
/// <param name="selectedIndex"></param>
private void CloseTabPage(int selectedIndex)
{
    (tabControl1.TabPages[selectedIndex].Tag as Form).Close();
    tabControl1.TabPages.RemoveAt(selectedIndex);
    if (tabControl1.TabPages.Count == 0) tabControl1.Visible = false;
}


接着添加tabControl1_MouseDoubleClick事件,响应关闭功能:

private void tabControl1_MouseDoubleClick(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left) // 只有左键双击才响应关闭
        CloseTabPage(tabControl1.SelectedIndex);
}


到此,以上操作已经实现了简单的子窗体变为标签页面显示了。下面,就是给标签添加上一个关闭按钮,就是还没用上的Label,这个就有些麻烦了,因为tabControl没有提供直接获取当前鼠标所在的是哪个TabPage的属性或者方法,因此只能通过坐标计算来得到当前是在哪个项上:

/// <summary>
/// 从菜单弹出位置得到当前所在的标签索引
/// </summary>
/// <returns></returns>
private int GetPageIndexWidthPoint(int pointX)
{
    int x = 0;
    for (int i = 0; i < tabControl1.TabPages.Count; ++i)
    {
        if (pointX >= x && pointX <= x + tabControl1.ItemSize.Width)
            return i;
        x += tabControl1.ItemSize.Width;
    }
    return tabControl1.TabPages.Count - 1;
}
/// <summary>
/// 计算从第一个可见项到当前项的宽度
/// </summary>
/// <param name="nowItemIndex"></param>
/// <returns></returns>
private int GetItemWidth(int nowItemIndex)
{
    int w = 0;
    for (int i = 0; i <= nowItemIndex; i++)
    {
        w += tabControl1.ItemSize.Width;
    }
    return w;
}


然后给tabControl添加MouseMove事件,得到当前是在第几个项上,并在相应的项上显示关闭按钮:

private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
    int i = GetPageIndexWidthPoint(e.X); // 获取当前鼠标所在标签位置
    if (i > -1)
    {
        int posX = GetItemWidth(i) - label1.Width - 4;    // 计算label1的x坐标
        //定位label1位置
        label1.Left = posX;
        label1.Top = tabControl1.Top + 6;
        label1.Visible = true;
    }
    else
    {
        label1.Visible = false;
    }
    label1.Tag = i;
}


当鼠标离开tabControl范围则隐藏关闭按钮:

private void tabControl1_MouseLeave(object sender, EventArgs e)
{
    label1.Visible = false;
}


运行后效果如下如:

 

然后给Label1添加上MouseClick事件,响应关闭功能:

private void label1_MouseClick(object sender, MouseEventArgs e)
{
    if (label1.Tag != null && (int)label1.Tag > -1)
    {
        CloseTabPage((int)label1.Tag);
        label1.Visible = false;  // 关闭后将自己隐藏
    }
}

 

一般来说在标签除了双击、和点击关闭按钮可以关闭相应标签和窗体了,还有就是鼠标右键有个关闭菜单,因此我们在添加一个右键菜单contextMenuStrip,添加一个项菜单,名为“关闭”,然后将tabControl的contextMenuStrip属性设置为这个菜单,接着双击菜单的“关闭”,编写代码如下:

private void 关闭ToolStripMenuItem_Click(object sender, EventArgs e)
{
    int index = GetPageIndexWidthPoint(contextMenuStrip1.Left - this.Left);  // 这里也需要通过弹出菜单的位置来得到当前是哪个项弹出的,注意菜单位置是针对屏幕左边的距离
    CloseTabPage(index);
}


好了,到此完成了MDI窗体标签化的设计了,运行下查看效果,理论上一切都OK了,但是细心的朋友会发现...NO!!Label标签的关闭按钮没有达到预期效果!点击压根没反应,想必是因为当前鼠标是被tabControl给捕获了导致Label没有捕获到鼠标,因此失去了事件响应。

接下来的处理才是Label能够相应MouseClick的关键,还好label有提供一个属性:Capture。因此我们在tabControl的MouseMove上做一些处理,如果鼠标进入label区域,则强制让Label捕获鼠标:

private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
    int i = GetPageIndexWidthPoint(e.X); // 获取当前鼠标所在标签位置
    if (i > -1)
    {
        int posX = GetItemWidth(i) - label1.Width - 4;    // 计算lblClose的x坐标
        //定位label1位置
        label1.Left = posX;
        label1.Top = tabControl1.Top + 6;
       //如果在关闭标签范围则强制标签捕获鼠标
        if (label1.Left <= e.X && label1.Left + label1.Width >= e.X &&
            e.Y >= 6 && e.Y <= 6 + label1.Height)
            label1.Capture = true;
        label1.Visible = true;
    }
    else
    {
        label1.Visible = label1.Capture || false;  // 只有当label没有捕获鼠标的时候隐藏关闭按钮
    }
    label1.Tag = i;
}
private void tabControl1_MouseLeave(object sender, EventArgs e)
{
    label1.Visible = label1.Capture || false;
    if (!label1.Visible)
        label1.Tag = -1;
}


好了,再运行下,发现还是不对劲,鼠标到了标签上后就感觉有些反应迟钝了,只有点击了鼠标才会正常过来!这是因为在上面代码中强制让label捕获鼠标后没是否了,不管鼠标移动在哪个位置,当前还是由label控制鼠标消息的,因此还需要添加label的MouseMove事件来及时释放鼠标:

private void label1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.X < 0 || e.X > label1.Width || e.Y < 0 || e.Y > label1.Height)  // 超出label范围释放鼠标
        label1.Capture = false;
}


最后运行下程序,OK了!这样就可以实现MDI子窗体标签样式显示了。

Demo源码下载

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值