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

圆角按钮控件 - 揭秘 DrawArc

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (19投票s)

2015 年 7 月 18 日

CPOL

6分钟阅读

viewsIcon

36600

downloadIcon

1419

本文介绍了 RoundedButton 控件,并描述了 .Net 的 DrawArc 方法,该方法是编程论坛上许多问题的焦点。

引言 目录

本文介绍了 RoundedButton 控件。它是为“已知颜色调色板工具”的修订版开发的。本文不仅描述了 RoundedButton 控件的工作原理,还讨论了 .Net Graphics.DrawArc 方法,该方法是编程论坛上许多问题的焦点。

本文中呈现的所有图形最初都显示为缩略图。这样读者就可以点击图片,用阅读器默认的图形显示程序打开它们。本文中的所有图像都是 PNG 格式。

在以下讨论中,由开发人员指定的属性将以 BoldMixedCase 文本显示;软件内部使用的变量将以 italicized_lowercase 文本显示。

背景 目录

在 .Net 中,椭圆是绘制椭圆、圆和弧线的基础。然而,正如可以预料的那样,微软对图形表面进行了一些修改。在左侧的图中,( 0, 0 ) 是图形表面(例如,窗口窗体或控件原点)的左上角。

在正常世界(即非 .Net)中,x 坐标值向右增加;y 坐标值向上增加。在 .Net 中,x 坐标值向右增加;但 y 坐标值向下增加。

此外,在正常世界中,角度从 x 轴逆时针测量,通常用弧度表示。在 .Net 中,角度从 x 轴顺时针测量,并根据调用的方法以弧度或度表示。

 

回想一下解析几何中的椭圆。它的 定义

一个封闭的曲线,其中从两个点(焦点)到曲线上任意一点的距离之和是一个常数。

圆是椭圆的一个特例,其中长轴和短轴相等。当这种情况发生时,两个焦点重合于中心,我们将这两个轴称为圆的直径。

 

在 .Net 中,椭圆根据图形的左上角、宽度和高度来定义。

左侧的图描绘了 .Net 的视图。要绘制椭圆,需要调用 Graphics.DrawEllipse 方法。

 
    public void DrawEllipse ( Pen pen,
                              int x,
                              int y,
                              int width,
                              int height )

椭圆图形的颜色和粗细由 DrawEllipse 调用中提供的 Pen 的颜色和宽度属性设置。如果需要绘制圆而不是椭圆,则将 widthheight 设置相等。

绘制弧线几乎与绘制椭圆完全相同,只是增加了两个参数:起始角度和扫描角度。

在左侧的图中,绘制了一个圆弧,从 180° 开始,顺时针扩展 90° 扫描角度。要实现这一点,我们调用 Graphics.DrawArc 方法。

 
     public void DrawArc ( Pen pen,
                           int x,
                           int y,
                           int diameter,   // width
                           int diameter,   // height
                           int start_angle,
                           int sweep_angle )

因为我们想要一个圆弧,所以将 widthheight 设置相等(为圆的直径)。

注意 DrawArc 和 DrawEllipse 之间的相似之处。如果从 DrawArc 方法的参数中移除 start_anglesweep_angle,那么我们就得到了 DrawEllipse 方法的参数。因此,DrawArc 可以看作是绘制与 DrawEllipse 相同的图形,但只显示从 start_angle 开始并扩展 sweep_angle 的部分。

RoundedButton 控件 目录

构造 目录

考虑左侧的图形。为说明起见,RoundedButton 控件的轮廓以黑色绘制;四个圆弧以红色绘制;四个绿色线条是调用 Graphics.CloseAllFigures 方法的结果;橙色线条是每个圆弧的高度;蓝色线条是每个圆弧的宽度;黑色点是每个圆弧的原点。请注意,橙色和蓝色线条的长度相等,构成了绘制圆弧的圆的直径。

