WPF, MVVM IoC: Alternative to Service Locator Pattern. Need dependency in View code behind - wpf

Following several guides I have a application layout like below using WPF .NET 4.7.1 and MVVM-Light. I'm totally new to WPF btw.
App.xaml:
<Application x:Class="My.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodel="clr-namespace:My.ViewModel"
StartupUri="View\MainView.xaml">
<Application.Resources>
<ResourceDictionary>
<viewmodel:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
</Application.Resources>
That registers the "ViewModelLocator" class as a resources and sets the WPF startup to "View/MainView.xaml".
MainView.xaml:
<Window x:Class="My.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
Where the ViewModelLocator is used like a Service Locator Pattern. Here setting the DataContext to my "MainViewModel" (not shown). As much as I do not like this, I can live with it in the WPF XAML context. However now it turns out that I need a dependency in the code-behind of the view (not the ViewModel).
MainView.cs:
public partial class MainView : INotifyPropertyChanged
{
public MainView()
{
// Need to access dependency here.
}
}
Now I could just call the ViewModelLocator directly in that constructor and have that resolve from my IoC container - but then I've completely given in and accepting that pattern.
I would prefer to have the dependency injected in the ctor of course, and if that is possible, I would also leave the ViewModelLocator entirely and inject the ViewModel here.
So question is, are there some standard way of instructing WPF application to use my container? And if yes, is it adviceable to go down that path and not use the ViewModelLocator thing?

You absolutely do not have to use the ViewModelLocator (Side note, the service locator pattern has had it's fair share of criticism lately as an anti-pattern, but I'll let you form your own opinion). MVVM Light and other Libraries basically give you access to a tool kit. You don't need to use all of the tools, and you should only use what is necessary for your specific domain.
Outside of the ViewModelLocator, there are two patterns known as ViewModel First and View First both have their pro's and cons. However both provide a means to decouple your code, which means it's not difficult to switch later.
As for constructing an application using MVVM Light without the service locator, my implementation of the View First method looks something like this.
I've heard the opinion that ViewModel First is preferred, however I find View First to be more simplistic for Test Driven Development (TDD)
App.xaml.cs (Application Code Behind)
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bootStrapper = new BootStrapper();
//Container Builder
var container = bootStrapper.BootStrap();
var mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
}
}
BootStrapper.cs (I'm using AutoFac in this case, but you can easily substitute.)
public class BootStrapper
{
public IContainer BootStrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<MainWindow>().AsSelf();
builder.RegisterType<MainWindowViewModel>().AsSelf();
return builder.Build();
}
}
MainWindowViewModel.cs
//I rolled my own ViewModelBase, but you can use MVVM Light's ViewModelBase
public class MainWindowViewModel : ViewModelBase
{
public string DisplayProgram
{
get { return _displayProgram; }
//MVVM Light's ViewModelBase uses RaisePropertyChanged();
set { _displayProgram = value; OnPropertyChanged(); }
}
public void Initialize()
{
//Called from view code behind.
}
}
MainWindow.xaml.cs (MainWindow Code Behind)
//When MainWindow.Show()..
public partial class MainWindow : Window
{
private readonly MainWindowViewModel _viewModel;
//Container resolves dependencies
public MainWindow(MainWindowViewModel viewModel)
{
//Let base Window class do its thing.
InitializeComponent();
//Handle loaded event
Loaded += MainWindowLoaded;
//Hold on to the MainWindowViewModel, and set it as the windows DataContext
_viewModel = viewModel;
DataContext = _viewModel;
}
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
_viewModel.Initialize();
}
}

Related

Why is Caliburn Micro binding some views but not others despite same naming conventions?

