|
You may take a look at
NetRix HTML Editor control
which has much more editing functions and additional features for accessing DHTML events.
Joerg
|
|
|
|
|
HTTP Error 404 - File or directory not found
My C#, My love
|
|
|
|
|
|
Alexander,
Thanks for the code, I used it to do a quick and easy HTML help file. Good work.
Joe
|
|
|
|
|
Do you know how to get the web browser control to support XP themes. Notice in your Google screenshot for example that the text input field and buttons are bevelled.
Interestingly, I've also found that when I add the browser control to an existing form that did support themes (via System.Windows.Forms.Application.EnableVisualStyles()), the controls revert back to old style.
I have been searching high and low for a way to get this to work but to no avail. Any ideas would be greatly appreciated.
|
|
|
|
|
Try using a manifest file and flag your controls to version 6 upwards.
I think the Application.EnableVisualStyles might be a bit problematic in dotnet where it has to have DoEvents or something called.
Hope this helps
|
|
|
|
|
I have found a couple of solutions to this problem.
The simple solution is to add a meta tag to your HTML:
Unfortunately, this only works if you have control over the HTML.
The more complicated solution involves writing a custom ATL COM component in managed C++ that implements the IDocHostUIHandler and IOleClientSite COM interfaces. Within this you need to specify that themes are supported, which they are not by default:
HRESULT STDMETHODCALLTYPE GetHostInfo(/* [out][in] */ DOCHOSTUIINFO *pInfo)
{
pInfo->dwFlags = DOCHOSTUIFLAG_THEME;
return S_OK;
}
This component is connected to the web browser control in the class that contains the control:
MyHtmlHost uiHandler = new MyHtmlHost(this.axWebBrowser1, null);
|
|
|
|
|
You've just solved my problem, so by way of thanks I'll make your life easier. You don't have to go through the agonies of C++ to implement GetHostInfo, you can do it in a method of your winform, all you need is the interfaces. I have included at the end of this posting my complete interfaces file that I partly wrote myself and mostly pillaged from the efforts of others. Alas their names are not in the code. It never occurred to me that I might be the one providing help, and I rewrote it to improve the coherence of style, but they were many and if any of you recognise your own work then tell me and I'll rectify the omission of your credit.
These interfaces will put you in a position to seriously yank the chain of MSHTML. For example once you have the interface declared correctly it is a piece of cake to return any C# object as the result of IDocHostUIHandler.GetExternal() which is how I bind events in generated UI back to event handlers implemented in C# in the host form.
You're quite right - there's not a single comment in the whole damn thing. I wrote it for me, not for you. And all of these interfaces are completely documented in this Microsoft tutorial on the subject.[^]
<button onclick="window.external.AddNew('tableX')">Add New X</button>
using System;
using System.Windows.Forms;
using mshtml;
using SHDocVw;
using System.Runtime.InteropServices;
namespace BrowserCustomisation {
#region IDocHostUIHandler
public enum DOCHOSTUITYPE {
DOCHOSTUITYPE_BROWSE = 0,
DOCHOSTUITYPE_AUTHOR = 1
}
public enum DOCHOSTUIDBLCLK {
DOCHOSTUIDBLCLK_DEFAULT = 0,
DOCHOSTUIDBLCLK_SHOWPROPERTIES = 1,
DOCHOSTUIDBLCLK_SHOWCODE = 2
}
public enum DOCHOSTUIFLAG {
DOCHOSTUIFLAG_DIALOG = 0x00000001,
DOCHOSTUIFLAG_DISABLE_HELP_MENU = 0x00000002,
DOCHOSTUIFLAG_NO3DBORDER = 0x00000004,
DOCHOSTUIFLAG_SCROLL_NO = 0x00000008,
DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE = 0x00000010,
DOCHOSTUIFLAG_OPENNEWWIN = 0x00000020,
DOCHOSTUIFLAG_DISABLE_OFFSCREEN = 0x00000040,
DOCHOSTUIFLAG_FLAT_SCROLLBAR = 0x00000080,
DOCHOSTUIFLAG_DIV_BLOCKDEFAULT = 0x00000100,
DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY = 0x00000200,
DOCHOSTUIFLAG_OVERRIDEBEHAVIORFACTORY = 0x00000400,
DOCHOSTUIFLAG_CODEPAGELINKEDFONTS = 0x00000800,
DOCHOSTUIFLAG_URL_ENCODING_DISABLE_UTF8 = 0x00001000,
DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8 = 0x00002000,
DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE = 0x00004000,
DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION = 0x00010000,
DOCHOSTUIFLAG_IME_ENABLE_RECONVERSION = 0x00020000,
DOCHOSTUIFLAG_THEME = 0x00040000,
DOCHOSTUIFLAG_NOTHEME = 0x00080000,
DOCHOSTUIFLAG_NOPICS = 0x00100000,
DOCHOSTUIFLAG_NO3DOUTERBORDER = 0x00200000,
DOCHOSTUIFLAG_DELEGATESIDOFDISPATCH = 0x00400000
}
[ StructLayout( LayoutKind.Sequential )]
public struct DOCHOSTUIINFO {
public uint cbSize;
public uint dwFlags;
public uint dwDoubleClick;
[MarshalAs(UnmanagedType.BStr)] public string pchHostCss;
[MarshalAs(UnmanagedType.BStr)] public string pchHostNS;
}
[StructLayout( LayoutKind.Sequential )]
public struct tagMSG {
public IntPtr hwnd;
public uint message;
public uint wParam;
public int lParam;
public uint time;
public tagPOINT pt;
}
[ComImport(),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
GuidAttribute("bd3f23c0-d43e-11cf-893b-00aa00bdce1a")]
public interface IDocHostUIHandler {
[PreserveSig]
uint ShowContextMenu(uint dwID, ref tagPOINT ppt,
[MarshalAs(UnmanagedType.IUnknown)] object pcmdtReserved,
[MarshalAs(UnmanagedType.IDispatch)] object pdispReserved);
void GetHostInfo(ref DOCHOSTUIINFO pInfo);
void ShowUI(uint dwID, ref object pActiveObject, ref object pCommandTarget, ref object pFrame, ref object pDoc);
void HideUI();
void UpdateUI();
void EnableModeless(int fEnable);
void OnDocWindowActivate(int fActivate);
void OnFrameWindowActivate(int fActivate);
void ResizeBorder(ref tagRECT prcBorder, int pUIWindow, int fRameWindow);
[PreserveSig]
uint TranslateAccelerator(ref tagMSG lpMsg, ref Guid pguidCmdGroup, uint nCmdID);
void GetOptionKeyPath([MarshalAs(UnmanagedType.BStr)] ref string pchKey, uint dw);
object GetDropTarget(ref object pDropTarget);
[PreserveSig]
void GetExternal([MarshalAs(UnmanagedType.IDispatch)] out object ppDispatch);
[PreserveSig]
uint TranslateUrl(uint dwTranslate,
[MarshalAs(UnmanagedType.BStr)] string pchURLIn,
[MarshalAs(UnmanagedType.BStr)] ref string ppchURLOut);
IDataObject FilterDataObject(IDataObject pDO);
}
[ComImport(),
GuidAttribute("3050f6d0-98b5-11cf-bb82-00aa00bdce0b")]
public interface IDocHostUIHandler2: IDocHostUIHandler {
void GetOverrideKeyPath([MarshalAs(UnmanagedType.BStr)] ref string pchKey, uint dw);
}
#endregion
#region IDocHostShowUI
[ComImport,
Guid("C4D244B0-D43E-11CF-893B-00AA00BDCE1A"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown) ]
public interface IDocHostShowUI {
[PreserveSig]
uint ShowMessage(IntPtr hwnd,
[MarshalAs(UnmanagedType.BStr)] string lpstrText,
[MarshalAs(UnmanagedType.BStr)] string lpstrCaption,
uint dwType,
[MarshalAs(UnmanagedType.BStr)] string lpstrHelpFile,
uint dwHelpContext,
out int lpResult);
[PreserveSig]
uint ShowHelp(IntPtr hwnd, [MarshalAs(UnmanagedType.BStr)] string pszHelpFile,
uint uCommand, uint dwData,
tagPOINT ptMouse,
[MarshalAs(UnmanagedType.IDispatch)] object pDispatchObjectHit);
}
#endregion
#region IOleClientSite
[ComImport,
Guid("00000118-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleClientSite {
void SaveObject();
void GetMoniker(uint dwAssign, uint dwWhichMoniker, ref object ppmk);
void GetContainer(ref object ppContainer);
void ShowObject();
void OnShowWindow(bool fShow);
void RequestNewObjectLayout();
}
#endregion
#region IOleObject
[ComImport,
Guid("00000112-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleObject {
void SetClientSite(IOleClientSite pClientSite);
void GetClientSite(ref IOleClientSite ppClientSite);
void SetHostNames(object szContainerApp, object szContainerObj);
void Close(uint dwSaveOption);
void SetMoniker(uint dwWhichMoniker, object pmk);
void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk);
void InitFromData(IDataObject pDataObject, bool fCreation, uint dwReserved);
void GetClipboardData(uint dwReserved, ref IDataObject ppDataObject);
void DoVerb(uint iVerb, uint lpmsg, object pActiveSite, uint lindex, uint hwndParent, uint lprcPosRect);
void EnumVerbs(ref object ppEnumOleVerb);
void Update();
void IsUpToDate();
void GetUserClassID(uint pClsid);
void GetUserType(uint dwFormOfType, uint pszUserType);
void SetExtent(uint dwDrawAspect, uint psizel);
void GetExtent(uint dwDrawAspect, uint psizel);
void Advise(object pAdvSink, uint pdwConnection);
void Unadvise(uint dwConnection);
void EnumAdvise(ref object ppenumAdvise);
void GetMiscStatus(uint dwAspect,uint pdwStatus);
void SetColorScheme(object pLogpal);
};
#endregion
}
Stop asking about my world domination plan. You'll find out when the time comes.
|
|
|
|
|
You can read about an example of an application that hosts MSHTML at http://www.dkcs.ws
Giles Bradshaw
|
|
|
|
|
Kent,
Your code makes the assumption that Document Complete is called exactly once in a pre-ready state and that your initialization code will be executed only once (when task = 2).
I would think carefully about this logic. It's been a while since I played with the axBrowser control, but I remember something about Document Complete being called many times when the page had iFrames ... ?
best, Bill Woodruff
"The greater the social and cultural distances between people, the more magical the light that can spring from their contact." Milan Kundera in Testaments Trahis
|
|
|
|
|
Yes, you will likely receive multiple DocumentComplete notifications if there are any kind of frames present.
One technique I have used in the DocumentComplete event handler is to check the WebBrowser.ReadyState property:
if (axWebBrowser.ReadyState == SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
{
// document should be completely loaded at this point
}
This may help.
|
|
|
|
|
Thank you for the helpfull article.
Is there a way to get notification when a component on a "doc complete" doc refreshes itself -post refresh. like with the frames of animations?
|
|
|
|
|
Thanks, this article has helped me out...
I want to automatically browse to a website and download a file (the file name is very dynamic). Can this be automated? Is there a way to automate the SAVE AS DIALOG box in IE ?
any ideas?
Gunnar
|
|
|
|
|
I used Win32 API’s thru C# such as FindWindow() and FindWindowEx() to automate a similar task.
Use FindWindow() to get a handle on the save as dialog box and FindWindowEx() to get a handle on any child, you can use SendMessage() and WM_SETTEXT to programmatically assign a new file name.
Hope this helps,
~Alexander Kent
|
|
|
|
|
I have been struggling with this for over 3 weeks now. Can you show me some examples on how something like this is done?
Gunnar
|
|
|
|
|
Gunnar, try this:
(Use Spy++ to get Class and WindowName)
public class Win32
{
public const int WM_SYSCOMMAND = 0x0112;
public const int WM_COMMAND = 0x0111;
public const int SC_CLOSE = 0xF060;
public const int WM_SETTEXT = 0x000C;
[DllImport("user32.dll")]
public static extern int FindWindow(
string lpClassName,
string lpWindowName
);
[DllImport("user32.dll")]
public static extern int SendMessage(
int hWnd,
uint Msg,
int wParam,
[MarshalAs(UnmanagedType.LPStr)] string str
);
[DllImport("user32.dll")]
public static extern int SendMessage(
int hWnd,
uint Msg,
int wParam,
int lParam
);
}
int iHandle = Win32.FindWindow("#32770", "File Download");
int k = Win32.SendMessage(iHandle, Win32.WM_COMMAND, 0x0001144, 0x00010370);
~Alexander Kent
|
|
|
|
|
Thank you, Thank you, Thank you, )
|
|
|
|
|
hi gunnar
even i want to achieve the same can u help me out do u have any source code to do this pls help
warm regards
vivek
|
|
|
|
|
Cool article, but I think it's important for people to realize that for many tasks, it's better just to use the framework classes to interact with websites in a non interactive manner. For instance, if you're datamining etc., I find it easier to use the framework classes. The only reason I mention this is I've seen people attempt to control the IE control for these situations where it's clearly overkill.
I once wrote a "validating browser" that browsed a website and put up warnings in a side panel about all non conforming HTML (used it to check the website I work on). The IE control was perfect for this task.
I, for one, do not think the problem was that the band was down. I think that the problem may have been that there was a Stonehenge monument on the stage that was in danger of being crushed by a dwarf.
-David St. Hubbins
|
|
|
|
|
// Because the DownloadComplete executes every time the axWebBrowser completes a page.
// A global variable will prevent recursive code executions.
DownloadComplete will be fired on every object of the page download complete.
like html, image, flash, etc.
DocumentComplete fires when axWebBrowser finished downloading page.
If page contains multiple frames multiple DocumentComplete will be issued, as well as for each Document multiple DownloadComplete will be fired.
I have no clue why your example even works in C#....
myDoc = (HTMLDocument) axWebBrowser1.Document; <- should have return NULL... as document is not yet ready, it was only downloaded...
And don't get me started on handling refresh / partial refresh / new window...
|
|
|
|
|
|
May be you should revisit those url's?
You f*cking attached it to wrong event!
Your solution is stupid, clearly there is a different btw object download completed and DOM document been available... If you try something like that with real C++, you would have gotten NULL 20% of the time, followed by exception.
MS has correct solution on they web site...
http://support.microsoft.com/?id=180366
The only reason I asked was I have really no clue, why C# is ok with that. May be because it runs slower?
|
|
|
|
|
Brian, it doesn’t really matter since:
a) My article focuses on MSHTML and not on axWebBrowser events
b) If you like it or not, it works either way
I appreciate your pointer that using the DocumentComplete event is a much wiser choice than using the DownloadComplete event and I will revise my code. But I am really disappointed you being incompetent to express yourselves in a reasonable manner and having to resort to swear words instead.
~Alexander Kent
|
|
|
|
|
Anger Management $$$$
It's only an example, it's useful to me.
Don't just cut and paste solutions into your code, check and verify them first.
|
|
|
|
|
See if this makes sense
The reason why Download_Complete works is because on this page, (www.google.com) there are only two downloads. 1) The main HTML page and 2) the Google image.
The only way you get into trouble is if 1) the Google image fires Download_Complete first or 2) the .Document property axWebBrowser1.Document is not available immediately after the HTML page downloads.
So when you tell the browser component to navigate to http://www.google.com, it begins downloading the HTML page. This page is very short, and probably gets served from cache. So I'm thinking that the HTML page downloads completely before the browser even begins downloading the image. So 1) probably never occurs.
Maybe some expert out there can tell me if 2) ever applies (ie, the Download_Complete for a HTML page fires, but the .Document property is not available until the Document_Complete event fires. My guess is that accessing the .Document property probably is synchronized some how so that it always returns the valid DOM object.
I know from my own experience that it is quite possible to navigate through the DOM object before all the images finish. So that tells me that .Document is available after the Download_Complete of the HTML page. But I do agree that the original code (which used Download_Complete, but did not check that the completed download was the HTML page) was in error.
But here is why I think C# is OK with that: In this case, the first Download_Complete will always correspond to the HTML page, which is short and simple. If you follow my analysis, it should be possible to write the same program with C++, (and attach to the Download_Complete event), and C++ will work the same way as C#. Although in either case, if you started with something more complicated than www.google.com, chances are the original example will not work.
To me, it seems clear that the reason C# works is not because C# is so slow that it cannot execute 3 statements in the time between the first Download_Complete and Document_Complete event. I would like to see a C++ example, written to attach to the Download_Complete event, which fails in the manner described.
|
|
|
|
|