Creating a Transparent "Canvas" Over Form

Hello all.

I am trying to create an application that has a transparent "canvas" (or "layer") over a WebBrowser control.  While being able to see the contents of the WebBrowser control, users could scrible on this canvas.

It would be as if the web page being displayed was the background image to a PictureBox control.  Using GDI API calls, the user could then draw on the control to highlight important information.

I've tried a PictureBox.  While I can get it be transparent (which makes me wonder, since MSDN says I can't do that), the PictureBox.Refresh and PictureBox.Cls will destroy the transparency.  For one reason or another, I can not get it transparent again after calling either one of those.

I've tried creating a custom control, but I don't seem to have access to a hWnd or a hDC to use for drawing.

Can I create a transparent control that will allow me to use the API drawing calls to scrible on it, while the controls under it are still visible?

Many thanks for any help!

Ecks

P.S.
I only have 150 available to me at the moment, but if I can get this figured out by Monday I will gladly up the point value when I have more!
LVL 1
EcksAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

EcksAuthor Commented:
Thought I would throw a few links out that I had found around:

http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_20175501.html
Asks a very similar question (if not the exact same).  It points to the following web address:

http://support.microsoft.com/default.aspx?scid=KB;en-us;q185626
Which says it can't be done and mentions a few tips on working around it.  It points to here:

http://support.microsoft.com/default.aspx?scid=kb;EN-US;185882
But that doesn't help me draw on the object -- just get a transparent layer.

The second link does say this under the resolution:
"... To work around this problem, you can replace the PictureBox control with a custom or third party control that offers the same basic functionality as the PictureBox control but does not exhibit the same SetWindowLong limitation."

So, maybe my question is:
"How do I create a custom control that offers the same basic functionality as the PictureBox control, but does not have the same transparency limitation?" :)

Thanks again for any pointers!!
0
magglass1Commented:
Try refreshing the WebBrowser control after clearing the PictureBox.
0
EcksAuthor Commented:
Thanks magglass1,

That kind of works, but isn't I can do in this case.  When scribling on the PictureBox I'd have to refresh the browser everytime the a PictureBox.Refresh is fired.  Using a free draw tool (like a pencil) would require a browser refresh at least once millisecond... unless you draw *really* slow. :)

I did try it, and the browser does pop back up.  But I see green (the PB background color) more then I see the browser.

Ecks
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

RhaedesCommented:
Since you are using a webbrowser control, a different approach is to feed it some JavaScript once the page has loaded to allow the user to 'draw' directly over/into the webbpage (using DHTML) and/or to make the webpage editable so the the user can add notes.
When you say 'scribble' what do you mean exactly? Do you mean text, or do you mean draw free-hand with the mouse? Or both? If you mean text, is it enough that the user can insert text, or would the text have to 'float' over the page? Finally, perhaps all you need is a way of letting the user select something in the HTML document and highlight it.

All of this is achievable, but the difficulty varies, so please be as specific as possible with what you require.

Kindest regards,
Rhaedes
0
EcksAuthor Commented:
By "scribble" I mean free draw, text, polygon drawing... and anything else we decide would make a good tool. :)  I, in effect, want to make a Paint program that is transparent.

Can't use DHTML because of several reasons.  Not always being in control of the source document is an easy reason.  Needing to save and load the data points (for playback at any time) is another.

Ecks

P.S.  Is there another forum that I might post a similar question to?  Although I'm using the control in VB (and also in VBA), what might be a good group to post a question of the creation of new control?
0
magglass1Commented:
Why do you need to refresh or clear the PictureBox in the first place?  Doesn't the drawing automaticaly get displayed?  Or do you have to refresh as they are drawing?
0
EcksAuthor Commented:
If PictureBox.AutoRefresh is set to True; PictureBox.Refresh must be called after each GDI reference, or the graphic will not show up.

If PictureBox.AutoRefresh is set to False; the image will show up, but any movement of the canvas (minimize, maximize, resize, move, etc...) will force a redraw and ... *poof* ... everything disappears.
0
magglass1Commented:
Then don't move the canvas. :-) Or if you do, then have it refresh, but only then.
0
magglass1Commented:
You would have to set up a routine to sense when the canvas is minimized, maximized, resized, or moved.
0
JohnMcCannCommented:
First

