Apply DI by using unity in WPF application - wpf

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

Related

In which window should I instantiate my object?

My WPF application has multiple windows:
MainWindow
ChildWindow1
Childwindow2
I instantiate the children from MainWindow by...
public partial class MainWindow : Window
{
ChildWindow1 CW1;
ChildWindow2 CW2;
public MainWindow()
{
InitializeComponent();
}
private void btnButton1_Click(object sender, RoutedEventArgs e)
{
CW1 = new ChildWindow1();
CW1.Show();
CW1.Owner = this;
CW2 = new ChildWindow2();
CW2.Show();
CW2.Owner = this;
}
}
Awesome!
But here's where I run into trouble: I have a non-UI class that I need to instantiate.
public class MyClass
{
public void DoSomething()
{
//Do something here
}
}
The object of that class should "live" inside ChildWindow1, because that's where it logically belongs. So, I instantiate it inside ChildWindow1 like so:
public partial class ChildWindow1 : Window
{
public MyClass MyObject = new MyClass();
public ChildWindow1()
{
InitializeComponent();
}
}
But eventually comes a point where ChildWindow2 needs to interact with the object in ChildWindow1:
Call the 'DoSomething()' method of the object in Childwindow1.
Subscribe to an event raised by the object in ChildWindow1
I can do all that from the main window, because it owns the children, but I want the children to be able to interact directly.
Am I violating a design principle by allowing CW1 <--> CW2 interaction?
How else would you get them to interact if not by calling methods or subscribing to events?
Thanks to all here, for providing this awesome learning resource! Much appreciated!
Make this the instance of MyClass both public and static, to access it from almost anywhere because that should be accessed based on a certain instance of window.
If you wanna access a variable from any window, either make it static or call it based on an instance of that window like:
public partial class ChildWindow1 : Window
{
public static MyClass MyObject = new MyClass();
public ChildWindow1()
{
InitializeComponent();
}
}
access like this CW1.MyObject

Open other window from window

I have some academic question here. I read this question WPF MVVM Get Parent from VIEW MODEL and concluded that ViewModel should not opens any windows itself. So I use Messenger now to send message to ViewModel's Window and Window opens other window - NewWindow. It works fine, but what if NewWindow does something and get some Result has to be passed in MainWindow for further actions? More detailed:
NewWindow opened by button click in Window (OpenNewWindowCommand) and made some calculations.
After calculations NewWindow got some Result (does't matter what exactly is it) and rise a corresponding event - GotSomeResult, where event arg is Result.
This Result has to be passed in MainWindow to further processing, so I bind event handler to GotSomeResult event.
Below you can see all required code to illustrate this scenario.
MainWindow code-behind:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
Messenger.Default.Register<NewWindowMessage>(this, OpenNewWindow);
}
private void OpenNewWindow(NewWindowMessage message)
{
var newWindow = new NewWindow();
var newWindowViewModel = (NewWindowViewModel) message.Target;
newWindowViewModel.GotSomeResult += ((MetaWindowViewModel)DataContext).ProcessResult;
newWindow.Owner = this;
newWindow.DataContext = newWindowViewModel;
newWindow.ShowDialog();
}
MainWindow ViewModel:
public void OpenNewWindowCommand()
{
Messenger.Default.Send(new NewWindowMessage(this, new NewWindowViewModel("OpenWindow"), String.Empty));
}
public void ProcessResult(Object someResult)
{
// Any actions with result
}
newWindowViewModel.GotSomeResult += ((MetaWindowViewModel)DataContext).ProcessResult; --- this string seems problem for me. Is it correct to get access to public method of ViewModel right in theView? Does it violent MVVM pattern?
Why don't you hook the handler to GotSomeResult at the VM level, ie :
public void OpenNewWindowCommand()
{
var newWindowViewModel = new NewWindowMessage(this, new NewWindowViewModel("OpenWindow"), String.Empty)
newWindowViewModel.GotSomeResult += this.ProcessResult;
Messenger.Default.Send();
}
It removes the references to your ViewModel in your codebehind (which indeed should be avoided):
private void OpenNewWindow(NewWindowMessage message)
{
var newWindow = new NewWindow();
newWindow.Owner = this;
newWindow.DataContext = message.Target;
newWindow.ShowDialog();
}

