|
||
|
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
|
|
|