Drawing Your Own Interface
If you need to create an interface that is different from any of the controls you currently have access to, you can always turn to drawing your own interface. When developing business applications, the standard set of controls are usually sufficient. Sometimes though, doing your own graphics is the only way to get the results you want. Consider the following control; I wanted to draw a series of messages into a window (tip-of-the-day type of information) and have each new message slowly fade-in over the top of the previous one. I could have implemented this using a label that I slowly changed the ForeColor on, but I plan on adding images and other aspects to the message in the future, so drawing my own control gives me the most flexibility.
To get started on any custom drawn control, create a new class that inherits from System.Windows.Forms.Control, and then override OnPaint to add your own graphics code. There's a bit more code needed in order to track the current message and to track the current stage in the animation. The result is a message block that fades in and fades out as it moves through a list of messages to display. Listing 8.6 shows the OnPaint routine where the drawing occurs. This routine handles drawing each phase of the animation from stop to go.
Listing 8.6. Writing Your Own OnPaint Routine Is the Key to Drawing Your Own Control
Public Enum AnimationStage
fadingIn
stayingPut
fadingOut
End Enum
Public Class MessageDisplayer
Inherits System.Windows.Forms.Control
Const ANIMATION_STEPS As Integer = 10
Dim currentStage As AnimationStage _
= AnimationStage.fadingIn
Dim m_Messages As String() _
= {"No Messages", "No Really"}
Dim m_currentMessage As Integer = 0
Dim m_previousMessage As Integer = 0
Dim m_interval As Integer = 1
Dim m_currentStep As Integer = 0
Dim WithEvents m_timer As Timer
Private Sub m_timer_Tick( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles m_timer.Tick
If Me.m_currentStep < Me.ANIMATION_STEPS Then
Me.m_currentStep += 1
Me.Invalidate()
Else
Me.m_currentStep = 0
Select Case Me.currentStage
Case AnimationStage.fadingIn
Me.currentStage = AnimationStage.stayingPut
Case AnimationStage.fadingOut
If Me.m_currentMessage < _
Me.m_Messages.Length - 1 Then
Me.m_currentMessage += 1
Else
Me.m_currentMessage = 0
End If
Me.currentStage = AnimationStage.fadingIn
Case AnimationStage.stayingPut
Me.currentStage = AnimationStage.fadingOut
End Select
Me.Invalidate()
End If
End Sub
Protected Overrides Sub OnPaint( _
ByVal e As PaintEventArgs)
Dim alpha As Integer
Select Case Me.currentStage
Case AnimationStage.stayingPut
e.Graphics.Clear(Me.BackColor)
alpha = 255
Case AnimationStage.fadingIn
e.Graphics.Clear(Me.BackColor)
alpha = (256 ANIMATION_STEPS) _
* Me.m_currentStep
Case AnimationStage.fadingOut
e.Graphics.Clear(Me.BackColor)
alpha = 255 - ((256 ANIMATION_STEPS) _
* Me.m_currentStep)
End Select
Debug.WriteLine(alpha)
Dim fColor As Color _
= Color.FromArgb(alpha, Me.ForeColor)
Dim message As String = Me.m_Messages( _
m_currentMessage)
Dim layoutRect As Drawing.RectangleF
layoutRect = New Drawing.RectangleF( _
0, 0, Me.Width, Me.Height)
layoutRect.Inflate(-10, -10)
Dim messageBrush As Brush _
= New SolidBrush(fColor)
Dim myFormat As New _
StringFormat(StringFormatFlags.LineLimit)
myFormat.Trimming = _
StringTrimming.EllipsisWord
myFormat.Alignment = _
StringAlignment.Center
e.Graphics.SmoothingMode = _
Drawing2D.SmoothingMode.AntiAlias
e.Graphics.DrawString( _
message, Me.Font, _
messageBrush, layoutRect, myFormat)
End Sub
End Class
|
To reduce flicker when handling your own graphics, there are some useful control level settings you can configure. The first three settings (shown in Listing 8.7) produce the least amount of flicker possible. Because this text is being wrapped and sized to fit the available space, it will need to be redrawn whenever the size of the control changes, which is handled by the fourth setting ResizeRedraw.
Listing 8.7. These Control-Level Settings Can Help to Prevent Flicker and to Ensure that the Control Redraws as Needed
Public Sub New()
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.DoubleBuffer, True)
Me.SetStyle(ControlStyles.ResizeRedraw, True)
SetupTimer()
End Sub
|
The call to SetupTimer in Listing 8.7 is one of the many small details of this sample code that is not shown in the in-book listings. Download this chapter's code to see the full set of code for this and all the other controls covered here.