Weird difference between UserControl and Form

P

Peter Wone

Hosting the WebBrowser control is easy. Customising it is not. You have to
implement IDocHostShowUI, IDocHostUIHandler, IDocHostUIHandler2, and
IOleClientSite, not one of which appears in a typelib that I could find.

With aid from around the world I did just that, and put together a
comprehensive set of interfaces and enumerations, and I used them on my
forms to set up the form as the client site for the browser object
implementing squillions of callback interfaces.

Wonderful wonderful, works a treat.

Now I have a colleague, of lesser mettle, who though learning fast is
perhaps not ready for the subtle nastiness of DIY interop. And ultimately,
through all of this gnarly COM stuff, all it's really doing is asking for
settings and firing events.

So today I decided to implement a UserControl that hosts a WebBrowser, sets
itself up as the client site and either handles the callbacks itself getting
settings from properties of the UserControl or presenting callbacks as
events.

So here's the freaky thing: this code works fine in the constructor of a
Form.

InitializeComponent();
(wb.GetOcx() as IOleObject).SetClientSite(this as IOleClientSite);
wb.Navigate("about:blank");

but the same code throws a NullReferenceException in the constructor of a
UserControl. I checked the generated code to compare the way the web browser
is initialised and the same sequence occurs in both form and UserControl.

I found a fix. It works. What I do NOT like is the fact that I don't know
why. This works:

InitializeComponent();
wb.Navigate("about:blank");
(wb.GetOcx() as IOleObject).SetClientSite(this as IOleClientSite);
wb.Navigate("about:blank");

Now, the primary difference between wb before and after using the Navigate()
method is that before, there is no MSHTML instance and afterwards an
instance has been created. Great, that fits with the NullReferenceException
going away. But if that's what's wrong, then why the @#$%@!! does it work on
the form?

I am willing to release source code to anyone credible.

It smells a bit like certain messages are seen by the browser when on a form
but not when on a UserControl. I'm going to subclass (as opposed to
aggregating) the browser class so I can override WndProc and log messages. I
look forward to your collective insight.
 
D

Dmitriy Lapshin [C# / .NET MVP]

Peter,

Older versions of Visual Studio included a wonderful tool called Spy++. It
enables the developer to view and log messages sent to any window without
any need to subclass that window. You just drag a crosshair-like cursor over
the window in question, the tool highlights it, and when you select the
window, the tool starts logging messages for it.

But I think the problem is not in messages but in a way the ActiveX control
is hosted on the user control as opposed to the form. There is a lot of
interfaces involved, and the 'protocol' is quite complex.
Can you, say, ask the wb instance to re-paint itself before querying for
IOleObject? Will it have the same effect?
 
P

Peter Wone

Thanks Dimitriy.

I think I'll do the subclassing thing anyhow because that way I can log to a
file, first from a Form and then from a UserControl. Then I can diff the
files. Trouble is, of course, I'm gonna be inundated with mouse messages.

As a matter of interest, is there any straightforward way to SEND a message
in dotnet? As far as I can see I'm going to have to knock up a com wrapper
and interop it. Which is a bit much just to get my hands on PostMessage()

What I was actually trying to do was put a propertygrid into edit mode so
the cursor appears as a big hint to my dumber users that they can type
stuff. I ended up using SendKeys.Send("{TAB}"); which is a bit too VB for my
taste.

I tried to subclass PropertyGrid to add an Edit() method so as to get at
protected void InvokeOnClick() but for reasons that elude me, when I drop
PropertyGrid2 on a form it promptly vanishes. No (reported) errors, but
nothing on the form. It's such a trivial piece of subclassing I really
cannot imagine why it fails:

using blah...
namespace whatever {
public class PropertyGrid2 : PropertyGrid {
public void Edit() {
InvokeOnClick();
}
}
}
 
D

Dmitriy Lapshin [C# / .NET MVP]

As a matter of interest, is there any straightforward way to SEND a
message in dotnet?

P/Invoking to SendMessage or PostMessage perharps? I can't think of anything
simplier (even though this approach is not that straightforward).
You don't need COM Interop for that actually, only proper usage of the
[DllImport] attribute and appropriate declaration are required.

But, as what you really need is forcibly focusing the property grid, you
might do away with something more .NET-like:

if (theGrid.CanFocus)
{
theGrid.Focus();
}
 
P

Peter Wone

Funny you should say that, I saw the DllImportAttribute in some code about
four hours after posting that and had the same thought.

Focusing it I've already achieved using precisely the code you quoted. But
that's not enough to put it in edit mode, it needs a click or a TAB
keystroke.
 
D

Dmitriy Lapshin [C# / .NET MVP]

Then calling PostMessage seems to be the way to go - you can try something
simple first such as sending WM_LBUTTONDOWN to the property editor window.
TAB can be more tricky as it is a system key and as far as I remember is
does not use the generic WM_KEYDOWN notification.
 
P

Peter Wone

Then calling PostMessage seems to be the way to go - you can try something
simple first such as sending WM_LBUTTONDOWN to the property editor window.
TAB can be more tricky as it is a system key and as far as I remember is
does not use the generic WM_KEYDOWN notification.

Yeah that's pretty much what I've done. Someone else was trying to use
messages to change the tooltip style and while I was fixing his code I
swiped his import of Win32 (and fixed quite a bit of that too) and now I
have a handy dandy little collection of message tools.

I have a new and quite interesting problem relating to window recreation
when the theme is changed. I've created a new thread for that one.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top