The UserControl object has a rectangular shape by default. However, a custom control need not be rectangular. It’s possible to create irregularly shaped forms, too, but unlike irregularly shaped controls, an irregularly shaped form is still quite uncommon. Irregularly shaped controls are used in fancy interfaces, and they usually react to movement of the mouse. (They may change color when the mouse is over them or when they’re clicked, for example.)
To change the default shape of a custom control, you must use the Region object, which is another graphics-related object that specifies a closed area. You can even use Bezier curves to make highly unusual and smooth shapes for your controls. In this section, we’ll do something less ambitious: We’ll create controls with the shape of an ellipse, as shown in the upper half of Figure 8.7. To follow the code presented in this section, open the NonRectangularControl project (download the project files here); the custom control is the RoundControl Windows Control Library project, and Form1 is the test form for the control.
You can turn any control to any shape you like by creating the appropriate Region object and then applying it to the Region property of the control. This must take place from within the control’s Paint event. Listing 8.12 shows the statements that change the shape of the control.
Listing 8.12: Creating a Nonrectangular Control
Protected Sub PaintControl(ByVal sender As Object, _ ByVal pe As PaintEventArgs) Handles Me.Paint pe.Graphics.TextRenderingHint = _ Drawing.Text.TextRenderingHint.AntiAlias Dim roundPath As New GraphicsPath() Dim R As New Rectangle(0, 0, Me.Width, Me.Height) roundPath.AddEllipse(R) Me.Region = New Region(roundPath) End SubCode language: PHP (php)
First, we retrieve the Graphics object of the UserControl; then we create a GraphicsPath object, the roundPath variable, and add an ellipse to it. The ellipse is based on the enclosing rectangle.
The R object is used temporarily to specify the ellipse. The new path is then used to create a Region object, which is assigned to the Region property of the UserControl object. This gives our control the shape of an ellipse.
Listing 8.12 shows the statements that specify the control’s shape. In addition, you must insert a few statements to display the control’s caption, which is specified by the control’s Caption property. The caption is rendered normally in yellow color, unless the mouse is hovering over the control, in which case the same caption is rendered with a 3D effect. You already know how to achieve this effect: by printing the same string twice in different colors with a slight displacement between them.
Listing 8.13 shows the code in the control’s MouseEnter and MouseLeave events. When the mouse enters the control’s area (this is detected by the control automatically — you won’t have to write a single line of code for it), the currentState variable is set to State.Active (State is an enumeration in the project’s code), and the control’s caption appears in raised type. In the control’s MouseLeave event handler, the currentState variable is reset to State.Inactive and the control’s caption appears in regular font. In addition, each time the mouse enters and leaves the control, the MouseInsideControl and MouseOutsideControl custom events are fired.
Listing 8.13: RoundButton Control’s MouseEnter and MouseLeave Events
Private Sub RoundButton MouseEnter(...) _ Handles MyBase.MouseEnter currentState = State.Active Me.Refresh() RaiseEvent MouseInsideButton(Me) End Sub Private Sub RoundButton MouseLeave(...) _ Handles MyBase.MouseLeave currentState = State.Inactive Me.Refresh() RaiseEvent MouseOusideButton(Me) End SubCode language: PHP (php)
These two events set up the appropriate variables, and the drawing of the control takes place in the Paint event’s handler, which is shown in Listing 8.14.
Listing 8.14: RoundButton Control’s Paint Event Handler
Protected Sub PaintControl(ByVal sender As Object, _ ByVal pe As PaintEventArgs) Handles Me.Paint pe.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias Dim roundPath As New GraphicsPath() Dim R As New Rectangle(0, 0, Me.Width, Me.Height) roundPath.AddEllipse(R) Me.Region = New Region(roundPath) Dim Path As New GraphicsPath Path.AddEllipse(R) Dim grBrush As LinearGradientBrush If currentState = State.Active Then grBrush = New LinearGradientBrush(New Point(0, 0), _ New Point(R.Width, R.Height), Color.DarkGray, Color.White) Else grBrush = New LinearGradientBrush(New Point(R.Width, R.Height), _ New Point(0, 0), Color.DarkGray, Color.White) End If pe.Graphics.FillPath(grBrush, Path) Dim X As Integer = (Me.Width - pe.Graphics.MeasureString( _ currentCaption, currentFont).Width) / 2 Dim Y As Integer = (Me.Height - pe.Graphics.MeasureString( _ currentCaption, currentFont).Height) / 2 If currentState = State.Active Then pe.Graphics.DrawString(currentCaption, currentFont, Brushes.Black, X, Y) pe.Graphics.DrawString(currentCaption, currentFont, _ New SolidBrush(currentCaptionColor), X - 1, Y - 1) Else pe.Graphics.DrawString(currentCaption, currentFont, _ New SolidBrush(currentCaptionColor), X, Y) End If End SubCode language: PHP (php)
The OnPaint method uses graphics methods to fill the control with a gradient and center the string on the control. They’re the same methods we used in the example of the user-drawn control earlier in this chapter. The drawing methods are discussed in detail in Chapter “Drawing and Painting with Visual Basic 2008”.
The code uses the currentState variable, which can take on two values: Active and Inactive. These two values are members of the State enumeration, which is shown next:
Public Enum State Active Inactive End EnumCode language: PHP (php)
The test form of the project shows how the RoundButton control behaves on a form. You can use the techniques described in this section to make a series of round controls for a totally different feel and look.
The Play button’s Click event handler in the test form changes the caption of the button according to the control’s current state. It also disables the other RoundButton controls on the test form. Here’s the Click event handler of the Play button:
Private Sub bttnplay_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles bttnPlay.Click If bttnPlay.Caption = "Play" Then Label1.Text = "Playing..." bttnPlay.Caption = "STOP" bttnPlay.Color = Color.Red bttnRecord.Enabled = False bttnClose.Enabled = False Else Label1.Text = "Stoped Playing" bttnPlay.Caption = "Play" bttnPlay.Color = Color.Yellow bttnRecord.Enabled = True bttnClose.Enabled = True End If End SubCode language: PHP (php)
In Chapter “Drawing and Painting”, you’ll learn more about shapes and paths, and you may wish to experiment with other oddly shaped controls. How about a progress indicator control that looks like a thermometer? Or a button with an LED that turns on or changes color when you press the button, like the buttons in the lower half of Figure 8.7? The two rectangular buttons are instances of the LEDButton custom control, which is included in the NonRectangularControl project. Open the project in Visual Studio and examine the code that renders the rectangular buttons emulating an LED in the left corner of the control.