One of my colleagues needed to modernize a tool we had for Excel. He asked me for suggestions, since he knew I’m into modern UI development. I know very little about the Excel API but after some googling I found out that you are limited to Winforms. There is however a control in Winforms called ElementHost that let you host WPF elements in Winforms. This coupled with a custom Bootstrapper for CM will enable you to develop sleek MVVM tools in Excel.
The Boostrapper will act as an entry point for our Caliburn enabled app. It needs to inherit from BootstrapperBase rather than the standard generic base class Bootstrapper of TModel.
public class Bootstrapper<TModel> : BootstrapperBase { private readonly StandardKernel kernel; public Bootstrapper(ElementHost host) : base(false) { kernel = new StandardKernel(); Start(); var model = kernel.Get<TModel>(); var view = ViewLocator.LocateForModel(model, null, null); ViewModelBinder.Bind(model, view, null); host.Child = view; } protected override void Configure() { kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope(); } protected override object GetInstance(Type service, string key) { return kernel.Get(service); } protected override IEnumerable<object> GetAllInstances(Type service) { return kernel.GetAll(service); } }
Its constructor takes the ElementHost as argument and then starts CM using the Start method on the base class. We need to hook up the ViewModel manually and its done like.
var model = kernel.Get<TModel>(); var view = ViewLocator.LocateForModel(model, null, null); ViewModelBinder.Bind(model, view, null);
First we create the ViewModel using Ninject, this way any dependecies for that model will be injected by Ninject. Then we ask CM to locate the WPF view using its convention based helper class ViewLocator. After we get the view we Bind it to the model using another CM helper class called ViewModelBinder.
Last but not least, we need to hook up the WPF element to the Winforms ElementHost.
host.Child = view;
Embedded resources
Alan Rutter asked me how to load embedded resources like themes etc.
App.xaml wont load in a none WPF project so you need to-do this manually like
var dict = new ResourceDictionary {Source = new Uri("Resources.xaml", UriKind.Relative)}; var app = new System.Windows.Application(); //This will load Application.Current Application.Current.Resources.MergedDictionaries.Add(dict);