|
|
||
|
Drawing Borders and Edges using the API DrawEdge FunctionSimplify standard drawing tasks using the System's built-in functionality
The DrawEdge function is a useful function provided by the Win32 API. It can draw a variety of the edge styles used to draw buttons, frames and borders around controls and forms. The source code for this article shows you how to:
DrawEdge is declared as follows:
Private Declare Function DrawEdge Lib "user32" ( _
ByVal hDC As Long, _
qrc As RECT, _
ByVal edge As Long, _
ByVal grfFlags As Long) As Long
The parameters to it are the hDC to draw onto, the rectangle in which to draw the edge, then two parameters to determine how the edge is drawn:
Once you have drawn an edge, you then normally want to draw any other parts of the control within the edge rather than overdrawing it. The Inner and Outer parts of the border occupy 1 pixel (or TwipsPerPixel twips) each, so it isn't too difficult to work out what the client area within the border is. The EvaluateSize() function below shows how this is done. This function also demonstrates how to make drawing easier by preventing anything from being drawn over the border. This is done by selecting a 'clipping region' which excludes the border into the device you are drawing into. When the clipping region is in place, Windows will only draw within the clipping region, all other drawing simply disappears. To select a clipping region, first create it with a region creating API function. The sample below uses CreateRectRgn, however you can create regions of any shape you like (see my article "A window that's star-shaped, circular or tank-shaped for details on creating more arbitrary shaped regions"). Once the region is created, you can make it the clipping region by using SelectClipRgn. To remove the region again, simply select a region handle of zero into the control. The code below provides a simple UserControl which allows all the DrawEdge options to be exercised, and also demonstrates clipping to prevent drawing over the edge which has been drawn. The code in the download expands on the details below by drawing some text into the control to prove that the edge isn't overwritten. It also demonstrates a couple of other techniques:
' ==================================================================
' EDGE Drawing:
' ==================================================================
Public Enum EDEDBorderStyle
BDR_RAISEDOUTER = 1
BDR_SUNKENOUTER = 2
BDR_RAISEDINNER = 4
BDR_SUNKENINNER = 8
BDR_BUTTON = BDR_RAISEDINNER Or BDR_RAISEDOUTER
BDR_CONTROL = BDR_SUNKENINNER Or BDR_SUNKENOUTER
BDR_THINBUTTON = BDR_RAISEDOUTER
BDR_THINCONTROL = BDR_SUNKENOUTER
BDR_ETCHRAISE = BDR_RAISEDOUTER Or BDR_SUNKENINNER
BDR_ETCHINSET = BDR_SUNKENOUTER Or BDR_RAISEDINNER
BDR_ALL = BDR_BUTTON Or BDR_CONTROL
End Enum
Public Enum EDEDBorderParts
Bf_left = 1
Bf_Top = 2
Bf_right = 4
Bf_bottom = 8
BF_TOPLEFT = Bf_left Or Bf_Top
Bf_BOTTOMRIGHT = Bf_right Or Bf_bottom
BF_RECT = Bf_left Or Bf_Top Or Bf_right Or Bf_bottom
BF_MIDDLE = &H800
BF_SOFT = &H1000
BF_ADJUST = &H2000
BF_FLAT = &H4000
BF_MONO = &H8000&
BF_ALL = BF_RECT Or BF_MIDDLE Or BF_SOFT Or BF_ADJUST Or BF_FLAT Or BF_MONO
End Enum
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function DrawEdge Lib "user32" ( _
ByVal hDC As Long, qrc As RECT, _
ByVal edge As Long, ByVal grfFlags As Long) As Long
' ==================================================================
' Clipping functions:
' ==================================================================
Private Declare Function SelectClipRgn Lib "gdi32" ( _
ByVal hDC As Long, ByVal hRgn As Long) As Long
Private Declare Function CreateRectRgn Lib "gdi32" ( _
ByVal x1 As Long, ByVal y1 As Long, _
ByVal x2 As Long, ByVal y2 As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" ( _
ByVal hObject As Long) As Long
' ==================================================================
' Client Region:
' ==================================================================
Private m_lLeft As Long
Private m_lTop As Long
Private m_lWidth As Long
Private m_lHeight As Long
Private m_hRgn As Long
' ==================================================================
' Border styles:
' ==================================================================
Private m_lBorderStyle As Long
Private m_lFlags As Long
Public Property Get BackColor() As OLE_COLOR
BackColor = UserControl.BackColor
End Property
Public Property Let BackColor(ByVal eColor As OLE_COLOR)
UserControl.BackColor = eColor
PropertyChanged "BackColor"
Draw
End Property
Public Property Let BorderPart(ByVal ePart As EDEDBorderParts, _
ByVal bState As Boolean)
If (bState) Then
m_lFlags = m_lFlags Or ePart
Else
m_lFlags = m_lFlags And Not ePart
End If
Draw
PropertyChanged "BorderPart"
End Property
Public Property Get BorderPart(ByVal ePart As EDEDBorderParts) As Boolean
BorderPart = BitSet(m_lFlags, ePart)
End Property
Public Property Let BorderStyle(ByVal eStyle As EDEDBorderStyle, _
ByVal bState As Boolean)
If (bState) Then
m_lBorderStyle = m_lBorderStyle Or eStyle
Else
m_lBorderStyle = m_lBorderStyle And Not eStyle
End If
Draw
PropertyChanged "BorderStyle"
End Property
Public Property Get BorderStyle(ByVal eStyle As EDEDBorderStyle) As Boolean
BorderStyle = BitSet(m_lBorderStyle, eStyle)
End Property
Private Function BitSet(ByVal lIn As Long, ByVal lBits As Long) As Boolean
BitSet = ((lIn And lBits) = lBits)
End Function
Private Sub EvaluateSize()
Dim lBSize As Long
m_lWidth = UserControl.ScaleWidth \ Screen.TwipsPerPixelX
m_lHeight = UserControl.ScaleHeight \ Screen.TwipsPerPixelY
m_lLeft = 0
m_lTop = 0
If (m_lBorderStyle >= 4) Then
lBSize = 2
ElseIf (m_lBorderStyle >= 1) Then
lBSize = 1
End If
If (BitSet(m_lFlags, Bf_left)) Then
m_lLeft = lBSize
m_lWidth = m_lWidth - lBSize
End If
If (BitSet(m_lFlags, Bf_Top)) Then
m_lTop = lBSize
m_lHeight = m_lHeight - lBSize
End If
If (BitSet(m_lFlags, Bf_right)) Then
m_lWidth = m_lWidth - lBSize
End If
If (BitSet(m_lFlags, Bf_bottom)) Then
m_lHeight = m_lHeight - lBSize
End If
DeleteObject m_hRgn
m_hRgn = CreateRectRgn(m_lLeft, m_lTop, m_lWidth + m_lLeft, _
m_lHeight + m_lTop)
SelectClipRgn UserControl.hDC, m_hRgn
End Sub
Public Sub Draw()
Dim tR As RECT
' Clear control:
If (m_hRgn <> 0) Then
SelectClipRgn UserControl.hDC, 0
End If
UserControl.Cls
' Draw the edge:
tR.Right = UserControl.ScaleWidth \ Screen.TwipsPerPixelX
tR.Bottom = UserControl.ScaleHeight \ Screen.TwipsPerPixelY
DrawEdge UserControl.hDC, tR, m_lBorderStyle, m_lFlags
' Draw in the 'client area'
EvaluateSize
UserControl.Refresh
End Sub
Private Sub UserControl_Initialize()
m_lBorderStyle = BDR_CONTROL
m_lFlags = BF_RECT
End Sub
Private Sub UserControl_Paint()
Draw
End Sub
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
m_lBorderStyle = PropBag.ReadProperty("BorderStyle", BDR_CONTROL)
m_lFlags = PropBag.ReadProperty("BorderPart", BF_RECT)
BackColor = PropBag.ReadProperty("BackColor", vbButtonFace)
End Sub
Private Sub UserControl_Resize()
Draw
End Sub
Private Sub UserControl_Terminate()
If (m_hRgn <> 0) Then
SelectClipRgn UserControl.hDC, 0
DeleteObject m_hRgn
End If
End Sub
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "BorderStyle", m_lBorderStyle, BDR_CONTROL
PropBag.WriteProperty "BorderPart", m_lFlags, BF_RECT
PropBag.WriteProperty "BackColor", BackColor, vbButtonFace
End Sub
|
|
|
|
||