Tuesday, February 07, 2006

Adobe SVG Viewer in .NET WinForms

I was working on a C# WinForms project where I had to embed the Adobe SVG Viewer in my form.  The only distribution available comes in the form of an ActiveX control.  The control becomes useful when you set its SRC property to some .svg file to render.  Unfortunately this must happen at design-time or else a ActiveXStateException is thrown:
System.AccessViolationException was unhandled
  Message="Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
  Source="Interop.SVGACTIVEXLib"
  StackTrace:
       at SVGACTIVEXLib.ISVGControl.set_SRC(String pVal)
       at AxSVGACTIVEXLib.AxSVGCtl.set_SRC(String value)
The control also offers setSrc and getSrc methods, but these fail with the same exception.  More inspection led to the discovery that the control's SRC attribute can only be set after being freshly dropped onto the design surface.  What to do? (answer follows...)

In my case, I wanted to generate SVG files at run-time using temporary file storage space, which meant the filename would not be predictable at design time.  A lot of Googling turned up an Adobe support article revealed that only an ActiveX host that implements IHtmlDocument2 can set the SRC attribute for security reasons, starting at their SVG 3.03 version. 

Well, seeing as I didn't want to rewrite a web browser, I decided to re-use the .NET component for IE.  I would wrap the SVG Viewer ActiveX control into a web browser control, inside a User Control that would hide the ugly workaround.  It turns out that very little code was required.  The result is that my SVG Viewer can effectively display whatever SVG I want at run-time, with no apparent side effects.

I include the code below, licensed with X11:
SVGViewer.cs
/// 
/// A user control that wraps up Adobe's SVG Viewer in an
/// IE web browser to allow for changing the source file
/// at run-time.
///

///
/// This is necessary due to:
/// http://support.adobe.com/devsup/devsup.nsf/docs/54114.htm
/// Also references:
/// http://www.csharphelp.com/archives/archive146.html
/// http://ryanfarley.com/blog/archive/2004/12/23/1330.aspx
/// http://www.adobe.com/svg/viewer/install/svgtest.html
///
public partial class SvgViewer : UserControl
{
public SvgViewer()
{
InitializeComponent();
}

const string svgHtml = "<html><head><style>body {{ margin: 0; padding: 0; }} </style><embed src='{0}' width='100%' height='100%' type='text/html; charset=UTF-8' />";

private string source;
[Bindable(true)]
[Category("Appearance")]
[Description("The SVG file to display.")]
public string Source
{
get { return source; }
set
{
source = value;
UpdateHtml();
}
}

protected virtual void UpdateHtml()
{
if (webBrowser1.Document.Body != null)
webBrowser1.Document.OpenNew(true);
webBrowser1.Visible = !string.IsNullOrEmpty(source);
if (string.IsNullOrEmpty(source)) return;
webBrowser1.Document.Write(string.Format(svgHtml, source));
}
}
SVGViewer.Designer.cs
namespace Phylogenetics
{
partial class SvgViewer
{
///
/// Required designer variable.
///

private System.ComponentModel.IContainer components = null;

///
/// Clean up any resources being used.
///

/// <param name="disposing" />true if managed resources should be disposed; otherwise, false.
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Component Designer generated code

///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///

private void InitializeComponent()
{
this.webBrowser1 = new System.Windows.Forms.WebBrowser();
this.SuspendLayout();
//
// webBrowser1
//
this.webBrowser1.AllowNavigation = false;
this.webBrowser1.AllowWebBrowserDrop = false;
this.webBrowser1.Dock = System.Windows.Forms.DockStyle.Fill;
this.webBrowser1.IsWebBrowserContextMenuEnabled = false;
this.webBrowser1.Location = new System.Drawing.Point(0, 0);
this.webBrowser1.MinimumSize = new System.Drawing.Size(20, 20);
this.webBrowser1.Name = "webBrowser1";
this.webBrowser1.ScriptErrorsSuppressed = true;
this.webBrowser1.ScrollBarsEnabled = false;
this.webBrowser1.Size = new System.Drawing.Size(150, 150);
this.webBrowser1.TabIndex = 0;
this.webBrowser1.Url = new System.Uri("about:blank", System.UriKind.Absolute);
this.webBrowser1.WebBrowserShortcutsEnabled = false;
//
// SvgViewer
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.webBrowser1);
this.Name = "SvgViewer";
this.ResumeLayout(false);

}

#endregion

private System.Windows.Forms.WebBrowser webBrowser1;

}
}

7 comments:

  1. Hi. I am in a similar situation and need to use the Adobe SVG viewer from c#.net WinForms. I get the same error and your solution seems elegant. However, I tried using your sample but could not get it to function.

    Do you have a more complete example of how to use your user control. Possibly a simple c# solution w/source? I am using VS 2005.

    Thanks in advance,
    Bob

    pcicorp@optonline.net

    Thanks in advance.

    ReplyDelete
  2. Hi,
    This is Neel, I have used your Source Code For Creating SvgViewer In my C# Application. i have aslo created User Control For that.But Similar Kind of Exception i m also facing while Compiling the Source Code.
    You have Written That U hd Wrapped The SvgViewer ActiveXControl in to The Web-browser. But I didn't find The code Related TO that..

    Please Give me Some SOlution For That.. Or Provide ME Some Other Sample Code FOr Creating SvgViewr Into My C#.Net Application.

    Thanks in Adbvance.

    ReplyDelete
  3. The Same Problem i am also facing As bob has faced. So Send The Appropriate Solution To my Gmail id.

    Thanks in Advance,

    ReplyDelete
  4. Neil and Bob,
    I've corrected the formatting of my post. Wow that was awful. I hope that helps you find the user control source code.

    ReplyDelete
  5. Hi Andrew,
    Thanks For the Formatted Source Code,But cn u tell me hw can u use this usercontrol to display Svg Document.

    Please Provide Me a source Code for this also.
    Waiting Fo That..
    Thanks in Advance.

    ReplyDelete
  6. Randy8:19 PM

    If everyone will just roll back to 3.02 of the Adobe SVG viewer, you will not have the problem you are talking about.

    ReplyDelete
  7. Hi ,
    I want to show a SVG file in ASP.Net Page and want editing into SVG(a map) file.How can we do this ? I tried it but not successes to desire result.
    Can it possible to take help of any DLL or any thing which i dont know .

    Hoping positive response...

    ReplyDelete