I have 3 ViewModels:
App.ViewModels.LoginViewModel
App.ViewModels.NavigationViewModel
App.ViewModels.AbcViewModel
and 3 Views:
App.Views.LoginView
App.Views.NavigationView
App.Views.AbcView
In my AppBootstrapper, LoginView is loaded like so:
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
var windowManager = IoC.Get<IWindowManager>();
var loginModel = IoC.Get<ILogin>("Login");
windowManager.ShowWindow(loginModel, "LoginView");
}
However, this returns that the view cannot be found for that ViewModel. Unless I change the namespace of the LoginView to App.Views.Login.LoginView and leave the VM namespace as it is. It then works fine.
After a succesfful login, I use the same process to load my NavigationViewModel. (After having changed the namespace to the App.Views.Navigation.NavigationViewModel so that it actually works)
Currently, this leaves me with the following namespaces for the views:
App.Views.Login.LoginView
App.Views.Navigation.NavigationView
App.Views.AbcView
NavigationViewModel is a conductor, it has a list of ViewModels and a TabControl on the view to display them.
Unfortunately I then have to manually bind my AbcViewModel to the view, otherwise nothing gets displayed. For example:
AbcView abcv= new AbcView ();
AbcViewModel abcvm= IoC.Get<AbcViewModel>("Abc");
ViewModelBinder.Bind(abcvm, abc, null);
I want everything to be done using the Caliburn ViewModel first approach, so that adding new ViewModels and Views I don't need to worry about binding the view manually. I've adhered to the structure and yet it isn't working. Where am I going wrong?
Basically, is there a way that caliburn can create and then bind my view when I create my ViewModel?
Do I need to somehow call the ViewLocator for each of my models? If so, how is this any different to the manual bind that I'm doing at the moment?
Does anyone know of a full example (Whole project) of a view model first caliburn project that I can sneak a look at?
Any help appreciated, thanks in advance.
Matt
You don't need to bind any Views/ViewModels yourself, Caliburn.Micro takes care of it. You have to only tell Caliburn.Micro where your start class is by overriding OnStartup in your Bootstrapper class (see tutorial).
You also have to provide ResourceDictionary with your Bootstrapper class in App.xaml and remove Startup:
<Application.Resources>
<!--Declare your bootstrapper class-->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:YourBootstrapperClass x:Key="bootstrapper" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Your Bootstrapper class must derive from BootstrapperBase and call Initialize() in constructor. That should be enough for Caliburn.Micro to bind views/viewmodels.
Additionally, if you want to use Dependency Injection in your project you can setup a SimpleContainer and register types you want to inject. You can do this in Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
// Container for your registered types.
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
// Tells Caliburn.Micro where the starting point of your application is.
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
// Register types you want to inject.
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
base.Configure();
}
protected override object GetInstance(Type service, string key)
{
// This is called every time a dependency is requested.
// Caliburn.Micro checks if container contains dependency and if so, returns it.
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
// Get all registered classes...
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
After that you can just inject dependecies via constructor like this:
public class ShellViewModel
{
public ShellViewModel(IEventAggregator eventAggregator)
{
// You can use eventAggregator here. It is already injected.
}
}
Caliburn.Micro uses naming convention so that it can discover Views/ViewModels automatically. You should have folders named: Views and ViewModels and your classes should be named YourClassView and YourClassViewModel. This way Caliburn.Micro can find them. So if you setup OnStartup like this:
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
Then your viewmodel must sit in ViewModels/ShellViewModel and your view for this ViewModel must sit in Views/ShellView.

How to make a second project in the same solution for Windows Phone/Wpf/Windows type of solution

I have a Windows Phone project and my business demands to create another one with some slight changes in the front-end (XAML). How to create another project that is identical to the first one but only the XAML files are different? I don't use MVVM. What I tried is creating a new project and copy the XAML files from the first one, and then LINK all other CS files, but it became a mess with all these namespaces and stuff.. I have resource dicionaries and lots of dependencies in the code. Any ideas how to make such a project that shares the same code-behind files with some differences in Visual Studio?
XAML with a code behind are partial classes. You cannot have two partial classes referring to the same class in two different assemblies. Therefore I think you can't use common code behind for XAML from different projects.
The best approach is using a common view model for different views, but you don't use MVVM pattern.
Then you can use something like a proxy. The proxy is a common class in a separate assembly. It contains all logic and data.
You get or set any state from your code behind only by the proxy.
UPD: Example:
It's a common contract for each view (it's an interface from a common assembly):
public interface IMyWindow
{
Label HelloLabel { get; }
}
It's the first WPF project:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="First Application" Height="350" Width="525">
<StackPanel>
<Label x:Name="_helloLabel" Content ="Hello, I'm First Application!"></Label>
<Button Click="ButtonBase_OnClick" Height="100">Press me</Button>
</StackPanel>
</Window>
public partial class MainWindow : Window, IMyWindow
{
private readonly MyWindowProxy _proxy;
public MainWindow()
{
InitializeComponent();
_proxy = new MyWindowProxy(this);
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_proxy.OnButtonClick();
}
public Label HelloLabel
{
get { return _helloLabel; }
}
}
It's the second WPF project:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Second Application" Height="350" Width="525">
<StackPanel>
<Label x:Name="_helloLabel" Content ="Hello, I'm Second Application!"></Label>
<Button Click="ButtonBase_OnClick" Width ="50" Height="50">OK</Button>
</StackPanel>
</Window>
public partial class MainWindow : Window, IMyWindow
{
private readonly MyWindowProxy _proxy;
public MainWindow()
{
InitializeComponent();
_proxy = new MyWindowProxy(this);
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_proxy.OnButtonClick();
}
public Label HelloLabel
{
get { return _helloLabel; }
}
}
It's a proxy for each view (it's a class from a common assembly):
public class MyWindowProxy
{
private readonly IMyWindow _window;
public MyWindowProxy(IMyWindow window)
{
_window = window;
}
public void OnButtonClick()
{
_window.HelloLabel.Content = "Hello from common proxy!";
}
}
Once again, this is NOT the best way to build an application architecture. I highly recommend using MVVM pattern then the question of separating of business logic disappear by itself.

