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

垂直可折叠分组框 分步指南

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (8投票s)

2012年1月24日

CPOL

2分钟阅读

viewsIcon

32666

downloadIcon

930

垂直可折叠分组框

VerticalGroupBox

引言

这是我的第一篇文章,而且我不是专业的写作者,请原谅我拙劣的英语和文章写作技巧。

很多时候,我们希望在有限的空间内展示大量信息,因为用户希望在一个表单上看到更多信息,而无需滚动。因此,可折叠控件应运而生。这里提供一点努力,制作一个可以垂直折叠的控件。

阅读前

这是一个逐步指南,用于实现此目标。您会看到重复的函数和属性声明,对这些函数或属性的新更改将用粗体标出,而新的内容则以普通字体显示。

步骤 1

在第一步中,我们将绘制分组框和位于分组框左上角的图像。

像这样声明变量。这意味着我们的控件最初将展开。同时声明图像矩形边界。

private bool _iscollapsed = false;
private Rectangle _buttonrect=new Rectangle(0,14,11,11);
public bool IsCollapsed
{
   get { return _iscollapsed; }            
}
private Rectangle ButtonRect
{
   get { return _buttonrect; }
} 

创建一个新的函数,该函数负责绘制分组框。首先,重新定位新的矩形,以便我们的图像位于左侧线条的中间。然后使用 GroupBoxRenderer.DrawGroupBox 函数绘制分组框。

void DrawGroupBox(Graphics g)
{            
   Rectangle bounds = new Rectangle(ClientRectangle.X+4, ClientRectangle.Y + 6, ClientRectangle.Width-4, ClientRectangle.Height-6);
   GroupBoxRenderer.DrawGroupBox(g, bounds, Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled);
}

另一个负责绘制图像的函数。

private void DrawButton(Graphics g)
{
   if (IsCollapsed)
      g.DrawImage(Properties.Resources.plus, ButtonRect);
   else
      g.DrawImage(Properties.Resources.minusver, ButtonRect);
}

并在 Paint 函数中像这样调用这些函数

protected override void OnPaint(PaintEventArgs pe)
{            
   DrawGroupBox(pe.Graphics);
   DrawButton(pe.Graphics);            
}

现在查看完成此步骤后的结果。

step1.png

第二步

在这一步中,我们将向分组框添加垂直文本

添加一个 StringFormat 变量,并将文本方向设置为垂直。同时定义一个 SolidBrush,首次调用时创建,并在最后重复使用并释放。

private StringFormat format = new StringFormat(StringFormatFlags.DirectionVertical);
private SolidBrush _drawBrush = null;
private SolidBrush DrawBrush
{
   get
   {
      if (_drawBrush == null)
         return _drawBrush = new SolidBrush(Color.FromArgb(0, 70, 213));
      else
         return _drawBrush;
   }
}

在 DrawGroupBox 函数中计算字符串宽度,并调用 DrawString 函数将字符串绘制在框的左侧线条上。

void DrawGroupBox(Graphics g)
{            
   Rectangle bounds = new Rectangle(ClientRectangle.X+4, ClientRectangle.Y + 6, ClientRectangle.Width-4, ClientRectangle.Height-6);
   GroupBoxRenderer.DrawGroupBox(g, bounds, Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled);
   
   StringFormat sf = new StringFormat();
   int i_textPos = (bounds.X + 8) + ButtonRect.Width + 2;
   int i_textSize = (int)g.MeasureString(Text, this.Font).Width;
   i_textSize = i_textSize < 1 ? 1 : i_textSize;
   int i_endPos = i_textPos + i_textSize + 1;
           
   g.DrawString(Text, this.Font, DrawBrush, ButtonRect.X-4,ButtonRect.Y+15,format);
}

现在查看完成此步骤后的结果。

step2.png

步骤 3

在这一步中,我们将完成两项任务:(1)使图像具有交互性,即它将像一个按钮一样工作;(2)使分组框成为一个可折叠的框。

