5

I'm using a WebBrowser control in design mode.

webBrowser1.DocumentText = "<html><body></body></html>";
doc = webBrowser1.Document.DomDocument as IHTMLDocument2;
doc.designMode = "On";

I have a save button that I would like to enable or disable depending on whether the contents of the control have changed.

I also need to know when the contents of the control have changed as I need to stop the user from navigating away from the control without accepting a confirmation message box stating that their changes will be lost.

I can't find any events that would let me know that the contents have changed.

crdx
  • 1,412
  • 13
  • 26
  • Just don't. Design mode is for programmers, they have source control to clean up big messes. Spamming him with "are you sure" prompts is just annoying, he's sure 99.9% of the time. Write javascript if you want to do this anyway. – Hans Passant Jun 21 '12 at 13:21
  • What do you mean? This is a WinForms app that lets the user edit some HTML content within a web browser control. They select what they're editing from a treeview and I need to confirm with them that they are aware that they will lose their changes if they try to navigate away without saving. It's not a web app. – crdx Jun 21 '12 at 14:39

4 Answers4

3

There is no such event since DocumentText is a simple string. I would create a string variable storing the last saved text and check it at each KeyDown / MouseDown / Navigating event.

string lastSaved;

private void Form_Load(object sender, System.EventArgs e)
{
   // Load the form then save WebBrowser text
   this.lastSaved = this.webBrowser1.DocumentText;
}

private void webBrowser1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
    // Check if it changed
    if (this.lastSaved != this.webBrowser1.DocumentText)
    {
        // TODO: changed, enable save button
        this.lastSaved = this.webBrowser1.DocumentText;
    }
}

private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
    // Check if it changed
    if (this.lastSaved != this.webBrowser1.DocumentText)
    {
        // TODO: ask user if he wants to save
        // You can set e.Cancel = true to cancel loading the next page
    }
}
nxu
  • 2,202
  • 1
  • 22
  • 34
  • I don't see a KeyDown event for the System.Windows.Forms.WebBrowser - the only key event it supports is PreviewKeyDown. – Scott Baker Jul 23 '14 at 17:58
  • 1
    This doesn't take into account if there is `TAB` or arrow keys pressed. Also it doesn't register change when images are resized, moved etc. See my solution. – Coder12345 Jun 04 '21 at 20:42
3

QueryInterface() the document for IMarkupContainer2, then call IMarkupContainer2::RegisterForDirtyRange with your own implementation of IHTMLChangeSink. Your IHTMLChangeSink::Notify implementation will be called when a change is made.

Note: Do this after you set the design mode. The document gets reloaded and event hook get lost, if you toggle the design mode.

Coder12345
  • 3,431
  • 3
  • 33
  • 73
Sheng Jiang 蒋晟
  • 15,125
  • 2
  • 28
  • 46
  • This sounds like a good implementation, but it's a little too abstract for my skill level. Could you give a code example? – Scott Baker Jul 23 '14 at 17:26
0

another solution, if you implement IDocHostUIHandler, you can use its method UpdateUI

Lenor
  • 1,471
  • 10
  • 19
0

This seems to work - QueryInterface() for IPersistFile interface and use IPersistFile::IsDirty method with the return value of S_OK (if dirty or if the design mode document has been modified).

You don't need to use IPersistFile::Load to load the contents into the web browser, WebBrowser->Navigate() will also work fine.

Note - there is a same IsDirty() method also in IPersistStream and IPersistStreamInit interfaces

I don't use C# but it should be relatively easy to rewrite.

bool IsEditorDirty(WebBrowser* WB)
    {
    // beware, if it fails to get Document and IPersistFile it will not register as dirty
    bool isdirty = false;

    IHTMLDocument2* pDoc2 = WB->Document;

    if (pDoc2) {
        IPersistFile* pPFile;

        if (pDoc2->QueryInterface(IID_IPersistFile, (void**)&diPFile) == S_OK) {
            isdirty = (pPFile->IsDirty() == S_OK);
            pPFile->Release();
            }
    
        pDoc2->Release();
        }

    return isdirty;
    }
Coder12345
  • 3,431
  • 3
  • 33
  • 73