Create a Shaped 3D Control with GDI+, Page 2
Implementing Control Methods
Listing 3: Custom control methods.
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Data
Imports System.Windows.Forms
Public Class Cube3d
Inherits System.Windows.Forms.Control
#Region " Component Designer generated code "
Public Sub New()
MyBase.New()
SetStyle(ControlStyles.SupportsTransparentBackColor Or _
ControlStyles.ResizeRedraw, True)
InitializeComponent()
FCube = New Cube(CubeX, CubeY, CubeHeight, CubeWidth, _
CubeDepth)
End Sub
#End Region
Private FPen As pen = New pen(Color.Black)
Private FCube As Cube
Protected Overrides Sub OnPaint(ByVal e _
As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
Dim path As GraphicsPath = FCube.GetCube()
e.Graphics.DrawPath(FPen, path)
Me.Region = EnclosingRegion()
End Sub
Public Function EnclosingRegion() As Region
Dim copy As Cube3D = Clone()
copy.ScaleCube(1, 1, 1)
Dim path As GraphicsPath = copy.Cube.GetCube()
path.FillMode = FillMode.Winding
Return New Region(path)
End Function
Public Function Clone() As Cube3d
Dim copy As Cube3D = New Cube3d
copy.SetCubeBounds(CubeX, CubeY, CubeWidth, CubeHeight, _
CubeDepth)
Return copy
End Function
Public Sub ScaleCube(ByVal x As Integer, ByVal y As Integer, _
ByVal z As Integer)
CubeLocation = New Point(CubeX - x, CubeY - y)
CubeWidth += 2 * x
CubeHeight += 2 * y
CubeDepth += 2 * z
End Sub
Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
MyBase.OnResize(e)
ResizeCubeStructure()
End Sub
Public Sub SetCubeBounds(ByVal x As Integer, _
ByVal y As Integer, _ ByVal width As Integer, _
ByVal height As Integer, ByVal depth As Integer)
FCube.Location = New Point(x, y)
FCube.Width = width
FCube.Height = height
FCube.Depth = depth
Invalidate()
End Sub
Private Sub ResizeCubeStructure()
FCube.FillRectangle(Bounds)
Invalidate()
End Sub
' elided intentionally!
The Listing 3 code modifies the constructor (Sub New) and OnPaint, and adds EnclosingRegion, Clone, ScaleCube, OnResize, SetCubeBounds, and ResizeCubeStructure. In the constructor, it adds a call to SetStyle. Passing an Or'd list of enumerated values permits you to customize the control style. For this demonstration, I elected to permit background transparency and enable automatic redrawing when the control is resized. The former permits see-through controls, and the latter ensures that the underlying cube representation adjusts when the control is resized.
For custom controls, the only method you really need to override is the OnPaint method. The OnPaint method is where you give a custom control its unique visual character. Your OnPaint method obtains the GraphicsPath—think array of points—that represents the cube, draws the cube, and then changes the control's region to match that of the cube shape rather than the rectangular control. (Refer to Shaping the Control for more information on using the Region property.)
The EnclosingRegion method plays a little game. Essentially, you obtain a copy of the cube and expand it three-dimensionally by one pixel, providing enough room for the cube outline. You use a copy because you don't want to change the cube itself; you just want a little more room for the clip region—visible area—of the cube. The Clone method creates a new instance, a copy, of the cube itself.
ScaleCube accepts three points and adjusts the location, width, height, and depth. (Multiplying width and height by 2 accounts for the offsetting change to x and y.) As well as permitting the cube to shrink and swell, you use this method to permit enough room to ensure that the cube control's outline is visible along the edges. (Scaling the cube to make room for the visual image is a bit dodgy, as my friend Tony Cowan would say, and could stand some improvement.)
The OnResize method is overridden to ensure that when the control is resized the underlying cube is resized relatively. Without this method, the actual dimensions of the cube could be greater or less than the size of the control, breaking the illusion of the control conforming to the primitive's dimensions.
Finally, SetCubeBounds accounts for the depth dimension and invalidates the control, so that it is redrawn, and ResizeCubeStructure shrinks or grows the cube to fit the control's bounds and again causes the control to be redrawn.
Surfacing Constituent Properties
The 3D cube control is defined by a location and three Cartesian points representing height, width, and depth. While it makes sense to permit the consumer to modify the location and dimensions, permitting design-time modification of other properties probably makes no sense and may be error prone. Listing 4 contains only the properties that you will permit consumers to modify at design time. Simply add the code in Listing 4 to the Cube3D's class code. (The complete Listing is provided in Control Code Listing.)
Listing 4: The public, design-time modifiable properties of the 3D Cube control.
Public Property CubeWidth() As Integer
Get
Return FCube.Width
End Get
Set(ByVal Value As Integer)
FCube.Width = Value
Invalidate()
End Set
End Property
Public Property CubeHeight() As Integer
Get
Return FCube.Height
End Get
Set(ByVal Value As Integer)
FCube.Height = Value
Invalidate()
End Set
End Property
Public Property CubeDepth() As Integer
Get
Return FCube.Depth
End Get
Set(ByVal Value As Integer)
FCube.Depth = Value
Invalidate()
End Set
End Property
Public Property CubeCenter() As Point
Get
Return FCube.Center
End Get
Set(ByVal Value As Point)
FCube.Center = Value
Invalidate()
End Set
End Property
Public Property CubeLocation() As Point
Get
Return FCube.Location
End Get
Set(ByVal Value As Point)
FCube.Location = Value
Invalidate()
End Set
End Property
If you want to change all of the cube's dimensions, call SetCubeBounds. Each of the cube's location and dimensional properties individually causes the cube to be redrawn. These properties, modifiable at design time, are CubeWidth, CubeHeight, CubeDepth, CubeCenter, and CubeLocation. CubeLocation moves the upper-left corner of the cube, and CubeCenter relocates the cube relative to its three-dimensional center. Each of these properties is implemented in terms of the underlying 3D cube structure.
