How to use Prism within an ElementHost - wpf

I'm new to Prism and I am attempting to host a Prisim control within an ElementHost. I seem to be missing something very basic. I have a single WinForm that contains an ElementHost. The following code is in the form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
var child = bootstrapper.Container.Resolve<Shell>();
elementHost.Child = child;
}
The BootStrapper handles regisration
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
Container.RegisterType<MyView>();
var shell = Container.Resolve<Shell>();
return shell;
}
protected override IModuleCatalog GetModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(MyModule));
return catalog;
}
}
The MyView.xaml is nothing more than a label at this point.
Shell.xaml is a UserControl that contains the following XAML:
<ItemsControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion" />
The module code is minimal:
public class MyModule : IModule
{
private readonly IRegionViewRegistry _regionViewRegistry;
public MyModule(IRegionViewRegistry registry)
{
_regionViewRegistry = registry;
}
public void Initialize()
{
_regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(MyView));
}
}
I've been tracing deep into the Prism code trying to figure out why the View is never set into the region. Am I missing something basic?

The reason is this code in Prism:
private static bool RegionManager::IsInDesignMode(DependencyObject element)
{
// Due to a known issue in Cider, GetIsInDesignMode attached property value is not enough to know if it's in design mode.
return DesignerProperties.GetIsInDesignMode(element) || Application.Current == null
|| Application.Current.GetType() == typeof(Application);
}
The reason is that for the non-WPF application the Application.Current is NULL!
The solution:
Create an empty class that will inherit from System.Windows.Application. (Name doesn’t matter):
At the point of entry to a plug-in execute the following code:
public class MyApp : System.Windows.Application
{
}
if (System.Windows.Application.Current == null)
{
// create the Application object
new MyApp();
}
This is it – now you have an Application.Current that is not null and it’s not equal to typeof(Application).

#Mark Lindell Above worked for me. The only things I had to change are below.
My bootstrapper
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
if (System.Windows.Application.Current == null)
{
// create the Application object
new HelloWorld.Myapp();
}
//App.Current.MainWindow = (Window)this.Shell;
//App.Current.MainWindow.Show();
//MainWindow = (Window)this.Shell;
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(HelloWorldModule.HelloWorldModule));
}
and my form class
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Create the ElementHost control for hosting the WPF UserControl
ElementHost host = new ElementHost();
host.Dock = DockStyle.Fill;
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run(true);
//var uc = bootstrapper.Container.Resolve<Shell>(); This line threw error
//Create the WPF UserControl.
HelloWorld.Shell uc = new HelloWorld.Shell();
//Assign the WPF UserControl to the ElementHost control's Child property.
host.Child = uc;
//Add the ElementHost control to the form's collection of child controls.
this.Controls.Add(host);
}
}
}
And just to be clear, I added below class in the WPF PRISM application containing Shell.
public class MyApp : System.Windows.Application
{
}
Edit: Note that the Load method handler (of form) has to be created by
rightclicking form, In the properties window, go to events and double
clicl Load. Copying and pasting load event handler doesn't work.

Related

Open new window on click in WPF, Ninject and Caliburn.Micro

I'm trying to set up a WPF app to call the new window on a menu click with the data provider interface injected into the new viewmodel.
Followed many tutorials and created the Bootstrapper for Caliburn, a service locator and module for ninject. So far the main view doesn't need the IDataProvider but I'd like to open a new window on click event.
The Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainScreenViewModel>();
}
}
The Service Locator and Module:
public class ServiceLocator
{
private readonly IKernel _kernel;
public ServiceLocator()
{
_kernel = new StandardKernel(new ServiceModule());
}
public MainScreenViewModel MainScreenViewModel => _kernel.Get<MainScreenViewModel>();
public NewLayoutViewModel NewLayoutViewModel => _kernel.Get<NewLayoutViewModel>();
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ISqlite>().To<Sqlite>();
Bind<IDataProvider>().To<DataProvider>();
}
}
And this is where I got stuck:
public class MainScreenViewModel : Conductor<object>
{
private IWindowManager _windowManager;
public MainScreenViewModel()
{
_windowManager = new WindowManager();
}
public void NewLayout()
{
_windowManager.ShowWindow(new NewLayoutViewModel());
}
}
since the NewLayoutViewModel requires the IDataProvider.
Not sure, what am I missing, but in my understanding Ninject should take care of this di for NewLayoutViewModel.
Found a good solution from Tim Corey on YouTube.
Basically the answer is, if you not insist Ninjet, use Caliburn.Micro's build-in DI solution "SimpleContainer".

