How do I load HTML into Webbrowser control for WPF?
My WPF client application is building a custom HTML page, I can load the HTML like this,
this.myWebBrowser.NavigateToString("<html><body><p>test page</p></body></html>");
The problem I have is how do I load custom HTML into the Webbrowser control, if the HTML has images and css file in it?
How do you 开发者_如何学JAVAreference the images and css? Can the images and css be embedded into the application, or do they need to be in a directory somewhere?
Thanks for the help.
Actually, there is such a thing as an HTML page with embedded images.
You can embed both images and CSS inside an HTML string, using the data URI scheme. However, not all versions of IE support this, and the WebBrowser control is really IE. If your code might run on a machine with IE < 8, this approach won't help you.
Another option would be to store your images and CSS as embedded resources, write them out to temp files, and then insert absolute URL's to the temp files when you generate your HTML.
Since you are generating the HTML, you can set the base url of all script/image by generating a base element which points to somewhere you have control over, like file:/// url pointing to a folder on the disk, a url pointing to localhost where your local web server is listening (too many http server samples on the internet so I don't get into details here), or your web site on the www.
When you navigate to string, the HTML would be rendered in the same interenet zone as about:blank. Depending on the client's IE security zone settings, your HTML may not have have permission to load files in certain locations (like script files on the computer if the page is in the Internet zone).
You can use the Mark of the Web to change your HTML page's internet zone.
For the reference, I've stumbled upon this question and used the Joel Mueller answer, but decided to automate parsing the image paths. Maybe somebody will found my code useful for a starter of a similar solution - it's quite rough and dirty, but seems to do the job well.
I am using C# resources, inside my html files I only put image filenames. It also allows me to open the file in a normal browser for testing how it'll look. The code automatically looks for the resource with the same name as filename inside the html, saves it under application data directory, and substitutes the path.
It's also worth noting that the application will overwrite the file if it differs from the already saved one. It was mainly needed for the development, but I didn't really had many of those files, so I didn't care about the performance loss here. Omitting this check and assuming the files are up-to-date should enhance the performance.
Settings.Default.ApplicationId
is a simple string with an application name, used for the directory name inside application data.
That's how my class ended up looking:
class MyHtmlImageEmbedder
{
static protected string _appDataDirectory =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Settings.Default.ApplicationId);
static public string getHtml()
{
String apiHelp = Resources.html_file;
Regex regex = new Regex(@"src=\""(.*)\""", RegexOptions.IgnoreCase);
MatchCollection mc = regex.Matches(apiHelp);
foreach (Match m in mc)
{
string filename = m.Groups[1].Value;
Image image = Resources.ResourceManager.GetObject(Path.GetFileNameWithoutExtension(filename)) as Image;
if (image != null)
{
var path = getPathTo(Path.GetFileNameWithoutExtension(filename) + ".png", imageToPngByteArray(image));
apiHelp = apiHelp.Replace(filename, path);
}
}
return apiHelp;
}
static public string getPathTo(string filename, byte[] contentBytes)
{
Directory.CreateDirectory(_appDataDirectory);
var path = Path.Combine(_appDataDirectory, filename);
if (!File.Exists(path) || !byteArrayCompare(contentBytes, File.ReadAllBytes(path)))
{
File.WriteAllBytes(path, contentBytes);
}
return path;
}
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int memcmp(byte[] b1, byte[] b2, long count);
public static bool byteArrayCompare(byte[] b1, byte[] b2)
{
// Validate buffers are the same length.
// This also ensures that the count does not exceed the length of either buffer.
return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;
}
public static byte[] imageToPngByteArray(Image image)
{
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms.ToArray();
}
}
Please note that the byteArrayCompareFunction uses memcmp only for performance reasons, but it can be easily substituted with a simple compare loop.
Then, I'm just calling browser.NavigateToString(MyHtmlImageEmbedder.getHtml());
.
You would do it exactly as with every other HTML page. The images and CSS would be referenced by URL, just like with any other HTML page. There's no magic here.
<WebBrowser Height="200" Name="myWebBrowser"></WebBrowser>
myWebBrowser.NavigateToString("EnterHtmlString");
精彩评论