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

本文介绍了一种自定义的圆形按钮控件的创建过程,作者在找不到满意的圆形按钮控件后,决定自行开发。控件使用C#编程,通过叠加多个圆形并应用渐变效果,实现了3D视觉效果。文章详细解释了控件的工作原理,包括如何通过调整圆的大小和位置来创建凹陷感,以及如何使用线性渐变和路径渐变画笔来增强真实感。
下载demo project and source - 83.4 KB

介绍

不久前,我试图找到一个不错的圆形按钮控件。我找不到一本,所以按照由来已久的传统,我决定自己写一本。我“几乎”完成了它,但由于各种原因,它被归入了“以后再看”的类别。在它独特的风格中,“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。

隐藏,复制Codeinternal 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方法。没什么特别令人兴奋的,但我把它包括进来作为参考。

隐藏,收缩,复制Codeprotected 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方法中再次使用了相同的技术,尽管它创建了更平滑的曲线,但我不知道它是如何或为什么工作的。但是,我们中有多少人真正知道电视是如何工作的?

隐藏,复制Codeprotected 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设置为白色,并根据按钮的大小计算一个中心点。

隐藏,复制Codepgb.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内容对齐到字符串对齐。最后,我检查按钮是否被禁用,如果是,我“灰色”文本。

隐藏,收缩,复制Codeprotected 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,并且虚拟光的角度被改变,使按钮的左上方变暗,右下方变亮,从而产生按钮已经退隐到表单表面的印象。当按钮被释放时,值恢复正常。

隐藏,复制Codeprotected 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的区域,其中包含了一些乏味的方法,比如:

隐藏,复制Codeprotected 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值