From what I have seen the DrawFrameControl examples require that the forms
autoredraw is true.
Because the user is interacting with my control I assume I don't want the
autoredraw so I can handle the paint messages.
The code I tried is ->
Dim rButton As RECT
Dim RetVal As Long
Dim MemDC As Long
Dim hBitmap As Long, hOldBitmap As Long
'Create the memory DC
MemDC = CreateCompatibleDC(UserControl.hdc)
'Create a bitmap and select it
hBitmap = CreateCompatibleBitmap(MemDC, 15&, 15&)
hOldBitmap = SelectObject(MemDC, hBitmap)
rButton.Bottom = 15
rButton.Left = 0
rButton.Right = 15
rButton.Top = 0
'Draw Top of Box
If ButtonUp Then
RetVal = DrawFrameControl(MemDC, rButton, DFC_CAPTION, DFCS_CAPTIONMAX)
Else
RetVal = DrawFrameControl(MemDC, rButton, DFC_CAPTION, DFCS_CAPTIONMAX
Or DFCS_PUSHED)
End If
RetVal = BitBlt(UserControl.hdc, 0&, 0&, 15&, 15&, MemDC, 0&, 0&, vbSrcCopy)
'Restore the memory DC and delete the bitmap and the DC
RetVal = SelectObject(MemDC, hOldBitmap)
RetVal = DeleteObject(hBitmap)
RetVal = DeleteDC(MemDC)
This code produces an image but it contains garbage.
--
Mark Twombley
diaganos(numeric)one at shaw dot ca
eg. bo...@nowhere.com
"Say No to Spam"
It looks to me like you are throwing too much code at it.
Instead of Button Up (which is the default) you might want to define a
Button Down ...
Private Sub Command1_Click()
Dim rbutton As RECT
Dim ButtonDown As Long
rbutton.Bottom = 15
rbutton.Left = 0
rbutton.Right = 15
rbutton.Top = 0
ButtonDown = True ' Or False, as needed
DrawFrameControl Me.hDC, rbutton, DFC_CAPTION, DFCS_CAPTIONMAX Or (DFCS_PUSHED And ButtonDown)
End Sub
Your user control has an hDC, and you can use that in place of Me.hDC above.
HTH
LFS
When you call CreateCompatibleBitmap() you pass it your new DC, so you're
telling it to create a bitmap in compatibility with the one currently
selected into it. Since you've not selected anything into it yet, it's
currently going to be the default stock-object bitmap which is a 1*1*1 DDB
so you're getting back a 1-bit bitmap too which is why it appears to be full
of garbage - It's just mapped to 1-bpp. What you need to do is create the
bitmap at a specific bit-depth with CreateBitmap() (Make sure you create it
at the current display bit-depth since it returns a DDB which can only be
selected at the display depth), or use a different DC as the reference such
as the desktop window's DC:
'***
Dim DeskWnd As Long, DeskDC As Long
DeskWnd = GetDesktopWindow()
DeskDC = GetDC(DeskWnd)
hBitmap = CreateCompatibleBitmap(DeskDC, 15, 15)
Call ReleaseDC(DeskWnd, DeskDC)
'***
You could also use the usercontrol's .hDC property here (If it has a DC)
however the above is independent of any controls if you require that at any
point.
If you still get a mess returned, then you may want to try a FillRect() on
the DC before you draw the frame control to it:
'***
Call FillRect(MemDC, rButton, GetSysColorBrush(COLOR_BTNFACE))
'***
Also, you're not taking into account the users current display settings
which may not be set to the default (15*15) caption size. You can use the
GetDeviceCaps() function to find this from the current system though, by
passing it the SM_CXSMSIZE/SM_CYSMSIZE constants.
Finally, I expect you already know this one but I just thought I'd point it
out in case; you don't need to use this every time:
'***
RetVal = FuncName( ... )
'***
If you're not checking the return value then there's no point in retaining
it, simply call the function:
'***
Call FuncName( ... )
'***
Hope this helps,
Mike
- Microsoft Visual Basic MVP -
E-Mail: ED...@mvps.org
WWW: Http://www.mvps.org/EDais/
Thanks Mike for the suggestions.
Regarding the 1-bpp bitmap issue. In your example you created the
compatible DC from the desktop where in my code I was createing it from the
control. I checked in MSDN and you are correct about the 1-bpp. I updated
my code to ->
MemDC = CreateCompatibleDC(UserControl.hdc)
'Create a bitmap and select it
hBitmap = CreateCompatibleBitmap(UserControl.hdc, 15, 15)
hOldBitmap = SelectObject(MemDC, hBitmap)
FillRect MemDC, rButton, GetSysColorBrush(COLOR_BTNFACE)
This got rid of the garbage but it didn't draw the control.
Regarding the getdevicecaps, I'm not doing that on purpose. I had originaly
writen my code to use that but when my control was used on XP the caption
bar was to big, and it doesn't take on the style of XP anyway.
The same code works fine here, perhaps there's something else you're doing
that you didn't post, or your API declares are incorrect. Here's the
declares I'm using here:
'***
Private Declare Function CreateCompatibleDC Lib _
"gdi32" (ByVal hDC As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" _
(ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32" _
(ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DrawFrameControl Lib "user32" _
(ByVal hDC As Long, ByRef lpRect As RECT, _
ByVal un1 As Long, ByVal un2 As Long) As Long
Private Declare Function BitBlt Lib "gdi32" _
(ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, _
ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, _
ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As
Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Const DFC_CAPTION As Long = 1
Private Const DFCS_CAPTIONMAX As Long = &H2
Private Const DFCS_PUSHED As Long = &H200
'***
If all else fails, then here's an alternative method for drawing the
maximize caption button:
'***
Dim DrawArea As RECT
Dim TempFont As Long, OldFont As Long
Dim CharSize As SIZE
Const MaxChar As String = "1"
DrawArea.Left = 0
DrawArea.Top = 0
DrawArea.Bottom = DrawArea.Top + 15
DrawArea.Right = DrawArea.Left + 15
Call DrawEdge(MemDC, DrawArea, BDR_RAISEDINNER, _
BF_LEFT Or BF_TOP Or BF_MIDDLE)
Call DrawEdge(MemDC, DrawArea, BDR_RAISEDINNER Or _
BDR_RAISEDOUTER, BF_RIGHT Or BF_BOTTOM)
TempFont = CreateFont( _
Min(DrawArea.Bottom - DrawArea.Top, _
DrawArea.Right - DrawArea.Left) - 4, _
0, 0, 0, 0, False, False, False, _
SYMBOL_CHARSET, 0, 0, 0, 0, "Marlett")
OldFont = SelectObject(MemDC, TempFont)
Call GetTextExtentPoint32(MemDC, MaxChar, 1, CharSize)
With DrawArea
Call TextOut(MemDC, _
(((.Right - .Left) - CharSize.cx) \ 2) + .Left, _
(((.Bottom - .Top) - CharSize.cy) \ 2) + .Top, MaxChar, 1)
End With
Call DeleteObject(SelectObject(MemDC, OldFont))
' ...
Private Function Min(ByVal inA As Long, ByVal inB As Long) As Long
Min = IIf(inA < inB, inA, inB)
End Function
> > 'Create a bitmap and select it
> > hBitmap = CreateCompatibleBitmap(UserControl.hdc, 15, 15)
> > hOldBitmap = SelectObject(MemDC, hBitmap)
> >
> > FillRect MemDC, rButton, GetSysColorBrush(COLOR_BTNFACE)
> >
> > This got rid of the garbage but it didn't draw the control.
>
> The same code works fine here, perhaps there's something else you're doing
> that you didn't post, or your API declares are incorrect.
I have to ask, what is the point of drawing to memory and then copying to the
control? Why not draw on the control? Since he said he wanted to use the
Paint event anyway, avoiding the creation of a second bitmap should be a good
thing, unless he has a lot more drawing going on than he originally indicated....
LFS
Common double buffering technique to prevent flickering when over-drawing,
the memory DC needed necessarily just by 15*15 pixels, however that's all
that's currently being drawn to it so there's no point in making it any
larger.
If you re-read the original post he said he was drawing on the paint
messages rather than the event, where you should always draw to the DC
returned by BeginPaint() or the DC handle passed to the WM_PRINT handler
rather than the surface itself, since these could be on-screen DC's and you
can't rely on AutoRedraw to handle the double buffering for you.
> If you re-read the original post he said he was drawing on the paint
> messages rather than the event, where you should always draw to the DC
> returned by BeginPaint() or the DC handle passed to the WM_PRINT handler
> rather than the surface itself, since these could be on-screen DC's and you
> can't rely on AutoRedraw to handle the double buffering for you.
"Because the user is interacting with my control I assume I don't want the
autoredraw so I can handle the paint messages."
Yeah, I mistook 'paint messages' for a paint event. It was that autoredraw
that distracted me from seeing his real intent. Even today, I find that whole
process such a chore, I avoid it like the plague! I guess its my mistake to
think everybody feels that way! :-P
LFS
> "Because the user is interacting with my control I assume I don't want the
> autoredraw so I can handle the paint messages."
>
> Yeah, I mistook 'paint messages' for a paint event. It was that
autoredraw
> that distracted me from seeing his real intent. Even today, I find that
whole
> process such a chore, I avoid it like the plague! I guess its my mistake
to
> think everybody feels that way! :-P
If you wrap it all up in a simple class it's not too bad at all:
'***
Call Buffer.Create(UserControl.ScleWidth, UserControl.ScaleHeight)
' Draw on Buffer.hDC
Call Buffer.Draw(UserControl.hDC)
Call Duffer.Destroy()
'***
Of course there's more efficient way of doing this, however all the
complexity is now wrapped up in the object which you need never worry about.