Dependecy injection(Windsor) on WPF UserControl

Using DI into MainView is not problem:
I added my windows into my container and on start up I show my windows that has been pulled out from my container. But If I have a usercontrol added into my main view as xaml tag, wpf view engine it will create automatically new instance for it without pulling out the UserControl I added into my container as well.. How can I force WPF view engine to search component required by view/xamal into my container instead of creating new one?
There is no way to do it without modifying your XAML. You can think about some workarounds, for example create a control inherited from ContentControl which will inject dependencies into its Content but I would not recommend this approach, only if you have no choice.
What I would recommend is to use the best WPF pattern - MVVM. The idea is to have a hierarchy of ViewModels, all of them will be created using IoC container with proper constructor injection. Also you will have hierarchy of views, each view will depend only on corresponding viewModel which will be passed into view's DataContext. This approach will allow you to use DI in WPF application nicely.
I think I understood what you suggested me
<Window x:Class="DDDSample02.Wpf.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:presentation="clr-namespace:DDDSample02.Wpf.Views"
Title="MainWindow" Height="384" Width="821">
<Grid>
<presentation:ProductsView DataContext="{Binding Path=ProductsPresenter}" />
</Grid>
</Window>
where MainWindow is pulled out from container at startup
protected override void OnStartup(StartupEventArgs e)
{
GuyWire.Wire();
((Window)GuyWire.GetRoot()).Show();//MainWindow
}
and Mainwindow looks like
public partial class MainWindow : Window
{
public MainWindow(DDDSample02.ViewModel.MainWindowPresenter presenter)
{
InitializeComponent();
this.DataContext = presenter;
}
}
public class MainWindowPresenter
{
public MainWindowPresenter(ProductsPresenter productPresenter)
{
this.ProductsPresenter = productPresenter;
}
public ProductsPresenter ProductsPresenter { get; private set; }
}

Is there a way to start a WPF application without StartUpUri that doesn't break something else?

