65.9K
CodeProject 正在变化。 阅读更多。
Home

C#中的可折叠分隔符控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (66投票s)

2002年10月12日

Ms-PL

3分钟阅读

viewsIcon

471861

downloadIcon

14089

一个Mozilla风格的可折叠分隔符控件,用C#编写。

引言

这个控件是模仿 Mozilla 网页浏览器中使用的可折叠分割器,但增加了一些功能。这个控件允许通过单击分割器控件按钮来动态展开和折叠链接的表单控件,并通过拖动分割器来调整大小。 此外,还有一个选项可以展开或收缩父窗体,以便要展开的控件不占用任何更多的窗体区域。

工作原理

CollapsibleSplitter 派生自 System.Windows.Forms.Splitter 类,使用 2 个重写、鼠标事件处理程序并公开 4 个新属性。 代码相对简单,并且有很好的注释。 我将在这里介绍的唯一代码是控件绘制以及与基本分割器控件的交互。

在创建控件时,我首先使用 Stephen Toub 的 DeriveClass 实用程序创建了一个新的派生类。 下一步是绘制控件表面,首先找到基本分割器的剪辑矩形,然后定义一个新的矩形,该矩形位于分割器的垂直中心,用于折叠器控件。

protected override void OnPaint(PaintEventArgs e)
{
    // force the width to 8px so that everything
    // always draws correctly
    this.Width = 8;

    // create a Graphics object
    Graphics g = e.Graphics;

    // find the rectangle for the splitter and paint it
    Rectangle r = this.ClientRectangle; // fixed in version 1.1
    g.FillRectangle(new SolidBrush(this.BackColor), r);

    // create a new rectangle in the vertical center 
    // of the splitter for our collapse control button
    rr = new Rectangle(r.X, (int) r.Y + ((r.Height - 115)/2),
        8, 115);

    // draw the background color for our control image
    if(hot)
        g.FillRectangle(new SolidBrush(hotColor), 
            new Rectangle(rr.X + 1, rr.Y, 6, 115));
    else
        g.FillRectangle(new SolidBrush(this.BackColor), 
            new Rectangle(rr.X + 1, rr.Y, 6, 115));

    // draw the top & bottom lines for our control image
    g.DrawLine(new Pen(SystemColors.ControlDark, 1), 
        rr.X + 1, rr.Y, rr.X + rr.Width - 2, rr.Y);
    g.DrawLine(new Pen(SystemColors.ControlDark, 1), 
        rr.X + 1, rr.Y + rr.Height, rr.X + rr.Width - 2, 
        rr.Y + rr.Height);

    // draw the arrows for our control image
    // the ArrowPointArray is a point array that 
    // defines an arrow shaped polygon
    g.FillPolygon(new SolidBrush(SystemColors.ControlDarkDark), 
        ArrowPointArray(rr.X + 2, rr.Y + 3));

    g.FillPolygon(new SolidBrush(SystemColors.ControlDarkDark), 
        ArrowPointArray(rr.X + 2, rr.Y + rr.Height - 9));

    // draw the dots for our control image using a loop
    int x = rr.X + 3;
    int y = rr.Y + 14;

    // Visual Styles added in version 1.1
    switch(visualStyle)
    {
    case VisualStyles.Mozilla:
        for(int i=0; i < 30; i++)
        {
            // light dot
            g.DrawLine(
                new Pen(SystemColors.ControlLightLight), 
                x, y + (i*3), x+1, y + 1 + (i*3));

            // dark dot
            g.DrawLine(
                new Pen(SystemColors.ControlDarkDark), 
                x+1, y + 1 + (i*3), x+2, y + 2 + (i*3));

            // overdraw the background color as we actually 
            // drew 2px diagonal lines, not just dots..
            if(hot)
                g.DrawLine(new Pen(hotColor), 
                    x+2, y + 1 + (i*3), x+2, y + 2 + (i*3));
            else
                g.DrawLine(new Pen(this.BackColor), 
                    x+2, y + 1 + (i*3), x+2, y + 2 + (i*3));
        }
        break;

    case VisualStyles.DoubleDots:
        for(int i=0; i < 30; i++)
        {
            // light dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlLightLight), 
                x, y + 1 + (i*3), 1, 1 );

            // dark dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlDark), 
                x - 1, y +(i*3), 1, 1 );

            i++;

            // light dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlLightLight), 
                x + 2, y + 1 + (i*3), 1, 1 );

            // dark dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlDark), 
                x + 1, y  + (i*3), 1, 1 );
        }
        break;

    case VisualStyles.Win9x:
        g.DrawLine(new Pen(SystemColors.ControlLightLight), 
            x, y, x + 2, y);
        g.DrawLine(new Pen(SystemColors.ControlLightLight), 
            x, y, x,y + 90);
        g.DrawLine(new Pen(SystemColors.ControlDark), 
            x + 2, y, x + 2, y + 90);
        g.DrawLine(new Pen(SystemColors.ControlDark), 
            x, y + 90, x + 2, y + 90);
        break;

    case VisualStyles.XP:
        for(int i=0; i < 18; i++)
        {
            // light dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlLight), 
                x, y + (i*5), 2, 2 );

            // light light dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlLightLight), 
                x + 1, y + 1 + (i*5), 1, 1 );

            // dark dark dot
            g.DrawRectangle(
                new Pen(SystemColors.ControlDarkDark), 
                x, y +(i*5), 1, 1 );

            // dark fill
            g.DrawLine(
                new Pen(SystemColors.ControlDark), 
                x, y + (i*5), x, y + (i*5) + 1);

            g.DrawLine(
                new Pen(SystemColors.ControlDark), 
                x, y + (i*5), x + 1, y + (i*5));
        }
        break;

    case VisualStyles.Lines:
        for(int i=0; i < 44; i++)
        {
            g.DrawLine(new Pen(SystemColors.ControlDark), 
                x, y + (i*2), x + 2, y + (i*2));
        }

        break;
    }

    // dispose the Graphics object
    g.Dispose();
}

