I am using the MVVMLight toolkit for my WPF application.
Now I was going through the demo sample from Lauren's MIX 10.
The sample code is in SL, and makes use of the UnityContainer.
The template provided by MVVMLight toolkit for WPF does not utilizes the unitycontainer concept. How can I make use of the UnityContainer in WPF.
I don't now if my question even makes sense. I do not see any documentation on how to use the ViewModelLocator. Maybe some one can provide a sample or a WPF version of the Demo used by Lauren in MIX
The way I use Unity on WPF (MVVM Light) is like this:
I create a bootstrapper class on the application root, something like:
public class Bootstrapper
{
public IUnityContainer Container { get; set; }
public Bootstrapper()
{
Container = new UnityContainer();
ConfigureContainer();
}
private void ConfigureContainer()
{
Container.RegisterType<IMyRepo, MyRepo>();
Container.RegisterType<MainViewModel>();
}
}
This is my bootstrapper. I register the ViewModels too because is easy create them in the Locator.
Next, I create the boostrapper on the ViewModelLocator's constructor and I resolve every ViewModel here, like:
public class ViewModelLocator
{
private static Bootstrapper _bootStrapper;
static ViewModelLocator()
{
if (_bootStrapper == null)
_bootStrapper = new Bootstrapper();
}
public MainViewModel Main
{
get { return _bootStrapper.Container.Resolve<MainViewModel>(); }
}
}
As you see, my ViewModelLocator is simple, it just create the bootstrapper and resolve the ViewModel, and these VM will resolve their dependencies through the container too :)
Maybe there is a best way to archieve this, but this is a good start indeed.
I would advise to use Managed Extensibility Framework. It's in .NET 4 and I switched myself from unity to MEF. I works very great when your app is growing. You can find lots of info on it by search using google.
Good luck!
Related
I am new in WPF and Prism. I'd like to know if I should create new bootstrapper for each new window? For Example I have "Window1" where I select element from ListBox and click button "ShowDetails" and in the new window "Window2" I should see the details of my selection. I have windows and modules for them, but I'd like to know how and where I can register the module "Module2" for "Window2"?
Example of my Bootstrapper.
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
var mainWindow = new Window1();
mainWindow.Show();
return mainWindow;
}
protected override IModuleCatalog GetModuleCatalog()
{
var moduleCatalog = new ModuleCatalog();
moduleCatalog.AddModule(typeof(Module1));
return moduleCatalog;
}
}
"App.xaml.cs"
public partial class App : Application
{
public App()
{
var bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
The Bootstrapper is used usually in the startup class of a WPF Application. Usually this will be the file App.xaml.cs in the standard template, which is the code-behind class of the App.xaml file. You override the method OnStartup and instantiate your Bootstrapper and call its run method. You can delay the startup of the bootstrapper until the override of OnStartup instead of writing this in the constructor of the App.xaml.cs class. You will then use the RegionManager in Prism and define regions in your XAML. If you have multiple independent Windows this is a bit different from the way Prism is intended to be used. There is the concept of a MainWindow or Shell which you define in the CreateShell method of the Bootstrapper class which is available in the Prism source code. Instead, have a main window and define regions and perhaps consider creating a mechanism for displaying additional windows in dialogs. It is possible partition up the MainWindow into multiple regions and inject user controls via the RegionManager. This is done via the activate method of the RegionManager.
Start up by reading the Patterns And Practices Guide and perhaps consider watching the videos of Mike Taulty upon Prism. The first video is here:
Prism & Silverlight: Part 1 - Taking Sketched Code Towards Unity
There are many videos in the video series (10 in total) that will help you get started with PRISM.
An example of how to define a region in XAML is shown next:
<ItemsControl Regions:RegionManager.RegionName="MainRegion" />
A PRISM region can be activated, e.g. through a DelegateCommand or ICommand bound to a button is the following code:
var viewA = new ViewA();
var regionA = (new RegionManager()).Regions["RegionA"];
regionA.Activate(viewA);
You will have to define multiple modules that implement the IModule Interface and add these to your ModuleCatalog as you already have done with ModuleA.
We have a (massive) legacy WinForms app which, through a menu item, opens up a WPF form. This WPF form will host an Infragistics grid, and some buttons/drop-downs.
This lone WPF form represents the nascent stage of a migration to WPF. Later on, more components of the app will move to WPF, and ultimately the entire app itself.
As part of the migration, we would like to use Caliburn Micro. Hence, it would be nice if we could start by using it with this lone WPF form.
Can someone please provide some pointers on how to use Caliburn Micro with the WPF form?
Or perhaps tell me why it may not make sense to use Caliburn Micro just yet?
The documentation I've read so far involves boot strappers that ensure the application starts with the desired root view model, rather than the scenario above.
Many thanks!
After much Googling and going through the Caliburn Micro source code, I've come up with an approach that works in a sample test application. I can't post the test application here for certain reasons, but here's the approach in a nutshell.
Create a WinForm with a button.
On button click, show a ChildWinForm
In the load handler of the ChildWinForm:
// You'll need to reference WindowsFormsIntegration for the ElementHost class
// ElementHost acts as the "intermediary" between WinForms and WPF once its Child
// property is set to the WPF control. This is done in the Bootstrapper below.
var elementHost = new ElementHost{Dock = DockStyle.Fill};
Controls.Add(elementHost);
new WpfControlViewBootstrapper(elementHost);
The bootstrapper above is something you'll have to write.
For more information about all it needs to do, see Customizing the Bootstrapper from the Caliburn Micro documentation.
For the purposes of this post, make it derive from the Caliburn Bootstrapper class.
It should do the following in its constructor:
// Since this is a WinForms app with some WPF controls, there is no Application.
// Supplying false in the base prevents Caliburn Micro from looking
// for the Application and hooking up to Application.Startup
protected WinFormsBootstrapper(ElementHost elementHost) : base(false)
{
// container is your preferred DI container
var rootViewModel = container.Resolve();
// ViewLocator is a Caliburn class for mapping views to view models
var rootView = ViewLocator.LocateForModel(rootViewModel, null, null);
// Set elementHost child as mentioned earlier
elementHost.Child = rootView;
}
Last thing to note is that you'll have to set the cal:Bind.Model dependency property in the XAML of WpfControlView.
cal:Bind.Model="WpfControls.ViewModels.WpfControl1ViewModel"
The value of the dependency property is used passed as a string to Bootstrapper.GetInstance(Type serviceType, string key), which must then use it to resolve the WpfControlViewModel.
Since the container I use (Autofac), doesn't support string-only resolution, I chose to set the property to the fully qualified name of the view model. This name can then be converted to the type, and used to resolve from the container.
Following up on the accepted answer (good one!), I'd like to show you how to implement the WinForms Bootstrapper in a ViewModel First approach, in a way that:
You won't have to create a WPF Window and,
You won't have to bind directly to a ViewModel from within a View.
For this we need to create our own version of WindowManager, make sure we do not call the Show method on the Window (if applicable to your case), and allow for the binding to occur.
Here is the full code:
public class WinformsCaliburnBootstrapper<TViewModel> : BootstrapperBase where TViewModel : class
{
private UserControl rootView;
public WinformsCaliburnBootstrapper(ElementHost host)
: base(false)
{
this.rootView = new UserControl();
rootView.Loaded += rootView_Loaded;
host.Child = this.rootView;
Start();
}
void rootView_Loaded(object sender, RoutedEventArgs e)
{
DisplayRootViewFor<TViewModel>();
}
protected override object GetInstance(Type service, string key)
{
if (service == typeof(IWindowManager))
{
service = typeof(UserControlWindowManager<TViewModel>);
return new UserControlWindowManager<TViewModel>(rootView);
}
return Activator.CreateInstance(service);
}
private class UserControlWindowManager<TViewModel> : WindowManager where TViewModel : class
{
UserControl rootView;
public UserControlWindowManager(UserControl rootView)
{
this.rootView = rootView;
}
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
if (isDialog) //allow normal behavior for dialog windows.
return base.CreateWindow(rootModel, isDialog, context, settings);
rootView.Content = ViewLocator.LocateForModel(rootModel, null, context);
rootView.SetValue(View.IsGeneratedProperty, true);
ViewModelBinder.Bind(rootModel, rootView, context);
return null;
}
public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
CreateWindow(rootModel, false, context, settings); //.Show(); omitted on purpose
}
}
}
I hope this helps someone with the same needs. It sure saved me.
Here are somethings you can start with
Create ViewModels and inherit them from PropertyChangedBase class provided by CM framework.
If required use the EventAggregator impelmentation for loosly coupled communication \ integration
Implement AppBootStrapper without the generic implementation which defines the root view model.
Now you can use the view first approach and bind the view to model using the Bind.Model attached property on view. I have created a sample application to describe the approach here.
For those that don't know, you can mark an assembly with the PreApplicationStartMethod, which will define a method that gets called before Application_Start in an ASP.NET site (if you're using .NET 4). I love using this in an Onion Architecture for defining a method that does all the setup for Dependency Injection.
My question is... is there any equivalent way of doing the same thing for a thick client application, such as one written in WPF?
For a WPF application it doesn't make much sense to mark an assembly with an attribute, since you are in control of which code will execute anyway.
A good place to do this initialization would be the OnStartup method.
In your App.xaml remove the StartupUri="MainWindow.xaml"
Then in your App.xaml.cs I do this:
public partial class App : Application
{
private IWindsorContainer _container;
private IView _view;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
_container = new WindsorContainer();
/// Register your interfaces with your concrete implementations.
// we'll do View first in this example (some do view first others do ViewModel first)
_view = _container.Resolve<IView>();
_view.Show();
}
}
I have a ViewModelLocator class that I am defining in app.xaml which is used by my views to databind to the correct ViewModel.
DataContext="{Binding HomeViewModel, Source={StaticResource Locator}}"
I am using Prism and Unity and my ViewModelLocator class needs a reference to an application level unity container.
I wanted inject the IUnityContainer into the ctor of the ViewModelLocator but the ViewModelLocator gets instanciated from app.xaml using a parameterless ctor.
Is there a preferred way to get access to an application level container - for all other classes in the app I just use ctor injection to get the global container.
What I am currenlty doing for the ViewModelLocator is defining a static variable in my BootStrapper class to store the container. I create the container by overriding the CreateContainer method on UnityBootStrapper.
protected override IUnityContainer CreateContainer()
{
BootStrapper.DIContainer = base.CreateContainer();
return BootStrapper.DIContainer;
}
Then in the ViewModelLocator class I just reference the BootStrapper.DIContainer property to register my viewmodels
BootStrapper.DIContainer.RegisterType<IShellViewModel, DesignShellViewModel>();
This works fine but it is the only place in the application that needs to reference this static property on the bootstrapper - and would like to get rid of it if possible.
thanks
Michael
I had the same issue while converting a Silverlight RIA Business app of mine to use Prism, Unity, and MVVM light toolkit. I came up with this workaround which is to just let the App.xaml create an instance of my ViewModelLocator class and during the application startup event I remove that instance it created from the application resources and re-add an instance using Unity container's Resolve method.
Register the VML with Unity.
Boostrapper.cs: (UnityBootstrapper class)
protected override void ConfigureContainer()
{
Container.RegisterType<ViewModelLocator>(new ContainerControlledLifetimeManager());
base.ConfigureContainer();
}
Use either constructor or property injection in the VML for IUnityContainer. Here I'm using property injection. Also note that the default parameterless constructor is required because App.xaml is going to instantiate its own instance that we'll just wind up throwing away.
ViewModelLocator.cs: (used for blendability)
public class ViewModelLocator
{
[Dependency]
public IUnityContainer Container { get; set; }
public ViewModelLocator() { }
....
}
Remove and re-add the VML to the application resources. Replace the string literal "Locator" with whatever you called your VML in the App.xaml ResourceDictionary section.
App.xaml.cs:
private void Application_Startup(object sender, StartupEventArgs e)
{
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
Resources.Remove("Locator");
Resources.Add("Locator", bootstrapper.Container.Resolve<ViewModelLocator>());
}
Your now cocked, locked, and ready to rock..
I thought I'd follow-up on this since it hasn't been marked as answered.
I've followed a similar approach to Degree451 except I don't remove and re-add the locator as that smells a bit. Instead, I use the built-in capabilities of both Silverlight as well as Unity to handle the problem.
Having the Container property in the ViewModelLocator class marked with the DependencyAttribute means that the class can have its dependencies resolved after instantiation. So in my Bootstrapper, I override ConfigureContainer and add the following code:
var vml = Application.Current.Resources["ViewModelLocator"] as ViewModelLocator;
Container.BuildUp(typeof(ViewModelLocator), vml);
The first line retrieves the instance automatically created by the application from the App.xaml markup. The second line uses Unity to resolve any properties marked with the DependencyAttribute.
To me, this is a much cleaner solution. Of course, it's not Blendable.
I am planning to create a WPF application with a main window which would launch various WinForms. Some of the WinForms use the System.Windows.Forms.Application class (DoEvents, Application.Path, etc). Do you think that there will be a problem in doing this?
Can I still use System.Windows.Forms.Application.DoEvents() from a WinForm that is launched from a WPF application?
The main problem will the ability to instantiate the Windows Forms window and set it's owner to that of the WPF window. The Winforms will want a IWin32Window which a WPF window isn't. To get around this, you need to make a custom class.
I found this code on Mark Rendle's blog (I've copied it here as I had to use the Google Cache to access the page).
LINK - WARNING: May not work
class Shim : IWin32Window
{
public Shim(System.Windows.Window owner)
{
// Create a WindowInteropHelper for the WPF Window
interopHelper = new WindowInteropHelper(owner);
}
private WindowInteropHelper interopHelper;
#region IWin32Window Members
public IntPtr Handle
{
get
{
// Return the surrogate handle
return interopHelper.Handle;
}
}
#endregion
}
and it's method of use:
namespace System.Windows.Forms
{
public static class WPFInteropExtensions
{
public static DialogResult ShowDialog(
this System.Windows.Forms.Form form,
System.Windows.Window owner)
{
Shim shim = new Shim(owner);
return form.ShowDialog(shim);
}
}
}
I haven't tested this code, but reading around the internet, it appears that you can host Winforms windows inside of a WPF app.
I just found this link on MSDN that has a very detailed description of how to interop a Win32 control/window in a WPF application.
Hope these help you out.
I've been doing this sometimes and didn't encounter any problem.
However i don't really recommend it, you should prefer WPF when you are in a WPF Application.
for exemple if you want application path use this :
System.Reflection.Assembly.GetExecutingAssembly().Location