要绘制由红色和绿色线条组成的图形,我们使用 GraphicsPath 。GraphicsPath 由 rounded_rectangle_path 方法创建。

 
    // ************************************ rounded_rectangle_path

    /// <summary>
    /// computes the GraphicsPath of a rounded rectangle
    /// </summary>
    /// <param name="x">
    /// x coordinate of the upper left corner of the rectangle
    /// </param>
    /// <param name="y">
    /// y coordinate of the upper left corner of the rectangle
    /// </param>
    /// <param name="width">
    /// width of the rectangle
    /// </param>
    /// <param name="height">
    /// height of the rectangle
    /// </param>
    /// <param name="radius">
    /// radius of the circle that defines the rounded corner
    /// </param>
    /// <returns>
    /// the GraphicsPath that defines the rounded rectangle
    /// </returns>
    GraphicsPath rounded_rectangle_path ( int x,
                                          int y,
                                          int width,
                                          int height )
        {
        int           local_diameter = 0;
        GraphicsPath  path = new GraphicsPath ( );
                                    // take into account right and 
                                    // bottom sides
        x += 1;
        y += 1;
        width -= 1;
        height -= 1;

        if ( Diameter == 0 )
            {
            local_diameter = Math.Min ( width, height );
            local_diameter = 
                round ( ( float ) local_diameter / 2.0F );
            }
        else 
            {
            local_diameter = Diameter;
            }

        path.StartFigure ( );
                                    // bottom right
        path.AddArc ( ( x + width - local_diameter ), 
                      ( y + height - local_diameter ), 
                      local_diameter, 
                      local_diameter, 
                      0.0F, 
                      90.0F );
                                    // bottom left
        path.AddArc ( x, 
                      ( y + height - local_diameter ), 
                      local_diameter, 
                      local_diameter, 
                      90.0F, 
                      90.0F );
                                    // top left
        path.AddArc ( x, 
                      y, 
                      local_diameter, 
                      local_diameter, 
                      180.0F, 
                      90.0F );
                                    // top right
        path.AddArc ( ( x + width - local_diameter ), 
                      y, 
                      local_diameter, 
                      local_diameter, 
                      270.0F, 
                      90.0F );
                                    // join all arcs together
        path.CloseAllFigures ( );

        return ( path );
        }

local_diameter 的值来自 Diameter 属性。在开发此控件的过程中,尝试了各种直径值。最终“最佳”值基于以下公式

    Min ( width, height ) / 2.0F )

如果 Diameter 属性的值为零,则控件将从此公式计算 local_diameter 的值。

rounded_rectangle_path 返回一个 GraphicsPath。根据 MSDN 文档,GraphicsPath 可用于

绘制形状轮廓、填充形状内部以及创建剪辑区域。

rounded_rectangle_path 返回的 GraphicsPath 将用于创建剪辑区域,将所有绘制限制在控件的所需区域内,并绘制 RoundedButton 控件的轮廓。 rounded_rectangle_pathdraw_border_graphic 调用。

    // *************************************** draw_border_graphic

    /// <summary>
    /// creates the border_graphic GraphicsBuffer by performing the 
    /// following actions:
    /// 
    ///     1.  create a GraphicsPath
    ///     2.  establish the clipping region that limits graphics 
    ///         operations to within the GraphicsPath
    ///     3.  draws the outline of the GraphicsPath to the 
    ///         border_graphic GraphicsBuffer
    ///     4.  deletes the GraphicsPath
    ///     5.  optionally draws the outlines of the circles that 
    ///         were used to create the rounded corners to the 
    ///         border_graphic GraphicsBuffer 
    /// </summary>
    void draw_border_graphic ( )
        {
        GraphicsPath    path = null;
                                    // ORDER IS IMPORTANT!!!

                                    // compute the path
        path = rounded_rectangle_path ( 0,
                                        0,
                                        this.Width, 
                                        this.Height );
                                    // set clipping region
        this.Region = new Region ( path );
                                    // draw the border
        border_graphic.Graphic.DrawPath ( 
            new Pen ( BorderColor,
                      BorderThickness ),
            path );
        path.Dispose ( );
                                    // draw circles
        if ( DrawRoundingCircles )
            {
            draw_rounding_circles ( border_graphic.Graphic,
                                    0,
                                    0,
                                    this.Width, 
                                    this.Height );
            }
        }

