A few folks have asked how to call managed code from the HTML\JavaScript extensibility layer within Expression Web 4.
Hope this helps!
John
1. Create a c# Class library project in Visual Studio, for this sample call it 'web'
2. mark the class as ComVisible
namespace web{ [ComVisible(true)] public class Class1 { }}
3. This will cause a compile time error, you will need to add the following using statement:using System.Runtime.InteropServices;
4. Next implement Extensibility.IDTExtensibility2 in your class:
namespace web{ [ComVisible(true)] public class Class1 : Extensibility.IDTExtensibility2 { }}
5. This will cause a compile time error, you will need to add a reference to:C:\Program Files (x86)\Common Files\microsoft shared\MSEnv\PublicAssemblies\extensibility.dllor for 32 bit machinesC:\Program Files\Common Files\microsoft shared\MSEnv\PublicAssemblies\extensibility.dll
6. You will still have a compile time error, you will need to stub out this interface: #region IDTExtensibility2 Members [ComVisible(true)] public class Class1 : Extensibility.IDTExtensibility2 { public void OnAddInsUpdate(ref Array custom) { throw new NotImplementedException(); }
public void OnBeginShutdown(ref Array custom) { throw new NotImplementedException(); }
public void OnConnection(object Application, Extensibility.ext_ConnectMode ConnectMode, object AddInInst, ref Array custom){}
public void OnDisconnection(Extensibility.ext_DisconnectMode RemoveMode, ref Array custom) { throw new NotImplementedException(); }
public void OnStartupComplete(ref Array custom) { throw new NotImplementedException(); } } #endregion
7. At this point you should be able to build, the last thing you will want to do is create a method to call from your add-in, something like this should suffice for testing: #region JavaScript implementation public Class1(){} //CTor public String Version() { return "1.0"; } //Version info public String TestFunction() { return "MyTestFunction"; } //My test method to call #endregion
8. Next up is adding a JavaScript mapping in your manifest. A few bits of information will be needed
a. your DLL name on diskb. your namespacec. your class name
edit your manifest and a load element within the <addin> element, something like:
<addin><name>My</name><description>My</description><version>1.0</version><load type="web.Class1, web" name="utils" /><panel id="My" title="My" src="my.html" /></addin>
Looking at the load element in depth:
type="web.Class1, web" web.class1, web == <namespace>.<classname>, <dll name on disk>
AND
name="utils"utils is the namespace you wish to use in JavaScript to eventually call your methods (more later, but eventually we will call utils.TestFunction() from JavaScript).
9. Given you are following the code above you should create this manifest on disk as addin.xml, copy your web.dll right next to it.
10. create the html page, for this blog I use my.html, with the contents to follow, place it next to the addin.xml and your dll
<html><head></head><body onload="document.write(utils.TestFunction())"></body></html>
11. copy all of your files (addin.xml, web.dll, my.html to a new directory call 'my' (or any name you prefer) in %appdata%\Microsoft\Expression\Web 4\Addins
12. Run Web 4
13. Under the panels meny you should see My, click that and you will see the return value from public String TestFunction() { return "MyTestFunction"; }placed into your document, in this case "MyTestFunction".
There are many usages that come to mind when looking at this functionality, passing a file location of a JPG and resizing it in managed code and then returning the URI or file path back to javascript is one example.
Hope you guys find this useful, if you have any problems I wanted to call out a few common issues I have seen people fall into1. Make sure your class is public2. make sure your methods are public
Enjoy!
I was asked to convert this to vb which seemed simple enough, however it came up with the following error in the ew log:
Error: Could not load type 'WebLib.Utilities' from assembly 'WebLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Here's the conversion:
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Runtime.InteropServices
Namespace WebLib
<ComVisible(True)> _
Public Class Utilities
Implements Extensibility.IDTExtensibility2
#Region "IDTExtensibility2 Members"
Public Sub OnAddInsUpdate(ByRef custom As Array) Implements Extensibility.IDTExtensibility2.OnAddInsUpdate
Throw New NotImplementedException()
End Sub
Public Sub OnBeginShutdown(ByRef custom As Array) Implements Extensibility.IDTExtensibility2.OnBeginShutdown
Public Sub OnConnection(ByVal Application As Object, ByVal ConnectMode As Extensibility.ext_ConnectMode, ByVal AddInInst As Object, ByRef custom As Array) Implements Extensibility.IDTExtensibility2.OnConnection
Public Sub OnDisconnection(ByVal RemoveMode As Extensibility.ext_DisconnectMode, ByRef custom As Array) Implements Extensibility.IDTExtensibility2.OnDisconnection
Public Sub OnStartupComplete(ByRef custom As Array) Implements Extensibility.IDTExtensibility2.OnStartupComplete
#End Region
#Region "JavaScript implementation"
Public Sub New()
Public Function Version() As String
Return "1.0"
End Function
#Region "String Helpers"
Public Function ToTitleCase(ByVal strText As String) As String
Dim strConvertedText As String = ""
Try
Dim cultureInfo As System.Globalization.CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture
Dim TextInfo As System.Globalization.TextInfo = cultureInfo.TextInfo
strConvertedText = TextInfo.ToTitleCase(strText.ToLower())
Catch
strConvertedText = strText
End Try
Return strConvertedText
End Class
End Namespace
Thanks,
-krr
Played around with this, repro'd the problem and did finally get it to work...
One thing I saw was the namespace VS and VB was giving my .DLL was doubled up (weird). You can check this in .NET Reflector (or just email me your DLL and I can look). But for grins try WebLib.WebLib.Utilities as your namespace in addin.xml (load tag). Note I also changed the property settings in VS to insure Com Visible (VS2010: Properties Application tab, then Click Assembly information. Also on the Properties-Compile tab check the Register for COM interop), lastly I signed the DLL (not sure if this is actually needed) - Properties Signing tab. Let me know!!!!
Well, that was strange. I did have the doubled up namespace problem. I had already set the Com Visible and the Register for COM interop, however I didn't sign the dll so it appears that's not needed. It all boiled down to the doubled up namespace.
I do have yet another question. Where is the information for the xweb.legacyapp object? It's not listed on the Expression Web SDK page that I can find.
Thanks so much for all your help.
No prob, glad to help! The namespace is indeed odd, took me a while to figure that one out!
Regarding LegacyDoc, I am not sure they actually posted help on that (sadly). That said the frontpage OM is online and should map rather closely, let me know and I can see if I can find the MSDN link!
I added references to Microsoft.Expression.Interop.WebDesigner and
Microsoft.Expression.Interop.WebDesignerPage, then used the object browser to examine them. It looks like these map fairly well.
I sent you an email via the blog. We can't get this to work at all and we have been creating adds from FrontPage 98 to Expression web 2.