圆形按钮窗口控制-不断减少的圆圈

介绍 不久前,我试图找到一个不错的圆形按钮控件。我找不到一本,所以按照由来已久的传统,我决定自己写一本。我“几乎”完成了它,但由于各种原因,它被归入了“以后再看”的类别。在它独特的风格中,“later”终于出现了,并且装备了我闪亮的新Microsoft Visual c# 2005 Express Edition,我决定试着完成它。 虽然我自己说过,但我觉得这些扣子看起来不错——你得自己判断!它们“实际”看起来更好,而不是本文中的jpeg。 背景 在我寻找圆形按钮控件的过程中,我看到了几篇文章(包括伟大的Chris Maunder自己写的一篇),对我和我小小的大脑来说,这些文章似乎包含了太多复杂的数学问题。另外,我一直在学习c#中的图形,并对groovy的一些东西进行了大量的试验,比如PathGradientBrush,从Bob Powell这个非常优秀的站点获得了很多灵感。可能是碰巧,我忘记了是怎么做到的,我偶然发现了一个想法,用线性渐变画笔和路径渐变画笔在不断减少的圆上叠加,来创建一个过得去的3-D按钮。以下图片说明了这一点: 悬停光标以获取描述。 它是如何工作的 实际上,把很多圆一个叠一个地放在另一个上面就是它的工作原理。该控件派生自Button类,并覆盖了OnPaint方法,所有绘图都在该方法中完成。我添加了一些新属性: RecessDepth -按钮被设置回包含表面的斜面高度的距离-按钮顶部的“外部”斜面深度的大小-“内部”斜面穹顶的大小-按钮是否有一个“圆角”顶部 通过使用适当的属性修饰它们,这些属性都被添加到属性面板的按钮外观类别中。另外,我为RecessDepth属性编写了一个自定义下拉UITypeEditor。我不会管理这个没有克里斯卖的优秀作品Windows窗体在c#编程,我高度推荐它,我不会试图解释UITypeEditor是如何工作的,因为它是覆盖样本在线这一章讨论了设计时IDE集成的各个方面(虽然我也做自己的实际的书!)。 注意:为了使ToolboxBitmap属性正常工作,我必须添加这个虚拟类,这也是Bob Powell在本文中建议的:ToolboxBitmap。 隐藏,复制Code

internal class resfinder
{
    // Trick from Bob Powell
}
.
.
.
.
[Description("Round (Elliptical) Button Control"),
    ToolboxBitmap(typeof(resfinder), "RoundButton.Images.RoundButton.bmp")]
    public class RoundButton : System.Windows.Forms.Button

值得注意的代码部分 这是overridden OnPaint方法。没什么特别令人兴奋的,但我把它包括进来作为参考。 隐藏,收缩,复制Code

protected override void OnPaint(PaintEventArgs e)
{
    buttonColor = this.BackColor;
    edgeColor1 = ControlPaint.Light(buttonColor);
    edgeColor2 = ControlPaint.Dark(buttonColor);

    Graphics g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;

    Rectangle buttonRect = this.ClientRectangle;
    edgeWidth = GetEdgeWidth(buttonRect);

    FillBackground(g, buttonRect);

    if (RecessDepth > 0)
    {
        DrawRecess(ref g, ref buttonRect);
    }

    DrawEdges(g, ref buttonRect);

    ShrinkShape(ref g, ref buttonRect, edgeWidth);

    DrawButton(g, buttonRect);

    DrawText(g, buttonRect);

    SetClickableRegion();

}

接下来是DrawRecess方法,它创建按钮被设置到表单表面的错觉。混合对象允许你指定在矩形的哪一点,以及在多大程度上,两种颜色在线性渐变画笔混合。我通过反复试验得出了这些参数,直到我认为它们是正确的,所以它们纯粹是主观的。ControlPaint。黑暗和ControlPaint。光线在这里非常有用,因为它们创建了父背景颜色的浅阴影和深阴影。当然,这是假设我们想要创造的错觉是一个由坚实的彩色材料组成的形状,而不是一个仍然是灰色的,但被涂上了不同颜色的材料。如果这是您喜欢的,那么只需更改父级即可。背景色Color.FromKnownColor (KnownColor.Control)。 我发现这里有趣的事情是“使用第二个较小的矩形…”部分。我在BuildGraphicsPath方法中再次使用了相同的技术,尽管它创建了更平滑的曲线,但我不知道它是如何或为什么工作的。但是,我们中有多少人真正知道电视是如何工作的? 隐藏,复制Code

protected virtual void DrawRecess(ref Graphics g, ref Rectangle recessRect)
{

    LinearGradientBrush recessBrush = new LinearGradientBrush(recessRect,
                                      ControlPaint.Dark(Parent.BackColor),
                                      ControlPaint.LightLight(Parent.BackColor),
                                      GetLightAngle(Angle.Up));
    // Blend colours for realism
    Blend recessBlend = new Blend();
    recessBlend.Positions = new float[] {0.0f,.2f,.4f,.6f,.8f,1.0f};
    recessBlend.Factors = new float[] {.2f,.2f,.4f,.4f,1f,1f};
    recessBrush.Blend = recessBlend;

    // Using this second smaller rectangle
    // smooths the edges - don't know why...?
    Rectangle rect2 = recessRect;
    ShrinkShape(ref g, ref rect2, 1);
    FillShape(g, recessBrush, rect2);

    ShrinkShape(ref g, ref recessRect, recessDepth); //orig

}

