|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionYou can learn a lot about .NET Windows Forms programming by building a custom control. There are several books on the topic, but you'll soon find yourself reaching for Google to answer questions about Forms, GDI+, and Visual Studio you don't even know how to ask. When you find answers, they will be frustratingly incomplete. What better way to learn? That's how it went for me when I wrote Aqua Button. Since this was a learning project and I wasn't bound by practicality, I set out to build a button that looks and feels like push buttons in Apple Mac OS X. Apple's user interface is called Aqua®, and it's alive with transparent, colorful controls. Aqua buttons and Windows buttons have some things in common, but they also have several rather large differences:
So, it's safe to say that AquaButton won't satisfy Windows interface guidelines. But it may help you make that leap from using Windows Forms controls to designing and building your own custom controls. Drawing the 3D buttonAquaButton has a 3D look with text shadows, button shadows, and highlights. While it may be possible to recreate this look with GDI+ in OnPaint, I took the easier path and created the button bitmaps in Photoshop. I used PixelJerk's Photoshop action to create my initial source bitmap, then removed the background layer and merged the remaining layers to make the button partially transparent. I sliced that bitmap into three segments: a left end cap (left.png), a right end cap (right.png), and a single-pixel column from the middle (fill.png). Each time AquaButton paints itself, it uses DrawImage to quickly draw the two end caps, and FillRectangle to fill in the body. This means that you can set the width of AquaButton, but not the height. If you need taller or thinner buttons, replace the source bitmaps with your own, then set the Aqua buttons are aqua-colored when they are the default button (specified with the protected ImageAttributes iaDefault, iaNormal;
protected ColorMatrix cmDefault, cmNormal;
I setup the image attributes and color matrices in InitializeGraphics. I use the // lighten the default image by reducing opacity
cmDefault = new ColorMatrix();
cmDefault.Matrix33 = 0.5f; iaDefault = new ImageAttributes();
iaDefault.SetColorMatrix( cmDefault, ColorMatrixFlag.Default,
ColorAdjustType.Bitmap );
and I use the // 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 );
Now all I have to do is draw the three button bitmaps (left.png, right.png, and fill.png) using 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( );
}
That's all there is to drawing AquaButton in it's basic states. With just a little more code, we can extend this to make AquaButton pulse. Making the button pulseAqua buttons pulse with a glow that seems to originate inside the button. I considered using a GIF-like animation with a sequence of bitmaps showing the button in several intermediate states of illumination, controlled by a timer. While this would allow me to create realistic lighting in Photoshop, I would need many intermediate bitmaps to create a fluid animation. I decided instead to use Gamma Correction, a simpler technique that sacrifices some lighting quality. Earlier I showed you how I lightened up the default and normal button images using a ColorMatrix. I did this so that I can use gamma correction to draw lighter (1.8 gamma) and darker (0.7 gamma) versions of the image using gamma correction. Change This is how it works. AquaButton starts a timer to invalidate itself every 70 milliseconds ( 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;
Now all we have to do is update the ImageAttributes with the new gamma value and repaint the button. In Aqua, only the default button pulses, so I just need to update iaDefault.SetGamma( gamma, ColorAdjustType.Bitmap );
Invalidate( );
Update( );
Supporting Visual DesignAquaButton exposes several properties to support the Visual Studio designer:
AquaButton also shadows several properties from System.Windows.Forms.Control:
I also wrote a custom designer, Extending AquaButtonI've already mentioned a few ways to customize AquaButton. If you're looking for a learning project, here are a few ideas. AquaButton looks like an Aqua button, but behaves differently when it comes to selection. You could extend AquaButton to implement these missing behaviors to make AquaButton more faithful to the Aqua look and feel:
Or you could go the other way and make AquaButton behave more like .NET Windows Forms buttons:
I'm interested to see how you extend AquaButton. I would be happy to post your enhancements here and give you credit. References
CreditsAquaButton is an independent creation and has not been authorized, sponsored, or otherwise approved by Apple Computer, Inc. Aqua is a trademark of Apple Computer, Inc. Revisions
|
||||||||||||||||||||||