Silverlight View exporting DependencyProperty to ViewModel via MEF

I have a need of one DependencyProperty from a View in my ViewModel constructor:
My problem: MEF wouldn't SatisfyImports() 'because it is marked with one or more ExportAttributes' (that is the exception)
This is the code structure for the VIEW:
public class MyView : UserControl
{
[Export(MethodTypes.ChartType)]
public Charts MyChartType
{
get
{
object k = GetValue(ChartTypeProperty);
Charts f = (Charts)Enum.Parse(typeof(Charts), k.ToString(), true);
return f;
}
set
{
SetValue(ChartTypeProperty, value);
}
}
[Import(ViewModelTypes.GenericChartViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
public MyView()
{
InitializeComponent();
if (!ViewModelBase.IsInDesignModeStatic)
{
// Use MEF To load the View Model
CompositionInitializer.SatisfyImports(this);
}
}
}
and the VIEWMODEL:
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.GenericChartViewModel)]
public class GenericChartViewModel
{
[ImportingConstructor]
public GenericChartViewModel([Import(MethodTypes.ChartType)] Charts forChartType)
{
string test = forChartType.ToString();
}
}
Please give me any hints on this or maybe suggest a better solution for passing parameters through mef
In my case, I would need to pass only dependecyproperty's for now...
Thanks
Your work around isn't really good.. can't you remove the export from ChartTypes and pass it manually to whoever wants it? I presume the viewmodel is only one insterested in it..
I managed to put this through !
Instead of the code in the default constructor, I use:
void MyView_Loaded(object sender, RoutedEventArgs e)
{
if (!ViewModelBase.IsInDesignModeStatic)
{
var catalog = new TypeCatalog(typeof(GenericChartViewModel));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
and the dependencyproperty value is correctly propagated to the ViewModel
(must do this after control is loaded, or the property will have its default value)
However, I would be very grateful if someone could:
tell me how generate a catalog from another non-referenced assembly?
Thanks

How to get the popup to go full screen?

I am currently experimenting with the ChildWindowDialog and with prism I have created a controller class. I would like my popup to be displayed on all the screen (a bit like fullscreen mode). I have HtmlPage.Window.Eval() below but I am not sure if this is the correct thing to do. One of the reasons it feels wrong is I have no idea how to test this class in the future. Also, I have coupled the controller to the Browser class which will mean I could not reuse it in a WPF app.
public class GalleryCoverFlowChildWindowController
{
private readonly IEventAggregator _eventAggregator;
private readonly IUnityContainer _container;
public GalleryCoverFlowChildWindowController(IEventAggregator eventAggregator, IUnityContainer container)
{
_eventAggregator = eventAggregator;
_container = container;
_eventAggregator.GetEvent<GalleryCoverViewPopupEvent>().Subscribe(PopupShow, ThreadOption.UIThread, true, Filter);
}
private bool Filter(string obj)
{
return true;
}
private void PopupShow(string obj)
{
var galleryPopup = _container.Resolve<GalleryCoverFlowChildWindow>();
galleryPopup.Width = (double)System.Windows.Browser.HtmlPage.Window.Eval("screen.availWidth");
galleryPopup.Height = (double)System.Windows.Browser.HtmlPage.Window.Eval("screen.availHeight");
galleryPopup.Show();
}
}
JD.
To resolve the issue with coupling, I created a ScreenService and injected it in via Unity. That way I do not have a dependency on DOM. This will make testing the code easier.
Any thoughts?

How to use Prism within an ElementHost

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.

Categories

Resources