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

The Grouper - 自定义分组框控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (126投票s)

2005年12月19日

7分钟阅读

viewsIcon

352500

downloadIcon

13260

Grouper 是一个特殊的、圆角且完全可自定义的分组框控件。该控件可以绘制边框、阴影,并具有渐变和实心背景、自定义文本和自定义图标等其他功能。

Sample Image - grouperscreenshot.jpg

引言

Grouper 是一个特殊的、圆角且完全可自定义的分组框控件。该控件可以绘制边框、阴影,并具有渐变和实心背景、自定义文本和自定义图标等其他功能。该控件的目的是设计一个外观更好的 WinForms 分组框控件。我采用了( KISS 原则,即保持简单愚蠢)的理念,设计了一个紧密结合、可重用的控件,希望大家都会喜欢。该控件仍处于 Beta 版本,但似乎很稳定。

使用代码

在添加控件之前,必须将引用(CodeVendor.Controls)添加到您的项目中。

using CodeVendor.Controls;

可以使用以下代码将 Grouper 控件添加到您的项目中。将此代码添加到您的类中。

private CodeVendor.Controls.Grouper Grouper1;
this.Grouper1 = new CodeVendor.Controls.Grouper();

自定义控件属性

以下是 Grouper 控件属性的列表,这些属性会改变控件的物理外观。

属性名称 类型 描述
控件属性
BackColor Color 此功能将绘制控件的背景颜色。
BackgroundColor Color 此功能将更改分组控件的颜色。此颜色还可以与 BackgroundGradientColor 结合使用以进行渐变绘制。
BackgroundGradientColor Color 此功能可与 BackgroundColor 结合使用以创建渐变背景。
BackgroundGradientMode GroupBoxGradientMode 此功能可启用背景渐变绘制。
BorderColor Color 此功能允许您更改控件边框的颜色。
BorderThickness Float 此功能允许您设置控件的边框大小。
CustomGroupBoxColor Color 如果将 Paint GroupBox 设置为 true,此功能将以指定的颜色绘制组标题背景。
GroupImage Image 此功能可以在组标题栏添加一个 16 x 16 的图像。
GroupTitle 字符串 此功能将为控件添加一个组标题。
Paint GroupBox bool 此功能将以 Custom GroupBoxColor 绘制组标题背景。
RoundCorners int 此功能将使控件的边角圆润。
ShadowColor Color 此功能将更改控件的阴影颜色。
ShadowControl bool 此功能允许您启用控件阴影。
ShadowThickness int 此功能将更改阴影边框的大小。
属性名称 类型 描述

Grouper 源代码

我开始编写这个控件是因为我厌倦了普通旧的 Windows GroupBox。我的 WinForms 应用程序的分组框需要一个具有圆边和渐变背景的更新的 XP 外观。像大多数编写控件的开发人员一样,我对自己说,我应该自定义绘制原始的 GroupBox 控件,还是重新发明轮子,从头开始?我选择了重新发明轮子 :)。

重新发明轮子使我能够无闪烁地绘制控件并根据我的喜好进行自定义。花费时间从头开始制作一个控件似乎漫长而徒劳,但能够自豪地知道代码是我编写的,并且如果有什么东西坏了,那是因为我写的东西而不是别人的,这是完全值得的。

要开始设计控件,我必须弄清楚原始 GroupBox 在设计模式下如何作为其他控件的容器。下面是将任何控件变成设计时容器控件的代码。