I've tried creating a custom control, but I don't seem to have access to a hWnd or a hDC to use for drawing

These propertys are only available when the usercontrol has these proertys set.

a) WindowLess = False
b) HasDc = true

I know a very neat and easy trick for making transparent UserControls.

I'll explain how then i'll paste the code at the bottom.

We are going to create a usercontrol and use it's mask property to mask out the back color. How we do the is actualy quite simple first we lock the window so that the user does not see anything.  Then we draw the usercontrol as normal.  Take a screenshot  of the controls draw canvas.  We then use the picture of the usercontrol as the maskpicture and tell the usercontrol to mask out the backcolor which leaves anythong you've added or drawn.

In order to prove it works when you create your usercontrol add something to it and make sure it is a different color to the usercontrol.  

1) Create a usercontrol.
2) Set the propertys as above (Should be defaults).
3) Set the AutoRedraw = True
4) Copy the following code straight into the usercontrol code module.


Option Explicit

Private Const RC_PALETTE As Long = &H100
Private Const SIZEPALETTE As Long = 104
Private Const RASTERCAPS As Long = 38

Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Private Type PicBmp
    Size As Long
    Type As Long
    hBmp As Long
    hPal As Long
    Reserved As Long
End Type

Private Type PALETTEENTRY
    peRed As Byte
    peGreen As Byte
    peBlue As Byte
    peFlags As Byte
End Type

Private Type LOGPALETTE
    palVersion As Integer
    palNumEntries As Integer
    palPalEntry(255) As PALETTEENTRY ' Enough for 256 colors
End Type