该控件矩形在 MouseMove 事件中用于确定光标是否在控件区域(热区)内,或者在基本控件分割器区域内。 一旦我们知道光标是否在“热区”中,我们就可以更改控件的各个方面:使用突出显示的背景颜色,忽略或传递 MouseDown 事件,以及设置适当的鼠标光标。

// this method was updated in version 1.11 to fix 
// a flickering problem discovered by John O'Byrne
private void OnMouseMove(object sender, MouseEventArgs e)
{
    // check to see if the mouse cursor position is within 
    // the bounds of our control
    if(e.X >= rr.X && e.X <= rr.X + rr.Width && 
        e.Y >= rr.Y && e.Y <= rr.Y + rr.Height)
    {
        if(!hot)
        {
            hot = true;
            this.Cursor = Cursors.Hand;
            this.Refresh();
        }
    }
    else
    {
        if(hot)
        {
            hot = false;
            this.Refresh();
        }

        if(!controlToHide.Visible)
            this.Cursor = Cursors.Default;
        else
            this.Cursor = Cursors.VSplit;
    }
}

protected override void OnMouseDown(MouseEventArgs e)
{
    // if the hider control isn't hot, 
    // let the base resize action occur
    if(!hot && !collapsed)
        base.OnMouseDown(e);
}

使用控件

可以使用演示应用程序中的预编译 dll 将 CollapsibleSplitter 控件添加到工具箱中,或者只需将该类复制到您的项目中并在您的代码中手动引用它。

要将可折叠分割器添加到您的 VS.Net 工具箱,请按照以下步骤操作

  • 右键单击 VS.Net 工具箱并选择“自定义工具箱...”
  • 选择“.Net Framework 组件”选项卡,然后单击“浏览..”按钮
  • 浏览并打开演示应用程序存档中的“CollapsibleSplitter.dll”文件
  • 单击“确定”将可折叠分割器控件添加到您的工具箱。

一旦您将其放在窗体上,请设置 Dock 属性,然后设置 ControlToHide 属性,以便分割器知道要与哪个窗体控件交互。 与折叠行为相关的所有属性都可以在属性窗口的“折叠选项”组下找到。 在窗体上创建后,可以通过调用 ToggleState 方法以编程方式切换视图状态,并且可以从 IsCollapsed 属性检索当前状态。

我希望您发现此控件有用,如果您改进此控件,请通过电子邮件将更新后的源代码发送给我。 如果您有任何意见或建议,请在下面的反馈部分中发布您的想法。

更新

版本 1.1 更改

  • 现在重写了 OnPaint 而不是处理事件,并且现在绘制了整个分割器,而不仅仅是折叠器控件
  • 现在正确定义了分割器矩形
  • Collapsed 属性已重命名为 IsCollapsed,并且代码已更改,因此无需设置任何值
  • 添加了新的视觉样式:Win9xXPDoubleDotsLines

版本 1.11 更改

  • 更新了 OnMouseMove 事件处理程序,以解决 John O'Byrne 发现的闪烁问题

版本 1.2 更改

  • 添加了对水平分割器的支持

版本 1.3 更改: (24 Aug 2003)

  • 添加了一个可选的 3D 边框
  • 常规代码和注释清理
  • 使用 CLSCompliant 属性标记了程序集
  • 添加了一个简单的设计器类来过滤不需要的属性
  • 添加了作为 VS.Net 工具箱控件包含的支持
  • 添加了工具箱位图
  • 删除了多余的重写
  • 添加了摘要
  • 从公共属性中删除了 ParentFolder - 现在在 OnHandleCreated 事件中自动设置它
  • 添加了展开/折叠动画代码
© . All rights reserved.