Earlier Jack announced an exciting new feature in our beta2 release in the ability host WPF controls across AppDomain boundaries. This will allow add-ins to generate their own UI and to pass it back for display within the hosts window. Ever since the add-in model was concieved this has been near the top of our list for feature requests and I want to thank the WPF team for scrambling to get this work done in time for the 3.5 release.
With that in mind I'd like to finally update our calculator samples to be just a bit more interesting. The attached sample requires Orcas Beta 2 and demonstrates a fairly typical add-in scenario in which an AppDomain isolated add-in generates some UI at the request of the host and the host displays directly as part of the application. If you take a look at the screenshot below the lower-right hand corner of the application is the piece that the add-in is generating. Everything else is generated by the host either on its own or using data from its various add-ins. In this sample only one section of the host actually displays the add-ins content but there is nothing limiting that in the product: a host can display multiple FrameworkElements from one or more add-ins anywhere it wishes.
When you download the application you should play around with the "Graphing Calculator" add-in. It really shows how smooth the integration between the AppDomains are in that the add-in doesn't need to mearly display a static image but can accept rich interaction directly from the user even when hosted cross domain.
We'll go into more depth later about this technology, some of the scenarios it enables, and some of the gotcha's you might run into, but for now I'd like to just get the sample into your hands and give you a brief taste of how easy it is to integrate this into your add-in application by comparing the differences between the pipelines of the old calculator and the new graphical add-ins.
First let's take a look at the views:
Numerical Calculator:
[AddInBase] public abstract class Calculator { public abstract String Name { get; } public abstract IList<Operation> Operations { get; } public abstract double Operate(Operation op, double[] operands); }
Graphic Calculator:
[AddInBase] public abstract class VisualCalculator { public abstract String Name { get; } public abstract IList<Operation> Operations { get; } public abstract UIElement Operate(Operation op, double[] operands); }
Only one line has changed, instead of returning a double the view simply returns the basic WPF control UIElement. The changes to the host view are just as simple.
Now let's look at the adapters as this is where the real magic happens. This time we'll just compare the pieces of code that have changed.
On the add-in side:
public double Operate(Calculator.Contracts.IOperationContract op, double[] operands) { return _view.Operate(OperationViewToContractAddInAdapter.ContractToViewAdapter(op), operands); }
public INativeHandleContract Operate(Calculator.Contracts.IOperationContract op, double[] operands) { return VisualAdapters.ViewToContractAdapter(_view.Operate(OperationViewToContractAddInAdapter.ContractToViewAdapter(op), operands)); }
The only changes here is that instead of passing back the result directly across the boundary as a double we now pass it back across as an INativeHandleContract and use the new class VisualAdapters to convert from the UIElement the add-in passes back into the INativeHandleContract that gets passed across the boundary. System.AddIn.Pipeline.VisualAdapters is a new class in the new System.Windows.Presentation assembly that contains the core functionality that enables the cross-appdomain hosting; you'll see it's use again in the host side adapters to convert from the INativeHandleContract back into a WPF control.
public override double Operate(HostView.Operation op, double[] operands) { return _contract.Operate(OperationHostAdapters.ViewToContractAdapter(op), operands); }
public override UIElement Operate(HostView.Operation op, double[] operands) { return VisualAdapters.ContractToViewAdapter(_contract.Operate(OperationHostAdapters.ViewToContractAdapter(op), operands)); }
Again, the only changes required were to change the return value to UIElement and to use our new helper class to convert from the contract back into a a new UIElement. This method actually returns an object of type FrameworkElement that lives in the hosts AppDomain and can be manipulated and plugged into the visual tree as any local FrameworkElement can.
That should be enough to get everyone started, as I said we'll be following up with more info and samples in the weeks to come. If you have any questions about this new capability please just let us know.
Note: The attached sample was built for a pre-RTM version of .NetFX 3.5 and will not work on the RTM build. For an updated sample please see our codeplex site here: https://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=clraddins&ReleaseId=9454
I have developed some AddIns where I need to pass VisualBrush and Storyboards back to the Host. How can I do that in a clean way. Right now I have to use hacks to get it done...as in passing a dummy Border with its background set to the VisualBrush and its Resources containing the Storyboards. I would prefer a cleaner way of doing this.
Any suggestions?
Thanks for the great work you guys are doing!
Pavan
For questions about specific WPF controls and the add-in model please post questions on this forum:
http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=119&SiteID=1
The WPF team monitors that closely and there are developers there familiar with WPF and the add-in model support who can help you.
Thanks,
Jesse
Hi,
The attached sample does not run "out of the box" in visual studio 2008 beta 2.
Assembly references is missing, and no addins are found when run (after adding the required references).
Regards,
Lars Wilhelmsen
Sorry you're running into problems here. We tested this on a few machines before posting and have had at least a few people contact us through the blog who didn't run into problems, but it sounds like there may be a configuration out there that is still causing problems.
Just to clear a few things up. Is this a clean install of beta2 or were previous builds of 2008 installed on the machine? Which assembly reference did you need to add?
Finally, can you list out any warnings you get during discovery? You need to make the following change to the application to get these warnings:
In CalculatorHost.xaml.cs change the line that says
AddInStore.Rebuild(path);
to
String[] warnings = AddInStore.Rebuild(path);
Then put a break point after that and take a look at the warnings array.
OT, but is there an MSDN forum for Add-Ins?
There is no dedicated forum for the add-in model but we are instead part of the base class library forums.
You can find that forum here: http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=39&SiteID=1
--Jesse
A somewhat awkward but necessary first step... I am a Software Development Engineer on the WPF Application
I tried to use the email link, but your email link is out of date.
I am trying to write an add-in that hosts a frame in it. When a certain event occurs I want to update the frame source to point to a new web page. When I do I get:
A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
Additional information: Application identity is not set.
Then my frame disappers.
I have modified the calculator demo to show the problem.
Change: private System.Windows.UIElement Graph(double[] operands) as below.
Run. Click Push Next 5 time. Click Graph. Click Push Next 5 more times. Click Graph. Get error.
Code:
Frame f;
private System.Windows.UIElement Graph(double[] operands)
{
if (f == null)
f = new Frame();
f.Source = new Uri("http://blogs.msdn.com");
f.Width = 200;
f.Height = 200;
}
else
f.Source = new Uri("http://www.microsoft.com");
return f;
First, this is a great example!
I'm trying to activate the add-in's in a new AddInProcess, but when I try to activate the 'Graphic Calculator' I get an TargetInvocationException telling me the following:
System.Reflection.TargetInvocationException occurred
Message="Exception has been thrown by the target of an invocation."
Source="mscorlib"
StackTrace:
Server stack trace:
at System.RuntimeMethodHandle._InvokeConstructor(Object[] args, SignatureStruct& signature, IntPtr declaringType)
at System.RuntimeMethodHandle.InvokeConstructor(Object[] args, SignatureStruct signature, RuntimeTypeHandle declaringType)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
at System.AddIn.Hosting.ActivationWorker.Activate()
at System.AddIn.Hosting.AddInServerWorker.Activate(AddInToken pipeline, ActivationWorker& worker)
at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(RuntimeMethodHandle md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.AddIn.Hosting.AddInActivator.ActivateOutOfProcess[T](AddInToken token, AddInEnvironment environment, Boolean weOwn)
at System.AddIn.Hosting.AddInActivator.Activate[T](AddInToken token, AddInProcess process, PermissionSet permissionSet)
at System.AddIn.Hosting.AddInActivator.Activate[T](AddInToken token, AddInProcess process, AddInSecurityLevel level)
at System.AddIn.Hosting.AddInToken.Activate[T](AddInProcess process, AddInSecurityLevel level)
at DemoApplication.CalculatorHost.LoadAddIns() in C:\projects\AppDomain Isolated WPF Demo\DemoApplication\CalculatorHost.xaml.cs:line 213
InnerException: System.InvalidOperationException
Message="The calling thread must be STA, because many UI components require this."
Source="PresentationCore"
at System.Windows.Input.InputManager..ctor()
at System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
at System.Windows.Input.InputManager.get_Current()
at System.Windows.Input.KeyboardNavigation..ctor()
at System.Windows.FrameworkElement.EnsureFrameworkServices()
at System.Windows.FrameworkElement..ctor()
at System.Windows.Controls.Control..ctor()
at System.Windows.Controls.Button..ctor()
at GraphCalc.GraphingCalculator.StartButton() in C:\projects\AppDomain Isolated WPF Demo\Graphing Calculator\GraphingCalculator.cs:line 42
at GraphCalc.GraphingCalculator..ctor() in C:\projects\AppDomain Isolated WPF Demo\Graphing Calculator\GraphingCalculator.cs:line 21
InnerException:
What can I do to resolve this?
Thank you,
Marcel
Hi Jesse,
I did tried to rebuild your sample on VS2008 RC, and i guess it needs updates. In the VisualCalculator...HostAdapter and same in Visual....AddInAdapter it gives 2 errors (same actuallz twice) that VisualAdapters do not exist in the context. I solve it by replacing it with FrameworkElementAdapters (however in case of Visual..AddInAdapter i had to cast to FrameworkElement which anyway derives from UIElement). Otherwise, it works perfectly. Thank you, C. Marius
What about winforms? What if I wan't the plugin to add a control to my host?
Creating Add-Ins for WPF Applications [excerpts from upcoming SDK content] You’re unlikely to be reading
You’re unlikely to be reading this if you haven’t used the .NET Framework to build managed applications
Hola! I just returned from TechEd 2007 held in Barcelona, Spain. Barcelona is a beautiful city with incredible