AquaButton:一款具有Mac OS X外观的自定义按钮示例






4.65/5 (50投票s)
2002 年 8 月 27 日
7分钟阅读

473472

5318
自定义按钮示例,
引言
通过构建自定义控件,您可以深入了解 .NET Windows Forms 编程。关于该主题的书籍有很多,但您很快就会发现需要谷歌搜索来解答关于 Forms、GDI+ 和 Visual Studio 的问题,而这些问题您甚至不知道如何提问。即使找到了答案,它们也可能不完整,令人沮丧。
还有什么比这更好的学习方式呢?
这就是我在编写 Aqua Button 时遇到的情况。由于这是一个学习项目,并且我不受实际应用的约束,所以我着手构建一个看起来和感觉都像 Apple Mac OS X 中的按钮的按钮。苹果的用户界面被称为 Aqua®,它充满生机,拥有透明、多彩的控件。Aqua 按钮和 Windows 按钮有一些共同点,但它们之间也存在一些相当大的差异。
- Aqua 按钮在作为默认按钮时会脉动
- Aqua 按钮不在 Tab 顺序中
- Aqua 按钮通常没有键盘等效项
- Aqua 按钮单击时不会按下——它们会改变颜色
因此,可以肯定地说 AquaButton
不会满足 Windows 界面指南。但它可以帮助您从使用 Windows Forms 控件过渡到设计和构建自己的自定义控件。
绘制 3D 按钮
AquaButton
具有 3D 外观,带有文本阴影、按钮阴影和高光。虽然可能可以通过 OnPaint
中的 GDI+ 重新创建此外观,但我选择了更简单的方法,即在 Photoshop 中创建按钮位图。我使用了 PixelJerk 的 Photoshop 动作来创建我的初始源位图,然后删除了背景图层并将剩余图层合并,使按钮部分透明。我将该位图切成三个部分:左侧端盖 (left.png)、右侧端盖 (right.png) 和中间的单像素列 (fill.png)。每次 AquaButton
绘制自身时,它都会使用 DrawImage
快速绘制两个端盖,并使用 FillRectangle
填充主体。这意味着您可以设置 AquaButton
的宽度,但不能设置高度。
如果您需要更高或更窄的按钮,请替换源位图,然后将 ButtonHeight
类常量设置为位图的高度。如果您的位图有阴影,请设置 ButtonShadowOffset
类常量,使其指定从按钮底部到图像底部的距离。AquaButton
使用最后一个常量将标签居中放置在按钮上。
Aqua 按钮在作为默认按钮时(通过 Form.AcceptButton
属性指定)为 Aqua 色。非默认按钮以灰度绘制。我不需要管理单独的源位图来以灰度绘制按钮——使用 GDI+ ImageAttributes
以灰度绘制按钮很容易。AquaButton
为每种状态声明 ImageAttribute
和 ColorMatrix
变量。
protected ImageAttributes iaDefault, iaNormal;
protected ColorMatrix cmDefault, cmNormal;
我在 InitializeGraphics
中设置了图像属性和颜色矩阵。我使用 cmDefault
颜色矩阵来使按钮变亮(稍后在解释我如何使用伽马校正来模拟脉动效果时您就会明白原因)。
// lighten the default image by reducing opacity
cmDefault = new ColorMatrix();
cmDefault.Matrix33 = 0.5f;
iaDefault = new ImageAttributes();
iaDefault.SetColorMatrix( cmDefault, ColorMatrixFlag.Default,
ColorAdjustType.Bitmap );
我使用 cmNormal
颜色矩阵来去饱和和提亮图像。
// desaturate the normal image
cmNormal = new ColorMatrix();
cmNormal.Matrix00 = 1/3f;
cmNormal.Matrix01 = 1/3f;
cmNormal.Matrix02 = 1/3f;
cmNormal.Matrix10 = 1/3f;
cmNormal.Matrix11 = 1/3f;
cmNormal.Matrix12 = 1/3f;
cmNormal.Matrix20 = 1/3f;
cmNormal.Matrix21 = 1/3f;
cmNormal.Matrix22 = 1/3f;
// lighten the normal image by reducing opacity
cmNormal.Matrix33 = 0.5f;
iaNormal = new ImageAttributes();
iaNormal.SetColorMatrix( cmNormal, ColorMatrixFlag.Default,
ColorAdjustType.Bitmap );
现在,我只需要使用 iaDefault
或 iaNormal
(作为 DrawButtonState
的参数)绘制三个按钮位图(left.png、right.png 和 fill.png)。
protected virtual void DrawButtonState (Graphics g, ImageAttributes ia)
{
TextureBrush tb;
// Draw the left end cap
g.DrawImage( imgLeft, rcLeft, 0, 0, imgLeft.Width, imgLeft.Height,
GraphicsUnit.Pixel, ia );
// Draw the right end cap
g.DrawImage( imgRight, rcRight, 0, 0, imgRight.Width, imgRight.Height,
GraphicsUnit.Pixel, ia );
// Draw the middle
tb = new TextureBrush( imgFill, new Rectangle( 0, 0,
imgFill.Width, imgFill.Height ), ia );
tb.WrapMode = WrapMode.Tile;
g.FillRectangle ( tb, imgLeft.Width, 0,
this.Width - (imgLeft.Width + imgRight.Width),
imgFill.Height);
tb.Dispose( );
}
这就是绘制 AquaButton
的基本状态。只需稍加修改,我们就可以扩展它以使其能够脉动。
制作按钮脉动
Aqua 按钮会发出似乎源自按钮内部的光晕。我曾考虑使用类似 GIF 的动画,其中包含显示按钮在不同亮度状态下的位图序列,并由计时器控制。虽然这可以让我用 Photoshop 创建逼真的光照效果,但我需要许多中间位图才能创建流畅的动画。
相反,我决定使用伽马校正,这是一种更简单的技术,会牺牲一些光照质量。之前,我向您展示了如何使用 ColorMatrix
提亮默认按钮和普通按钮图像。我这样做是为了能够使用伽马校正来绘制更亮(1.8 伽马)和更暗(0.7 伽马)版本的图像。如果这些看起来太亮或太暗,请更改 PulseGammaMin
和 PulseGammaMax
。
它是这样工作的。AquaButton
启动一个计时器,每 70 毫秒(PulseInterval
)使自身失效。在每次计时器滴答时,AquaButton
使用伽马校正使自身绘制得越来越亮或越来越暗,过渡几乎无缝。我的第一个尝试看起来更像是闪烁而不是脉动——按钮几乎立即从亮变暗。因此,我添加了逻辑来减慢光照变化,使其接近最小或最大伽马。如果您对它的外观不满意,请调整 PulseGammaShift
、PulseGammaReductionThreshold
和 PulseGammaShiftReduction
常量。这是 TimeOnTick
中的伽马移位逻辑。
if ((gamma - Button.PulseGammaMin < Button.PulseGammaReductionThreshold ) ||
(Button.PulseGammaMax - gamma < Button.PulseGammaReductionThreshold ))
gamma += gammaShift * Button.PulseGammaShiftReduction;
else
gamma += gammaShift;
if ( gamma <= Button.PulseGammaMin || gamma >= Button.PulseGammaMax )
gammaShift = -gammaShift;
现在,我只需要使用新的伽马值更新 ImageAttributes
并重新绘制按钮。在 Aqua 中,只有默认按钮才会脉动,所以我只需要更新 iaDefault
。
iaDefault.SetGamma( gamma, ColorAdjustType.Bitmap );
Invalidate( );
Update( );
支持可视化设计
AquaButton
公开了几个属性来支持 Visual Studio 设计器。
Pulse
- 确定AquaButton
作为默认按钮时是否脉动。SizeToLabel
- 确定AquaButton
是否根据其标签自动设置宽度。将其设置为true
,然后设置button label
。AquaButton
将在设计时自动调整自身大小。
AquaButton
还隐藏了 System.Windows.Forms.Control
的一些属性。
Size
-AquaButton
具有固定高度,因此不允许您在 Visual Studio 属性网格中设置Size
(包括Height
)是没有意义的。作为一种解决方案,我通过自定义设计器(见下文)将此属性从 Visual Studio 属性网格中隐藏了。Height
-AquaButton
不公开其Size
属性,因此您需要另一种方法来查看Height
。我隐藏了Control.Height
,并使其在属性网格中可浏览且只读。Width
- 与Height
类似,我隐藏了此属性,并使其在属性网格中可浏览。您可以决定是显式设置width
,还是使用SizeToLabel
自动调整按钮大小。
我还编写了一个自定义设计器 Wildgrape.Aqua.Controls.ButtonDesigner
,以过滤掉对 AquaButton
没有意义的属性:AllowDrop
、BackColor
、BackgroundImage
、ContextMenu
、FlatStyle
、ForeColor
、Image
、ImageAlign
、ImageIndex
、ImageList
、Size
和 TextAlign
。我这样做是为了简化可视化设计,但我没有将其隐藏以阻止调用者在代码中设置它们。
扩展 AquaButton
我已经提到了几种自定义 AquaButton
的方法。如果您正在寻找学习项目,这里有一些想法。
AquaButton
看起来像 Aqua
按钮,但在选择方面的行为不同。您可以扩展 AquaButton
来实现这些缺失的行为,使 AquaButton
更忠实于 Aqua
的外观和感觉。
Aqua
按钮不在 Tab 顺序中,但AquaButton
将此决定留给您。Aqua
按钮不获得焦点,因此默认按钮始终是默认按钮,并且即使另一个控件具有焦点也会脉动。AquaButton
继承了 .NET 按钮的选择行为,这意味着您可以通过 Tab 或鼠标单击来使另一个按钮成为默认按钮。
或者,您可以走另一条路,使 AquaButton
的行为更像 .NET Windows Forms 按钮。
- 添加焦点提示
- 使选定的按钮脉动(即使它不是默认按钮)。
- 允许用户设置按钮的高度(一位读者提出了一个解决方案——请参阅本文的反馈)。
我很想看看您如何扩展 AquaButton
。我很乐意在此发布您的增强功能并给予您署名。
参考文献
- CodeProject,www.codeproject.com
- GotDotNet,www.gotdotnet.com
- Microsoft .NET Windows Forms 新闻组,microsoft.public.dotnet.windowsforms 和 microsoft.public.dotnet.windowsforms.controls
- Microsoft Developer Network,msdn.microsoft.com
- Apple Computer,Aqua,www.apple.com/macosx/technologies/aqua.html
- Apple Computer,Aqua Human Interface Guidelines,developer.apple.com/techpubs/macosx/Essentials/AquaHIGuidelines/
- Charles Petzold,Programming Microsoft Windows with C#,Microsoft Press,2002
- Richard L. Weeks,.NET Windows Forms Custom Controls,SAMS Publishing,2002
- Ted Faison,Component-Based Development with Visual C#,M&T Books,2002
- Andrew Troelsen,C# and the .NET Platform,Apress,2001
致谢
AquaButton
是一项独立创作,未经 Apple Computer, Inc. 的授权、赞助或以其他方式认可。Aqua 是 Apple Computer, Inc. 的商标。
修订
- 2002 年 9 月 12 日 - 读者指出,该按钮未将
Click
事件转发给窗体。问题在于我在鼠标跟踪代码中做了太多事情,没有给基类处理事件的机会。在试验了Button
事件后,我重写了鼠标跟踪逻辑,使其变得简单得多。
许可证
本文没有明确的许可,但可能包含文章文本或下载文件本身的用法条款。如有疑问,请通过下方的讨论区联系作者。可以 此处 找到作者可能使用的许可证列表。