I've been trying for hours to get to the point where I can start a WPF application and have full control. I want to be able to create a ViewModel, create a View (Window), set the data context of the View to be the ViewModel, then show the View.
I've tried lots of methods, the most promising being to change the App.xaml to be a page and then adding my own Main method. Unfortunately this doesn't work properly because VS2010 then does not show the styles from the App.xaml in the designer, though they do work when running the app.
Is there a way to do what I want? If not, how do people normally start MVVM apps in WPF, creating a ViewModel outside of the View itself?
I would use the Startup event. You can add this to the App.xaml and remove the StartupUri line. When you add it, Visual Studio can create the event for you within the App.xaml.cs file. You can initialise your ViewModel and View within.
Here is one simple way...
<Application
x:Class="Demo.Ux.WpfApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
Here is the basic App.xaml.cs
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
try
{
var mainView = new MainView();
mainView.Show();
mainView.DataContext = new MainViewModel();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
Application.MainWindow can be used as well. The first displayed Window will be assigned to MainWindow auto-magically. Of course, you can skip creating your mainView and write directly to MainWindow which would thin out the syntax as well.
MainWindow = new MainView();
MainWindow.Show();
MainWindow.DataContext = new MainViewModel();
One final note, I'm doing the Show before the data bind. You need to do this to avoid a situation where the MainViewModel throw an exception during creation. If the MainView hasn't been shown, the app will close without letting you see the error.
in our application, we have choosen the way which you already proposed: writing a new Main method. You also have to make some changes in the project application settings then (no startup object). The app xaml has to look something like this:
<Application x:Class="EVOCURA.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup"
Exit="Application_Exit">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--Custom Controls-->
<ResourceDictionary Source="<your resources here>"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The code behind will look something like this:
public sealed partial class App : Application
{
static App()
{ }
public App()
{ }
private void Application_Startup(object sender, StartupEventArgs e)
{
// create the main window and assign your datacontext
MainAppWindow main = new MainAppWindow();
main.DataContext = <your datacontext here>
main.Show();
}
[STAThreadAttribute]
public static int Main(string[] args)
{
App app = new App();
app.InitializeComponent();
app.Run();
return 0;
}
}
Have a look at the Startup Event and notice, that no default StartupUri is specified im App.xaml
You could also pass the DataContext in a new constructor of your MainWindow, or create the DataContext directly in xaml.
The simplest way to assign an instance of the ViewModel to the DataContext of the view is in the code behind of the Window.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new myViewModel();
}
}
For the first part of your question, you can have the control of your application in the StartUp event
<Application x:Class="myApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
Code Behind :
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// Place your code here
}
}

Implementing MVVM in WPF without using System.Windows.Input.ICommand