Private Declare Function LockWindowUpdate Lib "user32" (ByVal hwndLock 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 CreateCompatibleBitmap Lib "GDI32" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function CreateCompatibleDC Lib "GDI32" (ByVal hDC As Long) As Long
Private Declare Function CreatePalette Lib "GDI32" (lpLogPalette As LOGPALETTE) As Long
Private Declare Function DeleteDC Lib "GDI32" (ByVal hDC As Long) As Long
Private Declare Function GetCurrentObject Lib "GDI32" (ByVal hDC As Long, ByVal uObjectType As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetDeviceCaps Lib "GDI32" (ByVal hDC As Long, ByVal iCapabilitiy As Long) As Long
Private Declare Function GetSystemPaletteEntries Lib "GDI32" (ByVal hDC As Long, ByVal wStartIndex As Long, ByVal wNumEntries As Long, lpPaletteEntries As PALETTEENTRY) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As PicBmp, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
Private Declare Function RealizePalette Lib "GDI32" (ByVal hDC As Long) As Long
Private Declare Function SelectObject Lib "GDI32" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function SelectPalette Lib "GDI32" (ByVal hDC As Long, ByVal hPalette As Long, ByVal bForceBackground As Long) As Long

Private m_Initializing              As Boolean

Private Sub UserControl_Initialize()
   m_Initializing = True
End Sub

Private Sub UserControl_InitProperties()
   m_Initializing = False
   pDraw
End Sub

Private Sub UserControl_Paint()
   If Not m_Initializing Then pDraw
End Sub

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
   m_Initializing = False
   pDraw
End Sub

Private Sub UserControl_Resize()
   If Not m_Initializing Then pDraw
End Sub

Private Sub pDraw()
On Error GoTo Err_Handler

Static bPainting As Boolean

Start_Painting:
   If bPainting Then Exit Sub
   bPainting = True
   LockWindowUpdate hWnd
   With UserControl
      If .MaskPicture <> 0 Then Set .MaskPicture = Nothing
      If .BackStyle <> 1 Then .BackStyle = 1
      If .ScaleMode <> vbPixels Then .ScaleMode = vbPixels
      .Cls
   End With
   DoEvents
         
Draw_Mask:
   Set UserControl.MaskPicture = CreatePictureFromDC(hDC, 0, 0, ScaleWidth, ScaleHeight)
   UserControl.MaskColor = UserControl.BackColor
   UserControl.BackStyle = 0
   
Exit_Handler:
   LockWindowUpdate False
   bPainting = False
   Exit Sub
   
Err_Handler:
   MsgBox TypeName(Me) & " - " & "pDraw" & " - " & Err.Description
   Resume Exit_Handler
End Sub

Private Function CreatePictureFromDC(ByVal hDCSrc As Long, ByVal LeftSrc As Long, ByVal TopSrc As Long, ByVal WidthSrc As Long, ByVal HeightSrc As Long) As Picture
Dim hDCMemory As Long, hBmp As Long, hBmpPrev As Long
Dim hPal As Long, hPalPrev As Long, RasterCapsScrn As Long
Dim HasPaletteScrn As Long, PaletteSizeScrn As Long
Dim typLogPal As LOGPALETTE

    hDCMemory = CreateCompatibleDC(hDCSrc)
    hBmp = CreateCompatibleBitmap(hDCSrc, WidthSrc, HeightSrc)
    hBmpPrev = SelectObject(hDCMemory, hBmp)

    RasterCapsScrn = GetDeviceCaps(hDCSrc, RASTERCAPS) ' Raster
    HasPaletteScrn = RasterCapsScrn And RC_PALETTE ' Palette
    PaletteSizeScrn = GetDeviceCaps(hDCSrc, SIZEPALETTE) ' Size of

   If HasPaletteScrn And (PaletteSizeScrn = 256) Then
      typLogPal.palVersion = &H300
      typLogPal.palNumEntries = 256
      GetSystemPaletteEntries hDCSrc, 0, 256, typLogPal.palPalEntry(0)
      hPal = CreatePalette(typLogPal)
      hPalPrev = SelectPalette(hDCMemory, hPal, 0)
      RealizePalette hDCMemory
   End If

   BitBlt hDCMemory, 0, 0, WidthSrc, HeightSrc, hDCSrc, LeftSrc, TopSrc, vbSrcCopy
   hBmp = SelectObject(hDCMemory, hBmpPrev)
   
   If HasPaletteScrn And (PaletteSizeScrn = 256) Then
      hPal = SelectPalette(hDCMemory, hPalPrev, 0)
   End If
   
   DeleteDC hDCMemory

   Set CreatePictureFromDC = CreatePictureFromBitmap(hBmp, hPal)
End Function

Private Function CreatePictureFromBitmap(ByVal hBmp As Long, ByVal hPal As Long) As Picture
Dim typGUID As GUID
Dim typPicBmp As PicBmp
Dim objIPicture As IPicture

    With typGUID
        .Data1 = &H20400
        .Data4(0) = &HC0
        .Data4(7) = &H46
    End With

    With typPicBmp
        .Size = Len(typPicBmp)
        .Type = vbPicTypeBitmap
        .hBmp = hBmp
        .hPal = hPal
    End With

    OleCreatePictureIndirect typPicBmp, typGUID, 1, objIPicture
    Set CreatePictureFromBitmap = objIPicture
End Function




Good luck.

P.S. Who says you can't make a usercontrol transparent tell me and I'll send the boys round.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
EcksAuthor Commented:
Thanks JohnMcCann, I'm looking over the code now and going to try it out.

I'm am wondering though, why do all of this and not just change the BackStyle to transparent?  Obviously, there is some reason since I can't get it to work that way. ;)
0
JohnMcCannCommented:
When you set the BackStyle to trasparent it masks out the MaskColor.
0
EcksAuthor Commented:
Also, I just added the control to a form and VB goes WACKO! It flickers along with the new control... is it supposed to do that? o.O
0
EcksAuthor Commented:
AutoRedraw = True

... got it. :P
0
JohnMcCannCommented:
I knew that was coming
0
JohnMcCannCommented:
Oh if you ever get that problem again problably for that reason close the form realy quick and it shouldn't crash.
0
magglass1Commented:
I am lost now...  So is it working or do you still need help?  Did you read my last post?
0
EcksAuthor Commented:
Sorry for the e-mail flood. :(

Ok, got it up and it is indeed transparent (sweet!), but I still can't seem to get a hDC or hWnd reference.

When I type in the control name, neither property is listed in the pop up window.  If I do ahead and type it in anyway, I get the following error:

    Method or data member not found.

The code that produces this:

    Debug.Print SlideCanvas1.hDC

WindowLess is False
HasDC is True

Oh, and I'm still not sure I understand (totally) the different between BackStyle=Transparent and what this is doing... but I trust ya. :)
0
magglass1Commented:
I was just about to comment on the email...  Nothing bad though...
0
JohnMcCannCommented:
You need to add them proertys to the usercontrol


Public Property Get hDC() As Long
   hDC = UserControl.hDC
End Property

Public Property Get hWnd() As Long
   hWnd = UserControl.hWnd
End Property
0
EcksAuthor Commented:
magglass1:

Still trying to work things out. :)

Because of the way the data is presented, I really have to have AutoRedraw turned on.  The issues I am having here are during a "playback" feature, which streams data out of a file (because of the amount of data, putting it in memory isn't a good idea).  Attempting to relocate the data that needs to be redrawn really isn't possible.
0
magglass1Commented:
I am bored here.  Trying to work on my new program, but I have no points to ask for help.  And it is 0204 in the morning which doesn't help much.
0
JohnMcCannCommented:
magglass1,

Ask your Q anyway, I don't do it for points
0
JohnMcCannCommented:
Because of the way the data is presented, I really have to have AutoRedraw turned on.  The issues I am having here are during a "playback" feature, which streams data out of a file (because of the amount of data, putting it in memory isn't a good idea).  Attempting to relocate the data that needs to be redrawn really isn't possible.

I thought AutoRedraw was on.

If you explain a little better I may know another work round
0
magglass1Commented:
Ok John, thanks.  Here is my question.
I am working on an IM Server app. and client app.  I am looking for the best way for them to communicate via TCP by sending strings.  For example, if the server sent the client data, it would have to sort out commands like a ping and sort out IMs from different users.  I could add headings to the strings like "Message:text" but I wouldn't know how to take away the substring "Message:"  Also, it would need to include the from address.  And if I did "From:User1Message:hello:  I could get into problems if the text included any of theses words I am using, unless I make Message: always the last command.  I have no clue how AIM works, and that would probably help me out a bit.  If you have any good meathods for sending and sorting this data, please advise me.
Thanks again.
0
magglass1Commented:
You should see my Deleted Items folder in Outlook.  It looks a bit redundant...
0
EcksAuthor Commented:
It is on John, I was refering to earlier posts. :)

Ok, I have the user control up and it is kind of working.  Unfortunately I can't see any of the drawings! :(

I am calling the following GDI function several times from a function:
    LineTo hDC, X, Y

It is given the new control's hDC and a proper X/Y, but as the line is supposed to show up... it doesn't.  I thought that "Refresh" might be the issue, so I added the following to my control:

Public Function Refresh()
    UserControl.Refresh
End Function

Still nothing show up.

When I resize, there is a glitch that forms (a line from 0,0 to the current x/y set) -- I'm not concerned about that right now, but when the above "LineTo" call passes through this glitch, you can see the line change.  In other words -- the graphics show up on the glitch, but not on the canvas.

Any ideas?

Thanks for all the help!
0
JohnMcCannCommented:
Ecks,

First the control is making itself transparent then you draw on it and then it makes itself transparent again.  If that made any sense.  So anything you draw on the contyrol is never shown

In order for you to do what you are asking you need to move the methods into the UserControl but bear in mind the logic for drawing apears in the pDraw method..  I can tell you Line is going to be a pain.  i remember seeing an earlier post about the line method and Usercontrols.

I will see if I can find the post for you.
0
JohnMcCannCommented:
magglass1,

I am not to familar with what you are asking.


0
magglass1Commented:
I just need a way to convert a string like "To:BobMessage:Hello!" into the string 'to' which = "Bob" and the string 'message' which = "Hello!"
0
EcksAuthor Commented:
John,

I figured it was making itself transparent again.  Just wasn't sure how to fix it. :)

So, I have two function at the moment:

drawPencil -- which just wraps around LineTo
drawArrow -- constructs a Polygon region shaped like an arrow

I send in an "Action" that include a Enum number that tells me which function to call.  Do you have any suggestions on how I can transfer that information into the control so it does the right thing?
0
JohnMcCannCommented:
magglass1,

Have a play with these function


VBA.Left()    ' gets text at the left hand side of string
VBA.Mid()    ' Gets text in the middle of a string
VBA.InStr()  ' Identifys where a charcter is in  a string
VBA.Right()  '

Also if you goto into visual basic goto the view menuy -> object browser.  Thenb select VBa from the first drop down combo and select strings from the list at the bottom these are all the in built string functions.
0
JohnMcCannCommented:
One possible metod may be to store the user actions in an array and call them during draw.

Example

Private Type MyDrawPencilStruct
   Left as integer
   Etc
End Type

Private Type MyDrawArrowStruct
   Left as integer
   Etc
End Type

drawPencils() As MyDrawPencilStruct
intDrawPencilCount as integer
drawArrows() As MyDrawArrowStruct

Public sub drawPencil()
  intDrawPencilCount = intDrawPencilCount + 1
  Redim drawPencil(intDrawPencilCount)
  with ubound(drawPencils)
     .Left = 0 ' Left is just an example  
  End with
  pDraw
End sub


Private Sub pDraw()
On Error GoTo Err_Handler

Static bPainting As Boolean

Start_Painting:
  If bPainting Then Exit Sub
  bPainting = True
  LockWindowUpdate hWnd
  With UserControl
     If .MaskPicture <> 0 Then Set .MaskPicture = Nothing
     If .BackStyle <> 1 Then .BackStyle = 1
     If .ScaleMode <> vbPixels Then .ScaleMode = vbPixels
     .Cls
  End With
  DoEvents

Draw_Pencils:

Draw_Arrows:
       
Draw_Mask:
  Set UserControl.MaskPicture = CreatePictureFromDC(hDC, 0, 0, ScaleWidth, ScaleHeight)
  UserControl.MaskColor = UserControl.BackColor
  UserControl.BackStyle = 0
   
Exit_Handler:
  LockWindowUpdate False
  bPainting = False
  Exit Sub
   
Err_Handler:
  MsgBox TypeName(Me) & " - " & "pDraw" & " - " & Err.Description
  Resume Exit_Handler
End Sub



0
EcksAuthor Commented:
Hm... I was afraid of that.  Potentially a lot of data to be storing in an array.  I can clear them out at each page change though, so it should be too bad.

Just glancing at the code above, it looks like this has to redraw the whole control every time a new pencil/arrow is drawn.  Am I seeing that right?  Course, I think I do that anyway with my 'Refresh' calls, so I don't know why I'm stressing about it. :)

Thanks again!  I'll give the code a try after I let me brain cool down some.
0
JohnMcCannCommented:
You are correct about the whole thing being redrawn.

The code is taking a picture of the control with it's backcolor to create the mask then all the code does is remove that color.
0
EcksAuthor Commented:
Want to make one last check to see if I might be missing something here:

added this function:
Public Function addPencil(X As Long, Y As Long, nColor As Long)
    PencilToolCount = PencilToolCount + 1
    ReDim PencilToolArray(PencilToolCount)

    With PencilToolArray(PencilToolCount)
        .X = X
        .Y = Y
        .nColor = nColor
    End With

    pDraw
End Function


To work with these variables:
Private Type PencilTool
    X As Long
    Y As Long
    nColor As Long
End Type
Private PencilToolArray() As PencilTool
Private PencilToolCount As Long

Finally added this to 'pDraw' where you specify above:
Draw_Pencils:
    For i = 1 To PencilToolCount
        With PencilToolArray(i)
            UserControl.Line -(.X, .Y), .nColor
        End With
    Next i

Does that look like what you would do?
0
JohnMcCannCommented:
Yeap

Did it work ok?
0
JohnMcCannCommented:
The method I have been using above is based on a copy of the DC.  To do this properly we should be invering the DC.  I know my method works and one day I will get round to writing the correct function.  The function in question is CreatePictureFromDC which needs to be CreateMaskFromDC.  I use the CreatePictureFromDC function more for doing screen captures of any DC (for creating manuals).
0
EcksAuthor Commented:
Nope. o_O

Actually, it works to a point.  The 'Line' function doesn't exactly work, but I think the problem is simply that I am not update the 'CurrentX' and 'CurrentY' positions.

The real problem is that the control flickers.  I am just sending so many requests to it that it can't keep up -- I'm seriously sending it a request every few milliseconds when the pencil is active.  It is kinda hypnotic really.  The drawing really slows down as a result too -- during playback, I get all the way through my slides but the drawing is only half way there. :)