Using RegisterViewWithRegion with derived usercontrol

I am writing an application with WPF using Prism and the Managed Extensibility Framework. The purpose is to allow developers to create there own 3rd party modules. To remove as much work as possible I have created some base classes in a Common module which already does most of the required work. (eg. drag n drop for views, adds MEF InheritedExportAttribute). One of these controls is called ModuleControl (derives from UserControl) shown below.
I have everything working great if my module has a view class directly derived from UserControl with one child ModuleControl in the XAML (see the examples below). This seems like a lot extra work for users. I'd like to make my view class derive from 'ModuleControl' just using code. If I do this as in the final example below, then RegisterViewWithRegion throws the following exception:
Activation error occured while trying to get instance of type ModuleView, key \"\"
System.Exception {Microsoft.Practices.ServiceLocation.ActivationException}
I realise this happens when the type can't be registered. So my questions are how can I achieve this? What am I doing wrong? Does RegisterViewWithRegion explicitly expect UserControl and nothing derived from it?
Here is an example of my first control located in Common.dll.
namespace Common.Controls
{
[InheritedExport]
public partial class ModuleControl: UserControl
{
private Point _anchor;
public ModuleControl()
{
InitializeComponent();
}
public ObservableCollection<IComponent> ModuleComponents
{
get
{
return (ObservableCollection<IComponent>)GetValue(ModuleComponentsProperty);
}
set
{
SetValue(ModuleComponentsProperty, value);
}
}
private void InitialiseCollection()
{
try
{
ModuleComponents = new ObservableCollection<IComponent>();
var components = ServiceLocator.Current.GetAllInstances(typeof(IComponent));
Assembly controlAssembly = UIHelper.FindAncestor<UserControl>(VisualTreeHelper.GetParent(this)).GetType().Assembly;
foreach (IComponent c in components)
{
if (c.ExportType.Assembly.Equals(controlAssembly))
{
ModuleComponents.Add(new Component(c.ViewType, c.Description));
}
}
}
catch (Exception e)
{
}
}
public string ModuleName
{
get
{
return (string)GetValue(ModuleNameProperty);
}
set
{
SetValue(ModuleNameProperty, value);
}
}
public static readonly DependencyProperty ModuleComponentsProperty =
DependencyProperty.Register("ModuleComponents", typeof(ObservableCollection<IComponent>), typeof(ModuleControl), new UIPropertyMetadata(null));
public static readonly DependencyProperty ModuleNameProperty =
DependencyProperty.Register("ModuleName", typeof(String), typeof(ModuleControl), new UIPropertyMetadata("No Module Name Defined"));
private void DragList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Store the mouse position
_anchor = e.GetPosition(null);
}
private void DragList_PreviewMouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = _anchor - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
// Get the dragged ListViewItem
ListView listView = sender as ListView;
ListViewItem listViewItem = UIHelper.FindAncestor<ListViewItem>((DependencyObject)e.OriginalSource);
// Initialize the drag & drop operation
if (listViewItem != null)
{
DataObject dragData = new DataObject("moduleFormat", listViewItem.Content);
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move);
}
}
}
private void moduleControl_LayoutUpdated(object sender, EventArgs e)
{
if (ModuleComponents == null)
InitialiseCollection();
}
}
Following from the example A Prism 4 Application Checklist I built the following module modifying this to use MEF instead of Unity where appropriate.:
This module is located in RandomNumbers.dll
namespace RandomNumbers
{
[ModuleExport(typeof(RandomNumberModule))]
public class RandomNumberModule: IModule
{
public void Initialize()
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("MyRegion", typeof(ModuleView));
}
}
}
The ModuleView XAML that works looks like this:
<UserControl x:Name="userControl" x:Class="RandomNumbers.Views.ModuleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:common="clr-namespace:Common.Base;assembly=Common"
xmlns:commonctrls="clr-namespace:Common.Controls;assembly=Common"
mc:Ignorable="d">
<commonctrls:ModuleControl x:Name="moduleControl" ModuleName="Random Number Module" />
</UserControl>
The codebehind for this is:
namespace RandomNumbers.Views
{
[Export]
public partial class ModuleView : UserControl
{
private Point startPoint;
public ModuleView()
{
InitializeComponent();
}
}
}
As already mentioned, all the above code works perfectly. However if I replace the XAML and code behind with this, then I get the exception as described. I have tried leaving out the ExportAttribute but nothing changes.
namespace RandomNumbers.Views
{
[Export(typeof(UserControl))]
public class ModuleView : ModuleControl
{
public ModuleView() : base() { }
}
}