GraphicsBuffer 是一个提供无闪烁绘图表面的类。 draw_rounding_circles 方法与 rounded_rectangle_path 方法非常相似,不同之处在于 Graphics.DrawEllipse 替换了 GraphicsPath.AddArc

属性 目录

以下是 RoundedButton 控件独有的属性。 RoundedButton 继承自 Button 类,因此此处不讨论其属性。

属性 类型 默认值 目的
BorderColor Color Color.White 设置/获取按钮边框的颜色
BorderThickness int 1 设置/获取按钮边框的粗细
Diameter int 0 设置/获取圆角直径。如果 Diameter 的值为零,则控件将计算圆角直径的值为 Min ( width, height ) / 2.0F )。
DrawRoundingCircles bool false 指定是否绘制圆角圆

圆角按钮演示 目录

RoundedButton Demonstration

下载包含圆角按钮对话框演示应用程序,该应用程序允许用户确定 RoundedButton 控件在各种情况下的外观。

Button GroupBox 允许用户修改 RoundedButton 的大小和颜色。Width 和 Height NumericUpDown 控件的范围都限制在 [ 22, 272 ]。它们都影响 RoundedButton 的 Width 和 Height 属性。BackColor 和 ForeColor 按钮允许用户设置相应的 RoundedButton 属性。请注意,ForeColor 用于绘制 RoundedButton 面上的任何文本。Use a contrasting ForeColor CheckBox 决定 RoundedButton 文本是使用用户指定的 ForeColor 还是与 RoundedButton BackColor 相对比的黑色或白色。

Border GroupBox 允许用户设置 RoundedButton 的边框属性。Diameter NumericUpDown 控件指定圆角圆的直径。Diameter 的下限为零;上限为 Min ( width, height ) / 2。随着高度和宽度的变化,Diameter 的上限也会相应变化。Thickness NumericUpDown 控件指定边框的粗细。其范围限制在 [ 0, 100 ]。Color 按钮设置边框的颜色。Draw Rounding Circles CheckBox 仅用于说明目的。在生产环境中应将其保持未选中状态。

 

对于有兴趣了解对比色如何确定的读者,使用了以下代码。

    // ***************************************** contrasting_color

    /// <summary>
    /// determines the color (black or white) that contrasts with 
    /// the given color
    /// </summary>
    /// <param name="color">
    /// color for which to find its contrasting color
    /// </param>
    /// <returns>
    /// the contrasting color (black or white)
    /// </returns>
    /// <reference>
    /// http://stackoverflow.com/questions/1855884/
    ///     determine-font-color-based-on-background-color
    /// </reference>
    Color contrasting_color ( Color color )
        {
        double  a;
        int     d = 0;

        a = 1.0 - ( ( 0.299 * color.R + 
                      0.587 * color.G + 
                      0.114 * color.B ) / 255.0 );

        if ( a < 0.5 )
            {
            d = 0;                  // bright colors - black font
            }
        else
            {
            d = 255;                // dark colors - white font
            }

        return ( Color.FromArgb ( d, d, d ) );
        }

结论 目录

本文介绍了 RoundedButton 控件,并详细描述了 .Net DrawArc 方法的工作原理。

参考 目录

开发环境 目录

RoundedButton 控件是在以下环境中开发的

Microsoft Windows 7 Professional Service Pack 1
Microsoft Visual Studio 2008 Professional
Microsoft .Net Framework Version 3.5 SP1
Microsoft Visual C# 2008

历史 目录

  • 2015 年 7 月 18 日 - 原始文章。
© . All rights reserved.