I wrote the code for the Arrows too.  While there is a glitch there too (I just ported code, so I probably didn't change something) they do actually work.

So... all your stuff works great!  My stuff doesn't. ;)

I've got another plan in my head -- based in part on the 'pDraw' code you posted, and some custom ActiveX control work I've been trying.  I have to decide if I'm still sane enough to attempt it tonight.
0
JohnMcCannCommented:
The correct function for creating the mask may help (CreateMaskFromDC).  As the mask picture should be a lot smaller.

I've been meaning to do it for sometime and may do tommorow.  If i create the code I will post it for you to try and see if that makes a difference.

I've just had one last thought bear  with me for 10mins
0
EcksAuthor Commented:
Many thanks for all the info!

I was wondering too, could you help me to understand why simply putting the BackStyle of a control to "Transparent" doesn't work?

When I have it as Opaque, I can draw on it.  But when I set it to Transparent, none of the drawing shows up.  I thought BackStyle just cut out the background color.  Does it also (in effect) remove parts of the control that do not have anything on them?
0
JohnMcCannCommented:
Your problem will stem from the control being redrwn som any times as you stated.  As aa new mask is created each time.

Unfortunatly I cannot see anyway around this as every mask needs to be different.

0
JohnMcCannCommented:
When you set the BackStyle to transparent it uses the picture you set as the transparent picture and the color you set as the transparent color.

In the above case we create the mask picture from the DC.
And set the maskcolor to the backcolor effectively removing any of the background left after drawing.
0
EcksAuthor Commented:
Thanks John, I get it now. :)