WPF, Prism, Unitybootstrapper, and Enterprise Library Logging setup throwing LogWriter exception

I'm just trying to get up off the ground and get used to working with Prism, in my bootstrapper I have:
public class Bootstrapper : UnityBootstrapper
{
private readonly EnterpriseLibraryLoggerAdapter _logger = new EnterpriseLibraryLoggerAdapter();
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<Shell>();
}
protected override ILoggerFacade CreateLogger()
{
return _logger;
}
for my App OnStartup:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
And the logging adapter
public class EnterpriseLibraryLoggerAdapter : ILoggerFacade
{
#region ILoggerFacade Members
public void Log(string message, Category category, Priority priority)
{
Logger.Write(message, category.ToString(), (int)priority); // <--Blows up here
}
#endregion
}
When the bootstrapper Runs, it hits the Logger.Write and throws an exception:
The type LogWriter does not have an accessible constructor.
I'm following from the StockTraderRI sample app. Am I missing a registration somewhere?
moved the configuration to my bootstrapper constructor and things seem to be working
var builder = new ConfigurationSourceBuilder();
builder.ConfigureLogging()
.WithOptions
.DoNotRevertImpersonation()
.LogToCategoryNamed("Debug")
.SendTo.FlatFile("Basic Log File")
.FormatWith(new FormatterBuilder()
.TextFormatterNamed("Text Formatter")
.UsingTemplate(
"Timestamp: {timestamp}{newline}Message: {message}{newline}Category: {category}{newline}"))
.ToFile("core.log")
.SendTo.RollingFile("Rolling Log files")
.RollAfterSize(1024)
.ToFile("RollingTest.log")
.LogToCategoryNamed("General")
.WithOptions.SetAsDefaultCategory()
.SendTo.SharedListenerNamed("Basic Log File");
var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);
EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);

Apply DI by using unity in WPF application

I got confused after reading the documentation of Unity framework.
link
I'am writing a WPF application which will search for some devices.
Below my code from my main Window.
As you can see, now i'am still declaring UnitOfWork and DeviceService inside my main Window. I want to replace this code by applying Dependency Injection. At the same time i would also inject my viewmodel inside my main window.
public Window1()
{
InitializeComponent();
UnitOfWork myUnitOfWork = new UnitOfWork();
DeviceService dService = new DeviceService(myUnitOfWork);
_vm = new DeviceViewModel(dService);
this.DataContext = _vm;
_vm.SearchAll();
}
I gave a try in below code but i failed in setting the container. The real question is how should i start? Do i need to completely change the stucture of my program?
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
UnitOfWork myUnitOfWork = new UnitOfWork();
container.RegisterInstance<UnitOfWork>(myUnitOfWork);
Window1 w1 = new Window1();
w1.Show();
}
}
I went trough the suggested tutorial. It is still not clear for me on how i should configure the property injection.
My viewmodel should be injected in the Window 1 class, so i assume that i have to create a dependency property.
private DeviceViewModel viewModel;
[Dependency]
public DeviceViewModel ViewModel
{
get { return viewModel; }
set { this.DataContext = value; }
}
How can i inject my viewmodel into window 1, knowing that DeviceViewModel has dependency on DeviceService and again on UnitOfWork ?
//CONSTRUCTOR
public DeviceViewModel(DeviceService service)
{
Service = service;
SearchCommand = new SearchCommand(this);
}
private UnitOfWork myUnit;
public DeviceService(UnitOfWork unit)
{
myUnit = unit;
}
You need to tell container how to build all the objects needed by other objects, then the container will instantiate whatever is needed when needed.
your property injection is only missing one line:
private DeviceViewModel viewModel;
[Dependency]
public DeviceViewModel ViewModel
{
get { return viewModel; }
set { viewModel = value; this.DataContext = viewModel; }
}
Then on you OnStartup()
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<UnitOfWork>();
container.RegisterType<DeviceService>();
container.RegisterType<DeviceViewModel>();
Window1 w1 = container.Resolve<Window1>();
w1.Show();
}
There are different parameters you can use in RegisterType(), so you can control the lifetime and the creation of your objects.
You need to go through this example: http://visualstudiogallery.msdn.microsoft.com/3ab5f02f-0c54-453c-b437-8e8d57eb9942
You are on the right tracks, just you should resolve the window, not new it up.
//instead of Window1 w1 = new Window1();
Window1 w1 = container.Resolve<Window1>();
w1.DataContext = container.Resolve<DeviceViewModel>();
Window1 will no longer need to set its own DataContext