您将注意到大量的ShrinkShape(ref g, ref edgeRect, 1);源代码中的语句。这就是创造“不断递减的圆”的方法。我使用了一个ref参数,这样问题中的矩形就会变得越来越小。 为了绘制穹顶,我只需要在DrawButton方法中使用这段代码。cColor的默认值是白色,因此如果我们想要一个圆顶顶部,我们将CenterColor设置为白色,并根据按钮的大小计算一个中心点。 隐藏,复制Code

pgb.CenterColor = buttonColor;

if (dome)
{
    pgb.CenterColor = cColor;
    pgb.CenterPoint =
        new PointF(buttonRect.X + buttonRect.Width / 8 + buttonPressOffset,
                   buttonRect.Y + buttonRect.Height / 8 + buttonPressOffset);
}

FillShape(g, pgb, buttonRect);

在按钮上绘制文本使用DrawText方法完成,如下所示。它使用从基按钮类继承的字体和前面板属性。如果按钮的高度超过其宽度的两倍,我使用我的VerticalString类来编写垂直文本。VerticalString是前面一篇CodeProject文章的主题,为了完整起见,我在项目下载中包含了源代码。我还必须确保在可能的情况下,按钮文本保持在按钮的范围内。作为这个过程的一部分,我必须转换文本fr的对齐方式om内容对齐到字符串对齐。最后,我检查按钮是否被禁用,如果是,我“灰色”文本。 隐藏,收缩,复制Code

protected void DrawText(Graphics g, Rectangle textRect)
{
    labelStrFmt = new StringFormat();
    labelBrush = new SolidBrush(this.ForeColor);
    labelFont = this.Font;    // Get the caller-specified font

    vs = new VerticalString();
    vs.TextSpread = .75;

    // Check for tall button, and write text vertically if necessary
    bool verticalText = false;
    if (textRect.Height > textRect.Width * 2)
    {
        verticalText = true;
    }

    // Convert the text alignment from
    // ContentAlignment to StringAlignment
    labelStrFmt.Alignment = ConvertToHorAlign(this.TextAlign);
    labelStrFmt.LineAlignment = ConvertToVertAlign(this.TextAlign);

    // If horizontal text is not horizontally centred,
    // or vertical text is not vertically centred,
    // shrink the rectangle so that the text doesn't stray outside the ellipse
    if ((!verticalText & (labelStrFmt.LineAlignment != StringAlignment.Center)) |
        (verticalText & (labelStrFmt.Alignment != StringAlignment.Center)))
    {
        textRect.Inflate(-(int)(textRect.Width/7.5), 
                         -(int)(textRect.Height/7.5));
    }

    textRect.Offset(buttonPressOffset, buttonPressOffset);
    // Apply the offset if we've been clicked

    // If button is not enabled, "grey out" the text.
    if (!this.Enabled)
    {
        //Write the white "embossing effect" text at an offset
        textRect.Offset(1, 1);
        labelBrush.Color = ControlPaint.LightLight(buttonColor);
        WriteString(verticalText, g, textRect);

        //Restore original text pos, and set text colour to grey.
        textRect.Offset(-1, -1);
        labelBrush.Color = Color.Gray;
    }

    //Write the text
    WriteString(verticalText, g, textRect);
}

按钮被按下的错觉是通过下面两个小方法实现的。当用户按下按钮时,buttonPressOffset变量被设置为1,并且虚拟光的角度被改变,使按钮的左上方变暗,右下方变亮,从而产生按钮已经退隐到表单表面的印象。当按钮被释放时,值恢复正常。 隐藏,复制Code

protected void buttonDown()
{
    lightAngle = Angle.Down;
    buttonPressOffset = 1;
    this.Invalidate();
}

protected void buttonUp()
{
    lightAngle = Angle.Up;
    buttonPressOffset = 0;
    this.Invalidate();
}

最后,几点… 圆形按钮控件只支持FlatStyle.Standard。我为FlatStyle编写了一些代码。平,FlatStyle。弹窗,它工作得很好,但我对代码和结果都不是很满意,所以我把它拿掉了。 如果你看一下源代码,你可能会注意到一个叫做Overrideable shape-specific methods的区域,其中包含了一些乏味的方法,比如: 隐藏,复制Code

protected virtual void AddShape(GraphicsPath gpath, Rectangle rect)
{
    gpath.AddEllipse(rect);
}


protected virtual void DrawShape(Graphics g, Pen pen, Rectangle rect)
{
    g.DrawEllipse(pen, rect);
}

为什么不直接调用AddEllipse,而不是AddShape?我还编写了其他一些类,比如TriangleButton和DiamondButton,它们显然没有使用AddEllipse或任何与椭圆有关的东西,所以我希望能够在代码中覆盖其他形状的方法。我没有在这里包括其他形状,部分原因是我认为一些代码有点混乱,需要更多的修改,而不是我现在所能做的,而且坦白地说,它们看起来不如圆形的好! 要在另一个项目中使用按钮,只需添加对RoundButton的引用。,并且圆形按钮图标应该出现在工具箱中。(你可能需要使用工具->选择工具箱项手动添加。) 这就是本文的结尾。我希望你觉得它有趣,喜欢的按钮! 本文转载于:http://www.diyabc.com/frontweb/news689.html

posted @ 2020-08-08 00:13  Dincat  阅读(319)  评论(0编辑  收藏  举报