I'm trying to implement a WPF application using MVVM (Model-View-ViewModel) pattern and I'd like to have the View part in a separate assembly (an EXE) from the Model and ViewModel parts (a DLL).
The twist here is to keep the Model/ViewModel assembly clear of any WPF dependency. The reason for this is I'd like to reuse it from executables with different (non-WPF) UI techs, for example WinForms or GTK# under Mono.
By default, this can't be done, because ViewModel exposes one or more ICommands. But the ICommand type is defined in the System.Windows.Input namespace, which belongs to the WPF!
So, is there a way to satisfy the WPF binding mechanism without using ICommand?
Thanks!
You should be able to define a single WPF custom routed command in your wpf layer and a single command handler class. All your WPF classes can bind to this one command with appropriate parameters.
The handler class can then translate the command to your own custom command interface that you define yourself in your ViewModel layer and is independent of WPF.
The simplest example would be a wrapper to a void delegate with an Execute method.
All you different GUI layers simply need to translate from their native command types to your custom command types in one location.
WinForms doesn't have the rich data binding and commands infrastructure needed to use a MVVM style view model.
Just like you can't reuse a web application MVC controllers in a client application (at least not without creating mountains of wrappers and adapters that in the end just make it harder to write and debug code without providing any value to the customer) you can't reuse a WPF MVVM in a WinForms application.
I haven't used GTK# on a real project so I have no idea what it can or can't do but I suspect MVVM isn't the optimal approach for GTK# anyway.
Try to move as much of the behavior of the application into the model, have a view model that only exposes data from the model and calls into the model based on commands with no logic in the view model.
Then for WinForms just remove the view model and call the model from the UI directly, or create another intermediate layer that is based on WinForms more limited data binding support.
Repeat for GTK# or write MVC controllers and views to give the model a web front-end.
Don't try to force one technology into a usage pattern that is optimized for another, don't write your own commands infrastructure from scratch (I've done it before, not my most productive choice), use the best tools for each technology.
Sorry Dave but I didn't like your solution very much. Firstly you have to code the plumbing for each command manually in code, then you have to configure the CommandRouter to know about each view/viewmodel association in the application.
I took a different approach.
I have an Mvvm utility assembly (which has no WPF dependencies) and which I use in my viewmodel. In that assembly I declare a custom ICommand interface, and a DelegateCommand class that implements that interface.
namespace CommonUtil.Mvvm
{
using System;
public interface ICommand
{
void Execute(object parameter);
bool CanExecute(object parameter);
event EventHandler CanExecuteChanged;
}
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute(parameter);
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
}
}
I also have a Wpf library assembly (which does reference the System WPF libraries), which I reference from my WPF UI project. In that assembly I declare a CommandWrapper class which has the standard System.Windows.Input.ICommand interface. CommandWrapper is constructed using an instance of my custom ICommand and simply delegates Execute, CanExecute and CanExecuteChanged directly to my custom ICommand type.
namespace WpfUtil
{
using System;
using System.Windows.Input;
public class CommandWrapper : ICommand
{
// Public.
public CommandWrapper(CommonUtil.Mvvm.ICommand source)
{
_source = source;
_source.CanExecuteChanged += OnSource_CanExecuteChanged;
CommandManager.RequerySuggested += OnCommandManager_RequerySuggested;
}
public void Execute(object parameter)
{
_source.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return _source.CanExecute(parameter);
}
public event System.EventHandler CanExecuteChanged = delegate { };
// Implementation.
private void OnSource_CanExecuteChanged(object sender, EventArgs args)
{
CanExecuteChanged(sender, args);
}
private void OnCommandManager_RequerySuggested(object sender, EventArgs args)
{
CanExecuteChanged(sender, args);
}
private readonly CommonUtil.Mvvm.ICommand _source;
}
}
In my Wpf assembly I also create a ValueConverter that when passed an instance of my custom ICommand spits out an instance of the Windows.Input.ICommand compatible CommandWrapper.
namespace WpfUtil
{
using System;
using System.Globalization;
using System.Windows.Data;
public class CommandConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new CommandWrapper((CommonUtil.Mvvm.ICommand)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
}
Now my viewmodels can expose commands as instances of my custom command type without having to have any dependency on WPF, and my UI can bind Windows.Input.ICommand commands to those viewmodels using my ValueConverter like so. (XAML namespace spam ommited).
<Window x:Class="Project1.MainWindow">
<Window.Resources>
<wpf:CommandConverter x:Key="_commandConv"/>
</Window.Resources>
<Grid>
<Button Content="Button1" Command="{Binding CustomCommandOnViewModel,
Converter={StaticResource _commandConv}}"/>
</Grid>
</Window>
Now if I'm really lazy (which I am) and can't be bothered to have to manually apply the CommandConverter every time then in my Wpf assembly I can create my own Binding subclass like this:
namespace WpfUtil
{
using System.Windows.Data;
public class CommandBindingExtension : Binding
{
public CommandBindingExtension(string path) : base(path)
{
Converter = new CommandConverter();
}
}
}
So now I can bind to my custom command type even more simply like so:
<Window x:Class="Project1.MainWindow"
xmlns:wpf="clr-namespace:WpfUtil;assembly=WpfUtil">
<Window.Resources>
<wpf:CommandConverter x:Key="_commandConv"/>
</Window.Resources>
<Grid>
<Button Content="Button1" Command="{wpf:CommandBinding CustomCommandOnViewModel}"/>
</Grid>
</Window>
I needed an example of this so I wrote one using various techniques.
I had a few design goals in mind
1 - keep it simple
2 - absolutely no code-behind in the view (Window class)
3 - demonstrate a dependency of only the System reference in the ViewModel class library.
4 - keep the business logic in the ViewModel and route directly to the appropriate methods without writing a bunch of "stub" methods.
Here's the code...
App.xaml (no StartupUri is the only thing worth noting)
<Application
x:Class="WpfApplicationCleanSeparation.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
App.xaml.cs (load up the main view)
using System.Windows;
using WpfApplicationCleanSeparation.ViewModels;
namespace WpfApplicationCleanSeparation
{
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
var view = new MainView();
var viewModel = new MainViewModel();
view.InitializeComponent();
view.DataContext = viewModel;
CommandRouter.WireMainView(view, viewModel);
view.Show();
}
}
}
CommandRouter.cs (the magic)
using System.Windows.Input;
using WpfApplicationCleanSeparation.ViewModels;
namespace WpfApplicationCleanSeparation
{
public static class CommandRouter
{
static CommandRouter()
{
IncrementCounter = new RoutedCommand();
DecrementCounter = new RoutedCommand();
}
public static RoutedCommand IncrementCounter { get; private set; }
public static RoutedCommand DecrementCounter { get; private set; }
public static void WireMainView(MainView view, MainViewModel viewModel)
{
if (view == null || viewModel == null) return;
view.CommandBindings.Add(
new CommandBinding(
IncrementCounter,
(λ1, λ2) => viewModel.IncrementCounter(),
(λ1, λ2) =>
{
λ2.CanExecute = true;
λ2.Handled = true;
}));
view.CommandBindings.Add(
new CommandBinding(
DecrementCounter,
(λ1, λ2) => viewModel.DecrementCounter(),
(λ1, λ2) =>
{
λ2.CanExecute = true;
λ2.Handled = true;
}));
}
}
}
MainView.xaml (there is NO code-behind, literally deleted!)
<Window
x:Class="WpfApplicationCleanSeparation.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplicationCleanSeparation="clr-namespace:WpfApplicationCleanSeparation"
Title="MainWindow"
Height="100"
Width="100">
<StackPanel>
<TextBlock Text="{Binding Counter}"></TextBlock>
<Button Content="Decrement" Command="WpfApplicationCleanSeparation:CommandRouter.DecrementCounter"></Button>
<Button Content="Increment" Command="WpfApplicationCleanSeparation:CommandRouter.IncrementCounter"></Button>
</StackPanel>
</Window>
MainViewModel.cs (includes the actual Model as well since this example is so simplified, please excuse the derailing of the MVVM pattern.
using System.ComponentModel;
namespace WpfApplicationCleanSeparation.ViewModels
{
public class CounterModel
{
public int Data { get; private set; }
public void IncrementCounter()
{
Data++;
}
public void DecrementCounter()
{
Data--;
}
}
public class MainViewModel : INotifyPropertyChanged
{
private CounterModel Model { get; set; }
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public MainViewModel()
{
Model = new CounterModel();
}
public int Counter
{
get { return Model.Data; }
}
public void IncrementCounter()
{
Model.IncrementCounter();
PropertyChanged(this, new PropertyChangedEventArgs("Counter"));
}
public void DecrementCounter()
{
Model.DecrementCounter();
PropertyChanged(this, new PropertyChangedEventArgs("Counter"));
}
}
}
Just a quick and dirty and I hope it's useful to someone. I saw a few different approaches through various Google's but nothing was quite as simple and easy to implement with the least amount of code possible that I wanted. If there's a way to simplify even further please let me know, thanks.
Happy Coding :)
EDIT: To simplify my own code, you might find this useful for making the Adds into one-liners.
private static void Wire(this UIElement element, RoutedCommand command, Action action)
{
element.CommandBindings.Add(new CommandBinding(command, (sender, e) => action(), (sender, e) => { e.CanExecute = true; }));
}
Instead of the VM exposing commands, just expose methods. Then use attached behaviors to bind events to the methods, or if you need a command, use an ICommand that can delegate to these methods and create the command through attached behaviors.
Off course this is possible. You can create just another level of abstraction.
Add you own IMyCommand interface similar or same as ICommand and use that.
Take a look at my current MVVM solution that solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.
See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4
I think you are separating your Project at wrong point. I think you should share your model and business logic classes only.
VM is an adaptation of model to suit WPF Views. I would keep VM simple and do just that.
I can't imagine forcing MVVM upon Winforms. OTOH having just model & bussiness logic, you can inject those directly into a Form if needed.
" you can't reuse a WPF MVVM in a WinForms application"
For this please see url http://waf.codeplex.com/ , i have used MVVM in Win Form, now whenver i would like to upgrade application's presentation from Win Form to WPF, it will be changed with no change in application logic,
But i have one issue with reusing ViewModel in Asp.net MVC, so i can make same Desktop win application in Web without or less change in Application logic..
Thanks...

Resources