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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (50投票s)

2002 年 8 月 27 日

7分钟阅读

viewsIcon

473472

downloadIcon

5318

自定义按钮示例,可帮助您编写自己的自定义控件。

Sample Image - AquaButton.png

引言

通过构建自定义控件,您可以深入了解 .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 为每种状态声明 ImageAttributeColorMatrix 变量。

   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 );

现在,我只需要使用 iaDefaultiaNormal(作为 DrawButtonState 的参数)绘制三个按钮位图(left.pngright.pngfill.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 伽马)版本的图像。如果这些看起来太亮或太暗,请更改 PulseGammaMinPulseGammaMax

它是这样工作的。AquaButton 启动一个计时器,每 70 毫秒(PulseInterval)使自身失效。在每次计时器滴答时,AquaButton 使用伽马校正使自身绘制得越来越亮或越来越暗,过渡几乎无缝。我的第一个尝试看起来更像是闪烁而不是脉动——按钮几乎立即从亮变暗。因此,我添加了逻辑来减慢光照变化,使其接近最小或最大伽马。如果您对它的外观不满意,请调整 PulseGammaShiftPulseGammaReductionThresholdPulseGammaShiftReduction 常量。这是 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 labelAquaButton 将在设计时自动调整自身大小。

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 没有意义的属性:AllowDropBackColorBackgroundImageContextMenuFlatStyleForeColorImageImageAlignImageIndexImageListSizeTextAlign。我这样做是为了简化可视化设计,但我没有将其隐藏以阻止调用者在代码中设置它们。

扩展 AquaButton

我已经提到了几种自定义 AquaButton 的方法。如果您正在寻找学习项目,这里有一些想法。

AquaButton 看起来像 Aqua 按钮,但在选择方面的行为不同。您可以扩展 AquaButton 来实现这些缺失的行为,使 AquaButton 更忠实于 Aqua 的外观和感觉。

  • Aqua 按钮不在 Tab 顺序中,但 AquaButton 将此决定留给您。
  • Aqua 按钮不获得焦点,因此默认按钮始终是默认按钮,并且即使另一个控件具有焦点也会脉动。AquaButton 继承了 .NET 按钮的选择行为,这意味着您可以通过 Tab 或鼠标单击来使另一个按钮成为默认按钮。

或者,您可以走另一条路,使 AquaButton 的行为更像 .NET Windows Forms 按钮。

  • 添加焦点提示
  • 使选定的按钮脉动(即使它不是默认按钮)。
  • 允许用户设置按钮的高度(一位读者提出了一个解决方案——请参阅本文的反馈)。

我很想看看您如何扩展 AquaButton。我很乐意在此发布您的增强功能并给予您署名。

参考文献

  1. CodeProject,www.codeproject.com
  2. GotDotNet,www.gotdotnet.com
  3. Microsoft .NET Windows Forms 新闻组,microsoft.public.dotnet.windowsformsmicrosoft.public.dotnet.windowsforms.controls
  4. Microsoft Developer Network,msdn.microsoft.com
  5. Apple Computer,Aqua,www.apple.com/macosx/technologies/aqua.html
  6. Apple Computer,Aqua Human Interface Guidelines,developer.apple.com/techpubs/macosx/Essentials/AquaHIGuidelines/
  7. Charles Petzold,Programming Microsoft Windows with C#,Microsoft Press,2002
  8. Richard L. Weeks,.NET Windows Forms Custom Controls,SAMS Publishing,2002
  9. Ted Faison,Component-Based Development with Visual C#,M&T Books,2002
  10. Andrew Troelsen,C# and the .NET Platform,Apress,2001

致谢

AquaButton 是一项独立创作,未经 Apple Computer, Inc. 的授权、赞助或以其他方式认可。Aqua 是 Apple Computer, Inc. 的商标。

修订

  • 2002 年 9 月 12 日 - 读者指出,该按钮未将 Click 事件转发给窗体。问题在于我在鼠标跟踪代码中做了太多事情,没有给基类处理事件的机会。在试验了 Button 事件后,我重写了鼠标跟踪逻辑,使其变得简单得多。

许可证

本文没有明确的许可,但可能包含文章文本或下载文件本身的用法条款。如有疑问,请通过下方的讨论区联系作者。可以 此处 找到作者可能使用的许可证列表。

© . All rights reserved.