I've already tried and failed on another attempt; working with a PictureBox in a custom ActiveX control.  I can't get the PictureBox transparent though, which is frustrating as that would have been a great way to do it.

Now I'm on to a new attempt here.  I am using a PictureBox with AutoRedraw set to False and things are showing up.  In order to get this method to be usable though, I need to work with the Paint event to redraw items when needed.

When working with the Paint event, can I get access to the the dimensions of the region needing to be updated; or am I stuck with repainting the whole thing?
0
JohnMcCannCommented:
I don't think you can directly in the usercontrol I think you need to be subclassing.

How do you make the PictureBox transparent?

Another way may be

1) Create a usercontrol as before.
2) Create a memDC
3) Work with the memDC for creating the transparent image.
4) BitBlit or PaintPicture on to the DC during the paint function.
0
JohnMcCannCommented:
Another function that may be of help

Private Declare Function TransparentBlt Lib "msimg32.dll" (ByVal hdc 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 nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal crTransparent As Long) As Boolean

Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

Typical usage

TransparentBlt(Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, GetPixel(Picture1.hdc, 0, 0))

Masks out all pixels of a given color.

Typically GetPixel(SourceDC,0,0) 'Top left pixel.
0
EcksAuthor Commented:
> How do you make the PictureBox transparent?

Top side defines:
'** form transparency declarations *'
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

