I built a very simple extension of the Winforms WebBrowser
class called EditableWebBrowser
, which works pretty well except when the instantiation is randomly removed from the designer code in the form where it's used. I also seem to get some occasional hanging behavior when starting the app. There's nothing special (the custom parts are primarily string manipulation) except for 2 things:
- It uses the SHDocVw reference in order to access the IHTMLDocument2 object.
- It uses MSHTML advanced interfaces in order to execute the save routine without prompting the user (using the ExecWB call).
Neither the SHDocVw nor MSHTML interfaces are used in the constructor, though, and they aren't invoked when the freezing occurs.
The control looks like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;
using SHDocVw;
using mshtml;
namespace WindowsFormsApp1.UserControls
{
public partial class EditableWebBrowser : System.Windows.Forms.WebBrowser
{
public string TempFile = "";
public string TempFileURL { get { return "file:///" + TempFile.Replace('\\', '/'); } }
// Private base constructor - we always want the parameterized public constructor
private EditableWebBrowser()
{
InitializeComponent();
}
public EditableWebBrowser(bool editableOnDocumentCompleted = true, string localFile = "tmpEditableWebBrowser.html") : this()
{
// Set temp file
this.TempFile = Path.GetTempPath() + localFile;
// Make the loaded document editable?
if(editableOnDocumentCompleted)
{
this.DocumentCompleted += WebBrowser1_DocumentCompleted;
}
}
// Handler for making the loaded document editable
private void WebBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
IHTMLDocument2 doc = (IHTMLDocument2)this.Document.DomDocument;
doc.designMode = "On";
}
// Some Regex objects and some custom methods here
private string _cleanupImportedHTML(string HTML)
{
...
}
public void Import(string HTML = null)
{
...
}
public void Clear()
{
...
}
public void SaveWithoutPrompt()
{
// Execute the browser's "Save" command without prompting
object inVal = "";
object outVal = "";
((IWebBrowser2)this.ActiveXInstance).ExecWB(SHDocVw.OLECMDID.OLECMDID_SAVE, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, ref inVal, ref outVal);
}
}
}
In the designer of the form where it's used (frmMain.Designer.cs), it's simply:
this.ewbContentEditor = new UserControls.EditableWebBrowser();
This is the line that is occasionally removed without any apparent pattern. However, the rest of the designer code that references it still stays in place, which obviously breaks the rest of the designer code that references it:
//
// ewbContentEditor
//
this.ewbContentEditor.Dock = System.Windows.Forms.DockStyle.Fill;
this.ewbContentEditor.Location = new System.Drawing.Point(3, 3);
this.ewbContentEditor.MinimumSize = new System.Drawing.Size(20, 20);
this.ewbContentEditor.Name = "ewbContentEditor";
this.ewbContentEditor.Size = new System.Drawing.Size(599, 375);
this.ewbContentEditor.TabIndex = 3;
I'm wondering if the issue could be related to the fact that the default parameters for the public constructor subscribe to a DocumentCompleted event, but that's my only idea at this point, and I don't know of a way to positively confirm that theory (without a definitive pattern, I don't want to just try something and assume it's fixed if it doesn't break soon).
Anyone else have any ideas or see the problem?
Also, the MSHTML interfaces were implemented using the approach described in this article: https://www.codeproject.com/Articles/2491/Using-MSHTML-Advanced-Hosting-Interfaces