views:

1492

answers:

5

I'm using a winforms webbrowser control to display some content in a windows forms app. I'm using the DocumentText property to write the generated HTML. That part is working spectacularly. Now I want to use some images in the markup. (I also would prefer to use linked CSS and JavaScript, however, that can be worked around by just embedding it.)

I have been googling over the course of several days and can't seem to find an answer to the title question.

I tried using a relative reference: the app exe is in the bin\debug. The images live in the "Images" directory at the root of the project. I've set the images to be copied to the output directory on compile, so they end up in bin\debug\Images*. So I then use a reference like this "Images..." thinking it will be relative to the exe. However, when I look at the image properties in the embedded browser window, I see the image URL to be "about:blankImages/*". Everything seems to be relative to "about:blank" when HTML is written to the control. Lacking a location context, I can't figure out what to use for a relative file resource reference.

I poked around the properties of the control to see if there is a way to set something to fix this. I created a blank html page, and pointed the browser at it using the "Navigate()" method, using the full local path to the file. This worked fine with the browser reporting the local "file:///..." path to the blank page. Then I again wrote to the browser, this time using Document.Write(). Again, the browser now reports "about:blank" as the URL.

Short of writing the dynamic HTML results to a real file, is there no other way to reference a file resource?

I am going to try one last thing: constructing absolute file paths to the images and writing those to the HTML. My HTML is being generated using an XSL transform of a serialized object's XML so I'll need to play with some XSL parameters which will take a little extra time as I'm not that familiar with them.

+1  A: 

Here's what we do, although I should mention that we use a custom web browser to remove such things as the ability to right-click and see the good old IE context menu:

public class HtmlFormatter
{
    /// <summary>
    /// Indicator that this is a URI referencing the local
    /// file path.
    /// </summary>
    public static readonly string FILE_URL_PREFIX = 
        "file://";

    /// <summary>
    /// The path separator for HTML paths.
    /// </summary>
    public const string PATH_SEPARATOR = "/";
}


// We need to add the proper paths to each image source
// designation that match where they are being placed on disk.
String html = HtmlFormatter.ReplaceImagePath(
    myHtml, 
    HtmlFormatter.FILE_URL_PREFIX + ApplicationPath.FullAppPath + 
    HtmlFormatter.PATH_SEPARATOR);

Basically, you need to have an image path that has a file URI, e.g.

<img src="file://ApplicationPath/images/myImage.gif">
Ken Wootton
The built in web browser can be configured to not show the regular context menu: IsWebBrowserContextMenuEnabled = false
Peter
+1  A: 

I got it figured out.

I just pass the complete resolved url of the exe directory to the XSL transform that contains the HTML output with image tags:

XsltArgumentList lstArgs = new XsltArgumentList();
lstArgs.AddParam("absoluteRoot", string.Empty, Path.GetFullPath("."));

Then I just prefixed all the images with the parameter value:

<img src="{$absoluteRoot}/Images/SilkIcons/comment_add.gif" align="middle" border="0" />
Peter
A: 

I ended up using something that's basically the same as what Ken suggested. However, instead of manually appending the file prefix, I used the UriBuilder class to build the complete URI with the "file" protocol.

This also solved a subsequent problem when we tested the app in a more realistic location, Program Files. The spaces was encoded, but the OS couldn't deal with the encoded characters when the file was referenced using a standard system path (i.e. "C:\Program%20Files..."). Using the true URI value (file:///C:/Program Files/...) worked.

Peter
A: 

Alternatively, keep your normal style relative links, drop the HTML transforming code and instead embed a C# web server like this in your exe, then point your WebControl at your internal URL, like localhost:8199/myapp/

Roark Fan
A: 

Ken's code was missing a few things that it needed to work. I've revised it, and created a new method that should automate things a little.

Just call the static method as so:

html = HtmlFormatter.ReplaceImagePathAuto(html);

and all links in the html that match file://ApplicationPath/ will be swapped with the current working directory. If you want to specify an alternate location, the original static method is included (plus the bits it was missing).

public class HtmlFormatter
{

    public static readonly string FILE_URL_PREFIX = "file://";
    public static readonly string PATH_SEPARATOR = "/";
    public static String ReplaceImagePath(String html, String path)
    {
        return html.Replace("file://ApplicationPath/", path);
    }
    /// <summary>
    /// Replaces URLs matching file://ApplicationPath/... with Executable Path
    /// </summary>
    /// <param name="html"></param>
    /// <returns></returns>
    public static String ReplaceImagePathAuto(String html)
    {
        String executableName = System.Windows.Forms.Application.ExecutablePath;
        System.IO.FileInfo executableFileInfo = new System.IO.FileInfo(executableName);
        String executableDirectoryName = executableFileInfo.DirectoryName;
        String replaceWith = HtmlFormatter.FILE_URL_PREFIX
            + executableDirectoryName
            + HtmlFormatter.PATH_SEPARATOR;

        return ReplaceImagePath(html, replaceWith);
    }
}