In an earlier post, I described a method of hosting windows controls that relied on a hidden form on the add-in side that hosted the add-in control, but appeared in a designated area of the host form.
There has been continued interest in using CLR Add-Ins with Windows Form, see the CLR Team’s post Support for Windows Forms in Hosts and Add-Ins.
While it work well graphically, some of the more detailed integration of ALT+ and CTRL+ keys, tab order, etc. did not work entirely well.
Following the advice of the Jesse Kaplan at the Microsoft CLR Add-In team, I have created a new sample using WPF and Crossbow to handle the pseudo-marshalling of the windows control.
This example uses VS2005 and, other than the add-in pipeline itself, it is, from the perspective of the host and add-in developer, a *pure* Windows Forms experience.
With the built in adapters for WPF in System.AddIn.Pipeline, in addition to WPF/Windows interop available via Crossbow (or the should I say the project formally know as Crossbow?), the code becomes almost trivial.
Here’s a quick run down of the pipeline:
The Add-In View
[AddInBase]
public interface IAddInControl
{
Control GetControl();
}
The Add-In Side Adapter
[AddInAdapter]
public class AddInControlVca : ContractBase, IAddInControlContract
{
private readonly IAddInControl addInControl_;
public AddInControlVca(IAddInControl addInControl)
{
addInControl_ = addInControl;
}
#region IAddInControlContract Members
public INativeHandleContract GetControl()
{
WindowsFormsHost host = new WindowsFormsHost();
host.Child = addInControl_.GetControl();
return FrameworkElementAdapters.ViewToContractAdapter(host);
}
#endregion
}
The Contract
[AddInContract]
public interface IAddInControlContract : IContract
{
INativeHandleContract GetControl();
}
The Host Side Adapter
[HostAdapter]
public class AddInControlCva : ElementHost
{
private readonly ContractHandle contractHandle_;
public AddInControlCva(IAddInControlContract contract)
{
Child = FrameworkElementAdapters.ContractToViewAdapter(contract.GetControl());
contractHandle_ = new ContractHandle(contract);
}
}
Consuming The Add-In
Collection<AddInToken> tokens = GetAddInTokens();
foreach (AddInToken token in tokens)
{
TabPage page = new TabPage(token.AddInFullName);
tabControl1.TabPages.Add(page);
Control addInControl = token.Activate<Control>(
AddInSecurityLevel.FullTrust,
string.Format(“AddInControlDomain{0}”,
tabControl1.TabPages.IndexOf(page)));
addInControl.Dock = DockStyle.Fill;
page.Controls.Add(addInControl);
}
[...] I wrote this initial example before Jesse Kaplan suggested using WPF and Crossbow to implement Windows Control Add-in integration. My newer post here describes this technique. The approach below may still be valid if seen as a *lightweight* technique if the overhead of loading WPF is too great and Hot Key integration is not important. [...]
Pingback by software greenhouses » Windows Controls Extensibility with .NET 3.5 CLR Add-Ins Using a “Leased-Space” Model — January 14, 2008 @ 2:00 am
How do you do what your sample above achieves but using FORMS, not user controls?
Comment by Eric Brand — February 15, 2008 @ 7:48 pm
Thanks for this article.
I was wondering – givin the security demand for FullTrust inside of the WindowsFormsHost, how would one host Windows Forms controls inside of AddIns that are only partially trusted (much of the reason that I use AddIns is becuase I can limit their capabilities).
Comment by Adam Langley — March 5, 2008 @ 9:17 pm
@Eric Brand
Why would you need to marshall the form to the host side? Why not just run it on the add-in side?
Or are you thinking of an MDI scenario? In that case I would have a host form on the host side and just fill it with a control from the add-in side…
Comment by Marty Nelson — April 6, 2008 @ 9:24 pm
@Adam -
I haven’t tried it with different security settings yet. My understanding is that the handle to the add-in control is a fairly low-level Win32 handle, so it’s mostly rendering and eventing, the add-in control code is still running on it’s own side.
Comment by Marty Nelson — April 6, 2008 @ 9:26 pm
Hi!,
i tried the solution and it works great, but is there a way to change the activation of the AddIn??
i’m using spring, and i’ve configured the user controls in my config file, but everytime an addin gets activated, it creates a new instance of the Control without actually querying spring for the configured instance, ( i’ve modified the Control GetControl() method to get the actual spring instance ) but i still have two instances of the same control….
another question is, why is the behavior of special keys like tab and ctrl+ and alt+ keys changed?? is there anyway to fix this issue??
thanks!
Comment by Leonardo — May 21, 2008 @ 10:19 pm
Leonardo -
When you activate an add-in, it is going to create a new one, that’s just how CLR add-in’s work. So you need to hold on to the activated instance if you want to reuse the underlying control. So you might go through a caching class of some kind that looks up the add by token name and either returns the already activated add-in if it exists, or activates (creates) it if it doesn’t and adds it to the cache.
The issue with ctrl+ and alt+ keys should work fine with the WPF/Windows interop. It was my first attempt without the interop that had these deficiencies.
Comment by Marty Nelson — May 28, 2008 @ 3:56 am
Thanks Marty,
About the control and alt keys, they do act strange on a modified version of your sample.
I modded the sample to create a MDI parent form, each of the HostedControls get inserted on a new form instance, everything seems fine until you press the control key or Tab+, the expected behavior would be to change the focus on each of the controls inside the form, but, the behavior i get is swapping between the different hosted forms in the mdi, maybe i’m doing something wrong, but i just wanted to know your comment to see if you get the same behavior.
thanks for your comments
Comment by Leonardo — June 9, 2008 @ 2:34 pm
Hi,
Is it to possible to unload the AppDomain where Add-in was created? When I tried to uload the add-in’s AppDomain, it closes the main application silently. Please find below the code I tried and advise me if something is wrong here.
private void somethingToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show(”Something”);
Control ctrl = tabControl1.TabPages[0].Controls[0];
tabControl1.TabPages.RemoveAt(0);
AppDomain domain = AddInController.GetAddInController(ctrl).AppDomain;
ctrl = null;
GC.Collect();
AppDomain.Unload(domain);
}
Thanks in advance.
Comment by Ram — June 30, 2008 @ 2:11 am
Could you please post a code snippet on how to uload the AppDomain in your sample?
Comment by Mike — June 30, 2008 @ 1:11 pm
What about loading the add-ins in a separate process?
I tried this and I get an error telling me the control can’t be created because the thread isn’t STA. Any idea how to get the creation thread STA using a separate process?
Comment by Judah Himango — March 5, 2009 @ 4:56 am