private int _actualwidth = 0;
private int _currentwidth = 0;
private int _collapsewidth = 20;
private int CollapseWidth
{
   get { return _collapsewidth; }
}
private int ActualWidth
{
   get { return _actualwidth; }
   set { _actualwidth = value; }
}
private int CurrentWidth
{
   get { return _currentwidth; }
   set { _currentwidth = value; }
}
public bool IsCollapsed
{
   get { return _iscollapsed; }
   set
   {
      _iscollapsed = value;
      if (!value)
      {                    
         Width = ActualWidth;
      }
      else
         Width = CollapseWidth;         
      Invalidate();
   }
}
protected override void OnPaint(PaintEventArgs pe)
{
   if (ActualWidth == 0)
      ActualWidth = Width;
   DrawGroupBox(pe.Graphics);
   DrawButton(pe.Graphics);            
}
重写 OnMouseUp 以捕获是否在图像内部执行了单击操作,如果是,则调用 Collapsed 更改函数并切换其当前状态。
private void CollapsedChanged()
{
   IsCollapsed = !IsCollapsed;            
}

protected override void OnMouseUp(MouseEventArgs e)
{
   if (ButtonRect.Contains(e.Location))
   {                
      CollapsedChanged();
   }
   base.OnMouseUp(e);
}
现在查看完成此步骤后的结果。

step3.png

步骤 4

在这一步中,我们将美化我们的控件。如果看到文本下方有一条线,看起来很丑陋,让我们将其删除。同时重写 OnResize 函数并调整其实际宽度,这在我们的控件大小更改时很有帮助。
void DrawGroupBox(Graphics g)
{            
   Rectangle bounds = new Rectangle(ClientRectangle.X+4, ClientRectangle.Y + 6, ClientRectangle.Width-4, ClientRectangle.Height-6);
   GroupBoxRenderer.DrawGroupBox(g, bounds, Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled);
   
   StringFormat sf = new StringFormat();
   int i_textPos = (bounds.X + 8) + ButtonRect.Width + 2;
   int i_textSize = (int)g.MeasureString(Text, this.Font).Width;
   i_textSize = i_textSize < 1 ? 1 : i_textSize;
   int i_endPos = i_textPos + i_textSize + 1;
   g.DrawLine(SystemPens.Control, ButtonRect.X+4, ButtonRect.Y + 15, ButtonRect.X+4, i_endPos);                    
   g.DrawString(Text, this.Font, DrawBrush, ButtonRect.X-4,ButtonRect.Y+15,format);
}
protected override void OnResize(EventArgs e)
{
   if (ActualWidth == 0)
      ActualWidth = Width;
   base.OnResize(e);
}

步骤 5

在这一步中,我们将添加事件支持,当折叠更改事件触发时,我们可以使用它在父表单中执行操作。
public delegate void CollapseChangeEventHandler(object sender);
public event CollapseChangeEventHandler OnCollapsedChanged;
private void CollapsedChanged()
{
   IsCollapsed = !IsCollapsed;
   if (OnCollapsedChanged != null)
      OnCollapsedChanged(this);
}

步骤 6

现在我们的控件几乎准备好了,但有一个问题,当我们折叠框时,左对齐的控件仍然可见,就像这样。

step51.png

可以通过设置此控件内所有控件的可见属性来解决此问题,但对于最初隐藏的控件呢?它们也会通过这种方法显示出来,因此解决此问题的方案是维护控件的可见状态,就像这样。

private List<control> _visiblectrls = new List<control>();
public bool IsCollapsed
{
   get { return _iscollapsed; }
   set
   {
      _iscollapsed = value;
      if (!value)
      {                    
         Width = ActualWidth;
      }
      else
         Width = CollapseWidth;
      foreach (Control c in _visiblectrls)
      {
         c.Visible = !value;
      }        
      Invalidate();
   }
}
protected override void OnLayout(LayoutEventArgs levent)
{
   if (_visiblectrls.Count == 0)
   {
      foreach (Control c in Controls)
      {
         if (c.Visible)
            _visiblectrls.Add(c);
      }
   }
   base.OnLayout(levent);
}
</control>

现在查看最终结果
step52.png

隐藏的按钮最初是隐藏的。

step53.png

step54.png

这就是我们工作的结束。非常感谢阅读这篇文章。

历史

第一个版本 2012年1月24日。
© . All rights reserved.