[Designer("System.Windows.Forms.Design.ParentControlDesigner, 
           System.Design", typeof(IDesigner))]

此代码必须添加在您的类顶部,作为您控件的属性,如下所示:

[Designer("System.Windows.Forms.Design.ParentControlDesigner, 
                        System.Design", typeof(IDesigner))]
public class Grouper : System.Windows.Forms.UserControl
{
}

将属性添加到控件后,该控件将自动变成设计时容器控件。哇! 非常简单且自包含,微软确实考虑到了所有事情。我找到此资源的 URL 是:Microsoft 支持

由于添加这些强大的功能几乎没有花费任何时间,我决定将更多时间投入到原始 GroupBox 所提供的基础上,也就是设计。

无闪烁绘制控件

如果您正在绘制自己的自定义控件,您应该考虑“无闪烁绘制的最佳方法是什么?”。要无闪烁地绘制,您需要一种方法来双缓冲您的绘图或图形。微软通过控件样式为我们提供了一个选项。下面的代码演示了如何为您的控件添加双缓冲。

//Set the control styles----------------------------------
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
//--------------------------------------------------------

此代码应添加到您的类的构造函数中。所有样式对于创建无闪烁控件都是必需的。如果您想制作自定义双缓冲,可以通过将所有图形绘制到屏幕外位图,然后一次性将位图绘制到屏幕上来创建相同的效果。这将几乎产生与上述代码相同的效果。另外,请注意此样式

this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);

此样式允许任何控件支持透明颜色。默认情况下,用户控件不支持透明颜色。因此,您必须启用此选项才能为您的背景色属性添加透明度。

让我们继续绘制我们的自定义绘制控件。要绘制用户控件,您需要重写两个方法中的一个,具体取决于所需的效果。这两个方法是 OnPaint()OnPaintBackground()。以下是重写这些方法的代码。

protected override void OnPaint(PaintEventArgs e)
{
  base.OnPaint (e);
}
        
protected override void OnPaintBackground(PaintEventArgs pevent)
{
  base.OnPaintBackground (pevent);
}

对于 Grouper,我们将使用 OnPaint() 方法。代码如下所示:

/// <SUMMARY>Overrides the OnPaint method to paint control.</SUMMARY>
protected override void OnPaint(PaintEventArgs e)
{
  PaintBack(e.Graphics);
  PaintGroupText(e.Graphics);
}

我从 PaintEventArgs 接收控件的 Graphics 对象,并将其传递给我的两个自定义方法 PaintBack()PaintGroupText()

创建圆角控件

那么,您想创建一个圆角的自定义用户控件吗?使用 GDI+ 库创建圆角控件非常简单。让我们深入研究代码,了解它是如何工作的。

/// <SUMMARY>This method will paint the control.</SUMMARY>
private void PaintBack(System.Drawing.Graphics g)
{
  //Set Graphics smoothing mode to Anit-Alias-- 
  g.SmoothingMode = SmoothingMode.AntiAlias;
  //-------------------------------------------

  //Declare Variables------------------
  int ArcWidth = this.RoundCorners * 2;
  int ArcHeight = this.RoundCorners * 2;
  int ArcX1 = 0;
  int ArcX2 = (this.ShadowControl) ? (this.Width - (ArcWidth + 1))- 
               this.ShadowThickness : this.Width - (ArcWidth + 1);
  int ArcY1 = 10;
  int ArcY2 = (this.ShadowControl) ? (this.Height - (ArcHeight + 1))-
               this.ShadowThickness : this.Height - (ArcHeight + 1);
  System.Drawing.Drawing2D.GraphicsPath path = 
                        new System.Drawing.Drawing2D.GraphicsPath();
  System.Drawing.Brush BorderBrush = new SolidBrush(this.BorderColor);
  System.Drawing.Pen BorderPen = new Pen(BorderBrush, this.BorderThickness);
  System.Drawing.Drawing2D.LinearGradientBrush BackgroundGradientBrush = null;
  System.Drawing.Brush BackgroundBrush = new SolidBrush(this.BackgroundColor);
  System.Drawing.SolidBrush ShadowBrush = null;
  System.Drawing.Drawing2D.GraphicsPath ShadowPath  = null;
  //-----------------------------------

  //Check if shadow is needed----------
  if(this.ShadowControl)
  {
    ShadowBrush = new SolidBrush(this.ShadowColor);
    ShadowPath = new System.Drawing.Drawing2D.GraphicsPath();
    ShadowPath.AddArc(ArcX1+this.ShadowThickness, ArcY1+this.ShadowThickness, 
                      ArcWidth, ArcHeight, 180, GroupBoxConstants.SweepAngle);
                      // Top Left
    ShadowPath.AddArc(ArcX2+this.ShadowThickness, ArcY1+this.ShadowThickness, 
                      ArcWidth, ArcHeight, 270, GroupBoxConstants.SweepAngle);
                      //Top Right
    ShadowPath.AddArc(ArcX2+this.ShadowThickness, ArcY2+this.ShadowThickness, 
                      ArcWidth, ArcHeight, 360, GroupBoxConstants.SweepAngle);
                      //Bottom Right
    ShadowPath.AddArc(ArcX1+this.ShadowThickness, ArcY2+this.ShadowThickness, 
                      ArcWidth, ArcHeight, 90, GroupBoxConstants.SweepAngle);
                      //Bottom Left
    ShadowPath.CloseAllFigures();

    //Paint Rounded Rectangle------------
    g.FillPath(ShadowBrush, ShadowPath);
    //-----------------------------------
  }
  //-----------------------------------

  //Create Rounded Rectangle Path------
  path.AddArc(ArcX1, ArcY1, ArcWidth, ArcHeight, 180, 
              GroupBoxConstants.SweepAngle); // Top Left
  path.AddArc(ArcX2, ArcY1, ArcWidth, ArcHeight, 270, 
              GroupBoxConstants.SweepAngle); //Top Right
  path.AddArc(ArcX2, ArcY2, ArcWidth, ArcHeight, 360, 
              GroupBoxConstants.SweepAngle); //Bottom Right
  path.AddArc(ArcX1, ArcY2, ArcWidth, ArcHeight, 90, 
              GroupBoxConstants.SweepAngle); //Bottom Left
  path.CloseAllFigures(); 
  //-----------------------------------

  //Check if Gradient Mode is enabled--
  if(this.BackgroundGradientMode==GroupBoxGradientMode.None)
  {
    //Paint Rounded Rectangle------------
    g.FillPath(BackgroundBrush, path);
    //-----------------------------------
  }
  else
  {
    BackgroundGradientBrush = 
      new LinearGradientBrush(new Rectangle(0,0,this.Width,this.Height), 
      this.BackgroundColor, this.BackgroundGradientColor, 
      (LinearGradientMode)this.BackgroundGradientMode);
                
    //Paint Rounded Rectangle------------
    g.FillPath(BackgroundGradientBrush, path);
    //-----------------------------------
  }
  //-----------------------------------

  //Paint Borded-----------------------
  g.DrawPath(BorderPen, path);
  //-----------------------------------

  //Destroy Graphic Objects------------
  if(path!=null){path.Dispose();}
  if(BorderBrush!=null){BorderBrush.Dispose();}
  if(BorderPen!=null){BorderPen.Dispose();}
  if(BackgroundGradientBrush!=null){BackgroundGradientBrush.Dispose();}
  if(BackgroundBrush!=null){BackgroundBrush.Dispose();}
  if(ShadowBrush!=null){ShadowBrush.Dispose();}
  if(ShadowPath!=null){ShadowPath.Dispose();}
  //-----------------------------------
}

上面的 PaintBack() 方法使用 System.Drawing.Drawing2D 类中的 GraphicsPath() 方法来创建四个圆弧,按左上角、右上角、右下角和左下角的顺序开始。圆弧需要设置为左上角:180,右上角:270,右下角:360,左下角:90,扫描角度为 90。GraphicsPathCloseAllFigures() 方法会为您关闭所有这些圆弧,从而创建圆角矩形路径。剩下要做的就是使用 FillPath() 方法填充您的路径。您还可以选择实心画笔或线性渐变画笔作为分组框的背景。要为控件绘制边框,我使用图形库的 DrawPath() 方法。通过将相同的路径传递给此方法并使用图形画笔,您可以创建一条遵循相同路径的线条边框。

处置图形对象!

许多程序员(包括我自己)都忘记 dispose() 图形画笔、颜色、路径等。未关闭的非托管资源会导致内存泄漏,并最终使您的机器停滞不前。请确保在使用图形库时遵循这些通用规则。

  1. 通过调用它们的 dispose() 方法来处置所有图形对象。
  2. 在处置它们之前,请确保检查您的图形对象不为 null
  3. 如果您在多个方法之间拥有通用的图形对象,请考虑将它们声明为类变量或常量。
  4. 不要依赖垃圾回收器自动收集您使用的图形资源。

需要改进的控件

  • 组标题栏在增大字体大小时无法正确调整大小。
  • 控件需要能够缩小到最小高度和宽度,而不会绘制重叠。
  • 组标题栏需要能够接受大于 16 x 16 的图标,并相应地扩展大小。
  • 某些字体在用 measurestring() 方法测量时,报告的精确尺寸存在问题。
  • 提供最小化分组框的选项,以及一个用户按钮来最小化。
  • 已完成背景填充画笔 (HatchBrush).
  • 带有预设选项的自定义控件设计器。
  • 斜角设计器。
  • 任何方向的阴影。
  • 半渐变背景。
  • 具有多层绘制和混合板的 TypeEditor。

HatchBrushTypeEditor

我认为有必要设计一个自定义的填充样式类型编辑器。这将作为 Grouper 的自定义着色和样式选项之一。

HatchBrushTypeEditor

请告知您对该控件 1.0 版本希望看到的任何改进或功能。

历史

  • 版本 1.0 HatchBrushTypeEditor 已完成 - 2006 年 1 月 23 日。
  • 版本 1.0a Beta - 2005 年 12 月 17 日。

屏幕截图

Sample screenshot

奖项

使用、复制、分发和修改的条款和条件

本代码由版权持有者和贡献者“按原样”提供,并且不提供任何明示或暗示的保证,包括但不限于对适销性和特定用途适用性的暗示保证。在任何情况下,版权所有者或贡献者均不对因使用本代码而产生的任何直接、间接、附带、特殊、惩戒性或后果性损害(包括但不限于购买替代商品或服务;使用、数据或利润损失;或业务中断)承担任何责任,无论是由合同、严格责任或侵权行为(包括疏忽或其他)引起的,即使已告知可能发生此类损害。

© . All rights reserved.