'** form transparency constants *'
Private Const WS_EX_TRANSPARENT = &H20&
Private Const GWL_EXSTYLE = (-20)

The actuall (in Initialize):
Call SetWindowLong(picBox.hWnd, GWL_EXSTYLE, WS_EX_TRANSPARENT)

-----------

If you check out the second link, in the second message, it actually says this doesn't work.  The only time this method has failed for me though is when using code from the MSDN site (and when not using a direct PictureBox or Form, like a custom control).

The transparency disappears very easily on a PictureBox though.  Transparency on a form is a little more stable, but I can break it really quick if I want (or don't want, more often). :)

I can't seem to get the TransparentBlt function working.  I am making the following call...

Call TransparentBlt(SlideCanvas.hdc, 0, 0, SlideCanvas.ScaleWidth, SlideCanvas.ScaleHeight, Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, GetPixel(Picture1.hdc, 0, 0))

Picture1 is loaded with a 5x5 pink bitmap, the scalewidth is also 5x5 pixels

I've tried setting SlideCanvas to all pink background, and I've loaded the same 5x5 bitmap into it as well.  Can't get the pink to disappear.  I'm looking around at a few sites, but I'm afraid I'll just end up seeing the background of the PictureBox, and not what is behind it. :(

Thanks again for all the help!
0
RhaedesCommented:
Wow. I woke up this morning to find 45 new emails from this question alone. You guys have been busy!
I see you're pretty advanced with this, so I'll just sit out, but for the record, Ecks, the two objections you state for not using DHTML are not really valid. DHTML is client-side, so you can feed the Script into the document AFTER it is loaded, even though you do not have control over the source. Also, there are different ways of 'recording' the modifications made, so that they will 'play back' when you navigate to the same page again. Moreover, a quick google search will come up with free script that will draw shapes etc on your document, which you can use from VB.
I understand you don't want to use DHTML, so that's fine, but if you want some demo code to prove what I am suggesting is possible, I'll be happy to provide it!

Kindest regards,
Rhaedes
0
magglass1Commented:
Thanks John, will try out those string functions.
0
EcksAuthor Commented:
Rhaedes,

If you know of some demo code, I would love to see it.
0
JohnMcCannCommented:
Ecks,


There was an ewrror in one of the posts they may cause you problems

Redim drawPencil(intDrawPencilCount)
 

Should be

Redim Preeserve drawPencil(intDrawPencilCount)

Sorry.

Otherwise you will always only have the last pencil drawn in the array.
 
0
JohnMcCannCommented:
Call TransparentBlt(SlideCanvas.hdc, 0, 0, SlideCanvas.ScaleWidth, SlideCanvas.ScaleHeight, Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, GetPixel(Picture1.hdc, 0, 0))

Looks ok

Check the AutoRedraw and if it is set to false are you calling refresh.


Just remembered I think you may need to call DoEvents first to get thet transparency

0
JohnMcCannCommented:
An example of TransparentBit

Create a new exe
Add two picture boxes
Add a picture with a lot of backcolor in it to pic1
Set both there scalemodes to vbPixels
Make sure Pic2 AutoRedraw = False (For this example)

Paste code into code window

Private Declare Function TransparentBlt Lib "msimg32.dll" (ByVal hdc 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 nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal crTransparent As Long) As Boolean
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

Private Sub Form_Load()
    Picture1.ScaleMode = vbPixels
    Picture2.ScaleMode = vbPixels
End Sub

Private Sub Picture2_Paint()
    DoEvents
    TransparentBlt Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, _
            Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, GetPixel(Picture1.hdc, 0, 0)
End Sub



0
RhaedesCommented:
Okay, I'll knock up some code for the DHTML approach and post it. One other thought: what about converting the HTML into RTF (using say http://www.cena.dgac.fr/~sagnier/info/formats/conversions/htm2rtf.htm ) and then simply drawing in it?

Kindest regards,
Rhaedes
0
RhaedesCommented:
Okay, here's the DHTML approach. I insist that this is just for example's sake: to show that it is possible to do, and that you can 'playback' what you record. It's simple and memory hungry, but was fast to write!
As it stands, it just lets you draw lines freehand over the document in different colours. Whenever you navigate back to a page you have previously scribbled on, the scribbles appear again as they were when you left the page.
With a little more time, you can add anything you want, from drawing shapes to adding boxes with notes (see http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm for some nice free script).

First create a directory somewhere to keep the scribble logs and change the LogDir line in the Form_Load sub accordingly.
Then take a form, and add the following:
-a webbrowser (WebBrowser1).
-a TextBox (Text1). This is where you type your URL
-a commandbutton (Command1). This is the button you press to make the webbrowser navigate to the URL in Text1. (Alternatively just hit return in the textbox)
-a ComboBox (Combo1). This is to change the colour of the scribbles.

Add the HTML Object Library, and paste the code below into a form.

Kindest regards,
Rhaedes



Private WithEvents Hdoc As HTMLDocument
Dim LogDir As String
Dim CurrentURL As String
Dim MouseIsDown As Boolean
Dim myScribbles As String

Private Sub Command1_Click()
WebBrowser1.Navigate2 (Text1.Text)
End Sub

Private Sub Form_Load()
LogDir = "c:\SOMEWHERE\myLog\" 'Must have final slash!

Combo1.AddItem ("red")
Combo1.AddItem ("green")
Combo1.AddItem ("blue")
Combo1.AddItem ("black")
Combo1.ListIndex = 0

Text1.Text = "http://www.google.com"
Command1_Click

End Sub

Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
Print #1, myScribbles
Close #1
End Sub

Private Sub Hdoc_onmousedown()
MouseIsDown = True
End Sub

Private Sub Hdoc_onmouseup()
MouseIsDown = False
End Sub

Private Sub Hdoc_onmousemove()
If MouseIsDown = False Then Exit Sub
Freehand
End Sub

Private Sub Freehand()
myX = Hdoc.parentWindow.event.clientX
myY = Hdoc.parentWindow.event.clientY

myXoffset = Hdoc.body.scrollLeft
myYoffset = Hdoc.body.scrollTop

newpoint = "<div style=""color:" + Combo1.Text + ";position:absolute;top:" + Str(myY + myYoffset - 6) + ";left:" + Str(myX + myXoffset - 6) + """>*</div>" + vbNewLine
Hdoc.body.innerHTML = Hdoc.body.innerHTML + newpoint
myScribbles = myScribbles + newpoint
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii = 13 Then Command1_Click
End Sub

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
On Error Resume Next
Print #1, myScribbles
Close #1
CurrentURL = Replace(URL, ":", "")
CurrentURL = Replace(CurrentURL, "/", "")
Set Hdoc = WebBrowser1.Document
If Dir(LogDir + CurrentURL + ".txt") <> "" Then Replay
Record
End Sub

Private Sub Record()
Open (LogDir + CurrentURL + ".txt") For Output As #1
End Sub

Private Sub Replay()
Open (LogDir + CurrentURL + ".txt") For Input As #1
Do While Not EOF(1)
Line Input #1, someHTML
Hdoc.body.innerHTML = Hdoc.body.innerHTML + someHTML
myScribbles = myScribbles + someHTML
Loop
Close #1
End Sub
0
RhaedesCommented:
...Even easier, why not convert the HTML file to an image, and then scribble over the image. Since in your original proposal I don't think you can use the links etc in the file (you can with the DHTML approach) why not simply do a google search for one of the many HTML2JPG apps and dlls out there. Just thinking out load. And I think John deserves the points whatever you decide to do.

Kindest regards,
Rhaedes
0
JohnMcCannCommented:
I have another though that may solve your problem.

Basically it involves drawing stright onto the mask.

How I would it is first create your mask during the initalize event and during the Draw event update your mask.


0
EcksAuthor Commented:
Thanks John,

I was thinking about this the other day and how I need to close it out -- thanks for reminding me. :)

I've actually moved away from this problem at the moment, to work on learning the new goodies in Windows Media 9, so I still don't have it solved just right.  The accepted answer helped me understand the most about the paint process though, so I snagged that for an answer.

I'll give the last suggestion a shot when I fix all our new problems. o_O

Thanks again everyone!
0
JohnMcCannCommented:
No problem
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.