How to close dialog window from viewmodel (Caliburn+WPF)?

I haveViewModel1 and View1 associated with it. I start dialog window from ViewModel2 (some another viewmodel) using IWindowManager object. The code from ViewModel2 class:
windowManager.ShowDialog(new ViewModel());
So, I have Dialog Window with View1 user control.
My answer is next - I can close that dialog window using red close button, but how to close it using my specific button (contained in View1 user control), something like "Cancel" button with close command (Command={Binding CancelCommand}), CancelCommand of course is contained in ViewModel1 class.
It's even easier if your view model extends Caliburn.Micro.Screen:
TryClose();
You can get the current view (in your case the dialog window) with implementing the IViewAware interface on your ViewModel. Then you can call Close on the the view (the Window created as the dialog) when your command is executed.
The easiest why is to derive from ViewAware:
public class DialogViewModel : ViewAware
{
public void ExecuteCancelCommand()
{
(GetView() as Window).Close();
}
}
If you are not allowed to derive you can implement it yourself:
public class DialogViewModel : IViewAware
{
public void ExecuteCancelCommand()
{
dialogWindow.Close();
}
private Window dialogWindow;
public void AttachView(object view, object context = null)
{
dialogWindow = view as Window;
if (ViewAttached != null)
ViewAttached(this,
new ViewAttachedEventArgs(){Context = context, View = view});
}
public object GetView(object context = null)
{
return dialogWindow;
}
public event EventHandler<ViewAttachedEventArgs> ViewAttached;
}
Note: I've used Caliburn.Micro 1.3.1 for my sample.
A cleaner way (Subject of personal taste) that I use alot is to use the IResult pattern, this way you abstract the Window implemenation
Viewmodel
public IEnumerable<IResult> CloseMe()
{
yield return new CloseResult();
}
Result code
public class CloseResult : Result
{
public override void Execute(ActionExecutionContext context)
{
var window = Window.GetWindow(context.View);
window.Close();
base.Execute(context);
}
}
public abstract class Result : IResult
{
public virtual void Execute(ActionExecutionContext context)
{
OnCompleted(this, new ResultCompletionEventArgs());
}
protected virtual void OnCompleted(object sender, ResultCompletionEventArgs e)
{
if (Completed != null)
Completed(sender, e);
}
public event EventHandler<ResultCompletionEventArgs> Completed;
}
edit (Only needed for IoC): If you wanna take it a step further you do a base class for all screens
public abstract class ShellPresentationModel : Screen
{
public ShellPresentationModel(IResultFactory resultFactory)
{
Result = resultFactory;
}
public IResultFactory Result { get; private set; }
}
This way you can inject dependencies with a IoC much easier, then your VIewmodel close method will look like this
public IEnumerable<IResult> CloseMe()
{
yield return Result.Close();
}
An example on a IResult that uses dependency can be
public class ShowDialogResult<TModel> : Result
{
private readonly IWindowManager windowManager;
private readonly TModel model;
private Action<TModel> configure;
public ShowDialogResult(IWindowManager windowManager, TModel model)
{
this.windowManager = windowManager;
this.model = model;
}
public IResult Configure(Action<TModel> configure)
{
this.configure = configure;
return this;
}
public override void Execute(ActionExecutionContext context)
{
if(configure != null)
configure(model);
windowManager.ShowDialog(model);
base.Execute(context);
}
}
edit Just noticed that i forgot to add an example of the above IoC exmaple, here goes
With a child IoC container pattern it would look like this
public IEnumerable<IResult> ShowDialog()
{
yield return Result.ShowDialog<MyViewModel>();
}
Without a child container pattern you would need to inject parent dependeync into the child manually
yield return Result.ShowDialog<MyViewModel>().Configure(m => m.SomeData = this.SomeData);

Resources