在用户界面设计中,经常需要在有限的空间内展示尽可能多的信息。为了满足这一需求,可折叠控件成为了一个理想的选择。本文将详细介绍如何使用C#创建一个垂直折叠的控件,以便用户可以在不滚动的情况下查看更多信息。
本指南将逐步指导完成创建可折叠控件的过程。在阅读过程中,将看到一些重复的函数和属性声明,其中新的更改将以粗体标记,而新的声明则以正常字体显示。
首先,将在组框的左上角绘制一个图像。需要声明一些变量,以确保控件最初是展开的,并定义图像矩形的边界。
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);
}
完成这一步后,将看到初步的结果。
在这一步中,将向组框添加垂直文本。需要定义一个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);
}
完成这一步后,将看到添加了垂直文本的结果。
在这一步中,将完成两项任务:(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函数,以捕获是否在图像内执行了点击操作,如果是,则调用CollapsedChange函数并切换其当前状态。
private void CollapsedChanged() {
IsCollapsed = !IsCollapsed;
}
protected override void OnMouseUp(MouseEventArgs e) {
if (ButtonRect.Contains(e.Location)) {
CollapsedChanged();
}
base.OnMouseUp(e);
}
完成这一步后,将看到可折叠功能的结果。
在这一步中,将对控件进行美化。如果看到文本下方有一条线,看起来不太美观,可以通过重写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);
}
完成这一步后,控件将更加美观。
在这一步中,将添加事件支持。当折叠更改事件触发时,可以在父窗体中执行操作。
public delegate void CollapseChangeEventHandler(object sender);
public event CollapseChangeEventHandler OnCollapsedChanged;
private void CollapsedChanged() {
IsCollapsed = !IsCollapsed;
if (OnCollapsedChanged != null)
OnCollapsedChanged(this);
}
完成这一步后,控件将能够响应折叠更改事件。
现在控件几乎已经准备好了,但当折叠盒子时,左对齐的控件仍然可见。这个问题可以通过设置此控件内所有控件的可见属性来解决,但对于那些最初隐藏的控件,这种方法也会使它们显示出来。因此,需要维护控件的可见状态。
private List _visiblectrls = new List();
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);
}