How to perform XAML conversion (eg whole grid or viewbox) to png file?
I need to do this from the ViewModel level.
Link to the example function that I can not call in ViewModel because I do not have access to the object.
Is there a simple and pleasant way?
The view will be responsible for actually exporting the elements that you see on the screen according to the answer you have linked to.
The view model should initialize the operation though. It can do so in a number of different ways.
One option is to send a loosely coupled event or message to the view using an event aggregator or a messenger. Please refer to the following blog post for more information on subject: http://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/.
Another option is to inject the view model with a loose reference to the view. The view implements an interface and uses either constructor injection or property injection to inject itself into the view model, e.g.:
public interface IExport
{
void Export(string filename);
}
public partial class MainWindow : Window, IExport
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel(this);
}
public void Export(string filename)
{
//export...
}
}
public class ViewModel
{
private readonly IExport _export;
public ViewModel(IExport export)
{
_export = export;
}
public void DoExport()
{
//...
_export.Export("pic.png");
}
}
This way the view model only knows about and have a depdenceny upon an interface. It's has no dependency upon the view and in your unit tests you could easily provide a mock implementation of the IExport interface.
The view model will and should never have any access to the actual elements to be exported though. These belong to the view.
You need something like Interaction - a way for VM to take something from view. If you don't want to install a whole new framework for that, just use Func property:
Your VM:
public Func<string, Bitmap> GetBitmapOfElement {get;set;}
...
//in some command
var bmp = GetBitmapOfElement("elementName");
Then, in your view you have assign something to that property:
ViewModel.GetBitmapOfElement = elementName =>
{
var uiElement = FindElementByName(elementName); // this part you have figure out or just always use the same element
return ExportToPng(FrameworkElement element); // this is the function form the link form your answer modified to return the bitmap instead of saving it to file
}
If you need it async, just change property type to Func<string, Task<Bitmap>> and assign async function in your view
What about dependency properties? Consider the following class that is used to passing data (the data may be a stream or whatever you want):
public class Requester
{
public event Action DataRequested;
public object Data { get; set; }
public void RequestData() => DataRequested?.Invoke();
}
Then you create a usercontrol and register a dependency property of type Requester:
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty RequesterProperty
= DependencyProperty.Register("Requester", typeof(Requester), typeof(MainWindow),
new PropertyMetadata(default(Requester), OnRequesterChanged));
public MyUserControl()
{
InitializeComponent();
}
public Requester Requester
{
get => (Requester) GetValue(RequesterProperty);
set => SetValue(RequesterProperty, value);
}
private static void OnRequesterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> ((Requester) e.NewValue).DataRequested += ((MyUserControl) d).OnDataRequested;
private void OnDataRequested()
{
Requester.Data = "XD";
}
}
And your view model would look something like this:
public class MainWindowViewModel
{
public Requester Requester { get; } = new Requester();
public void RequestData() => Requester.RequestData();
}
In XAML you simply bind the dependency property from your control to the property in your view model:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<local:MyUserControl Requester="{Binding Requester}"/>
</Grid>
</Window>
Related
I have several different ViewModels that I would like to display in the same view (MainPage.xaml).
I'm new to this and don't know how to do it. I have tried to create a MainViewModel:
public class MainViewModel : ViewModelBase, INotifyPropertyChanged
{
WeatherViewModel weatherView = new WeatherViewModel();
ForecastViewModel forecastViewModel = new ForecastViewModel();
DeparturesViewModel departuresViewModel = new DeparturesViewModel();
CalenderViewModel calenderViewModel = new CalenderViewModel();
}
public void GetAllViews()
{
weatherView.GetCurrentTemp();
forecastViewModel.GetForecastTemp();
departuresViewModel.GetDepartures();
calenderViewModel.GetCalender();
}
And in my MainPage.xaml.cs I have this:
public MainPage()
{
this.InitializeComponent();
this.DataContext = new MainViewModel();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as MainViewModel;
vm.GetAllViews();
}
I manage to display each ViewModel individually like this instead:
this.DataContext = new WeatherViewModel();
but I would like to display everything in the same View.
I think you're on the right track but missed some small but important pieces.
In your example code the MainViewModel class is currently setup with private fields where you really need public properties. Additionally, I would make sure ViewModelBase implements INotifyPropertyChanged if it's not already; that way none of the classes deriving from ViewModelBase need to worry about that part.
public abstract class ViewModelBase : INotifyPropertyChanged
{
/* INotifyPropertyChanged implementation +
whatever other common behavior makes sense
belongs in this class
*/
}
public class MainViewModel : ViewModelBase
{
public WeatherViewModel Weather { get; } = new WeatherViewModel();
public ForecastViewModel Forecast { get; } = new ForecastViewModel();
public DeparturesViewModel Departures { get; } = new DeparturesViewModel();
public CalendarViewModel Calendar { get; } = new CalendarViewModel();
}
In your view code behind file you're setting the data context to 2 different instances of MainViewModel - once in the constructor and once in the Loaded event handler. I'd stick with the constructor version or instead you could set the data context in XAML like this:
<MainPage.DataContext>
<MainViewModel>
</MainPage.DataContext>
Once the data context for the main page is setup and the view models are public properties then you can use bindings to access the state (properties) of the view models perhaps something like this:
<TextBlock Text='{Binding Path=Weather.CurrentTempCelsius, StringFormat='Current Temp: {0}°C'}' />
Multiple ViewModels in same View
You have many ways to approach. Fist way using x:bind. You could initialize each view model in the page resource and give them x:Name, then using x:bind to access specific property like following.
<Page.Resources>
<local:CalenderViewModel x:Name="CalenderViewModel"/>
<local:DeparturesViewModel x:Name="DeparturesViewModel"/>
<local:ForecastViewModel x:Name="ForecastViewModel"/>
<local:WeatherViewModel x:Name="WeatherViewModel"/>
</Page.Resources>
<Grid>
<TextBlock Text="{x:Bind WeatherViewModel.temperature}"/>
</Grid>
Other Way is that integrate all the ViewModels into MainViewModel. And coding.monkey provide the correct solution that you could refer directly.
In one hand I have my model which had to collect data from several files and build a oriented object database, and in another I have my interface in which I want to display data from my database . So I use binding but my ComboBox, etc.. remain empty. I have the feeling that my database is built then erased when the interface is launched. Here's the code of my Main defined in the App.xaml.cs:
public partial class App : Application
{
[STAThread]
public static void Main()
{
var application = new App();
application.InitializeComponent();
DirectoryInfo dir = new DirectoryInfo("P:\\....");
Model model = new Model(dir);
model.entityBox.initialize();
application.Run();
}
}
Code for binding in MainWindow.xaml:
<Window.DataContext>
<local:EntityBox></local:EntityBox>
</Window.DataContext>
<Grid>
<ComboBox x:Name="critereComboBox" ItemsSource="{Binding Criteres}"/>
In EntityBox.cs:
private List<string> _criteres = new List<string>();
public void initialize()
{
_criteres.Add("TXC");
_criteres.Add("TYC");
_criteres.Add("TZC");
_criteres.Add("MXC");
_criteres.Add("MYC");
_criteres.Add("MZC");
}
public List<string> Criteres
{
get{ return _criteres; }
}
You need to initialize combobox inside context class, because when you use XAML to bind your data context, the context class is created independently by XAML, the model creation in Main function has literally no effect to your Control.
You also need to implement INotifyPropertyChanged to your Model (ViewModel?) class. I am also suggest you to step into MVVM approach.
I suppose it's a one-way binding. A short answer is to use ObservableCollection
private ObservableCollection<string> _criteres = new ObservableCollection<string>();
As it will notify when you call Add, but you might need to call them in UIDispatcher.
i'm having a problem with mediator pattern in mvvm
I'l describe almost all classes for better understanding of my problem.
I'v got MainWindow and ViewModel for it, it is very simple and auctually doing nothing but holding one of my UserControls, there is a UserControl property in ViewModel that is binded to ContentControl.Content in MainWindow.
UserControls are identical there is only a single button in each of them,
and allso there are two ViewModels with commands to handle clikcs.
Class Mediator is a singletone and i tried to use it for iteraction between my ViewModel
So what i'm trying to do is to switch between UserControls, not creating them and their ViewModel inside a MainWindowViewModel. Switching must take place after i'm clicking a buttons. For example if i click on the button on FirstUserControl then ContentControl of the MainWindow should switch to SecondUserControl.
The probleam appears in UserControlsViewModels where i should pass UserControls object as a parameters in Mediator NotifyCollegue() function, but i have no acces to them
(of course, that is one of the principles of MVVM), and that is the problem of user types, because with standart types that should not be a problem (for example to pass int or string...).
i found this solutin here
http://www.codeproject.com/Articles/35277/MVVM-Mediator-Pattern
And why i can't swith UserControls in MainWindowViewModel, because i want the MainWindow to be clear of everything except current UserControl binded to ContentControl.
What may be possible solutions to this problem, should i make another singletone class and collect all the userControls references there and use them inside UserControlsViewModels, or maybe something else?
I hope that I have clearly described my problem, and that there is some kind of solution.
I will be glad to answer any question and very grateful for the help!!!
oh, and that is not the real app, i just want to get the idea(concept) of mesaging system between ViewModels, not mixing ViewModel and not creation Views and their ViewModels inside of other ViewModels...
Thanks again!
MainView
<Window x:Class="TESTPROJECT.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TESTPROJECT"
mc:Ignorable="d"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="500" Width="750">
<Grid>
<ContentControl Grid.Row="1" Content="{Binding PagesControl}"/>
</Grid>
MainView ViewModel
namespace TESTPROJECT
{
class MainWindowViewModel : ViewModelBase
{
private UserControl _pagesControl;
public UserControl PagesControl
{
//Property that switches UserControls
set
{
_pagesControl = value;
OnPropertyChanged();
}
get
{
return _pagesControl;
}
}
public MainWindowViewModel()
{
//Method that will be listening all the changes from UserControls ViewModels
Mediator.Instance.Register(
(object obj) =>
{
PagesControl = obj as UserControl;
}, ViewModelMessages.UserWroteSomething);
}
}
}
FirstUserControl
<UserControl x:Class="TESTPROJECT.FirstUserControl"
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:local="clr-namespace:TESTPROJECT"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button Command="{Binding GetCommand}">
hello, i'm first user control!
</Button>
</Grid>
FirstUserControl ViewModel
namespace TESTPROJECT
{
class FirstUserControlViewModel : ViewModelBase
{
//command that is binded to button
private DelegateCommand getCommand;
public ICommand GetCommand
{
get
{
if (getCommand == null)
getCommand = new DelegateCommand(param => this.func(param), null);
return getCommand;
}
}
//method that will handle button click, and in it i'm sending a message
//to MainWindowViewModel throug Mediator class
//and that is allso a problem place because in theory i should
//pass the opposite UserControl object , but from here i have no
//acces to it
private void func(object obj)
{
Mediator.Instance.NotifyColleagues(
ViewModelMessages.UserWroteSomething,
"PROBLEM PLACE");
}
}
}
SecondUserControl
<UserControl x:Class="TESTPROJECT.SecondUserControl"
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:local="clr-namespace:TESTPROJECT"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button Command="{Binding GetCommand}">
Hello, i'm second user control!
</Button>
</Grid>
SecondUserControl ViewModel
namespace TESTPROJECT
{
class SecondUserControlViewModel : ViewModelBase
{
//command that is binded to button
private DelegateCommand getCommand;
public ICommand GetCommand
{
get
{
if (getCommand == null)
getCommand = new DelegateCommand(param => this.func(param), null);
return getCommand;
}
}
//method that will handle button click, and in it i'm sending a message
//to MainWindowViewModel throug Mediator class
//and that is allso a problem place because in theory i should
//pass the opposite UserControl object , but from here i have no
//acces to it
private void func(object obj)
{
Mediator.Instance.NotifyColleagues(
ViewModelMessages.UserWroteSomething,
"PROBLEM PLACE");
}
}
}
Class Mediator
and
enum ViewModelMessages
namespace TESTPROJECT
{
//this enum holding some kind of event names fro example UserWroteSomething
// is a name of switching one UserControl to another
public enum ViewModelMessages { UserWroteSomething = 1 };
class Mediator
{
//Singletone part
private static Mediator instance;
public static Mediator Instance
{
get
{
if (instance == null)
instance = new Mediator();
return instance;
}
}
private Mediator() { }
//Singletone part
//collection listeners that holds event names and handler functions
List<KeyValuePair<ViewModelMessages, Action<Object>>> internalList =
new List<KeyValuePair<ViewModelMessages, Action<Object>>>();
//new listener registration
public void Register(Action<object> callBack, ViewModelMessages message)
{
internalList.Add(
new KeyValuePair<ViewModelMessages, Action<Object>>(message, callBack));
}
// notifying all the listener about some changes
// and those whose names fits will react
public void NotifyColleagues(ViewModelMessages message, object args)
{
foreach(KeyValuePair<ViewModelMessages, Action<Object>> KwP in internalList)
if(KwP.Key == message)
KwP.Value(args);
}
}
}
App starting point
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
FirstUserControl first = new FirstUserControl() { DataContext = new FirstUserControlViewModel() };
SecondUserControl second = new SecondUserControl() { DataContext = new SecondUserControlViewModel() };
new MainWindow()
{
DataContext = new MainWindowViewModel() { PagesControl = first }
}.ShowDialog();
}
}
If I understand you correctly, you want to navigate to another view (or view model respectively) when a certain action on the currently active view model happens (e.g. you press a button).
If you want to use your mediator for this, you could structure it like this:
public class Mediator
{
// These fields should be set via Constructor Injection
private readonly MainWindowViewModel mainWindowViewModel;
private readonly Dictionary<ViewModelId, IViewFactory> viewFactories;
public void NotifyColleagues(ViewModelId targetViewModelId, ViewModelArguments arguments)
{
var targetFactory = this.viewModelFactories[targetViewModelId];
var view = targetFactory.Create(viewModelArguments);
this.mainWindowViewModel.PagesControl = view;
}
// other members omitted to keep the example small
}
You would then create a factory for every view - view model combination. With the ViewModelArguments, you can pass information into the newly created view models that originate from other view models. ViewModelId can be a simple enum like your ViewModelMessage, instead you can also use the Type of the view model (which I would advise you to pursue).
Furthermore, I would advise you to not use a private constructor on the Mediator class because otherwise you cannot pass in the mainWindowViewModel and the dictionary for the view factories. You should be able to configure this in your Application-Startup method.
Also, please note that there are many other ways to structure MVVM applications, like e.g. using Data Templates to instantiate the view for a view model - but I think that is a bit too stretched for your little example.
I want display fake data in Visual Studio Designer. I use View Service Interface way with this aborecence (it is minimal example) :
-ServiceView
-IMainWindow.cs
-ICustomer.cs
-SampleData
-MainWindowDesign.cs
-CustomerDesign.cs
-Data.xaml
IMainWindow.cs
namespace TestDesignSampleData.ServiceView
{
interface IMainWindow
{
ObservableCollection<ICustomer> Customers { get; }
}
}
ICustomer.cs
namespace TestDesignSampleData.ServiceView
{
interface ICustomer
{
string Name { get; set; }
}
}
MainWindowDesign.cs
namespace TestDesignSampleData.SampleData
{
class MainWindowDesign : IMainWindow
{
public ObservableCollection<ICustomer> Customers { get; set; }
public MainWindowDesign()
{
//Customers = new ObservableCollection<ICustomer>();
}
}
}
CustomerDesign.cs
namespace TestDesignSampleData.SampleData
{
class CustomerDesign : ICustomer
{
public string Name { get; set; }
}
}
Data.xaml
<sd:MainWindowDesign xmlns:sd="clr-namespace:TestDesignSampleData.SampleData">
<sd:MainWindowDesign.Customers>
<sd:CustomerDesign Name="Charly Sparow"/>
<sd:CustomerDesign Name="Jacky Chan"/>
<sd:CustomerDesign Name="Dora Exploring"/>
</sd:MainWindowDesign.Customers>
</sd:MainWindowDesign>
This can build and execute, but the data are not displayed and the designer send this error for each line of the collection in Data.xaml :
Error 1 The specified value cannot be assigned to the collection.
The following type was expected: "ICustomer".
For some reason, Customer Design is not recognized as ICustomer.
EDIT:
I think it is Visual Studio designer bug. I dont have this problem with Blend. Maybe the Blend designer's compiler is more advanced.
Minimal Project to reproduce the error :
https://github.com/Orwel/DesignerError/releases/tag/CompiledProject
I think XAML designer has some issue with interpreting arrays/collections with interface type. If interface is not necessity, you can use inheritance instead.
Define your ICustomer -> create base class Customer that implements ICustomer and then create your special classes by inheriting Customer not ICustomer.
As you will have base class you can create array/collection of Customers instead of ICustomer which works correctly with XAML designer in Visual Studio.
I can't quite determine if/when/how you're binding to your MainWindowDesign class, but I've had some luck in using the following structure (customised for your example) to show design time data in WPF.
public class MainWindowDesignFactory
{
public static IMainWindow ViewModel
{
get
{
return new MainWindowDesignFactory().Create();
}
}
public IMainWindow Create()
{
var vm = new MainWindowDesign();
vm.Customers = new ObservableCollection<ICustomer>()
{
new CustomerDesign() { Name = "Charly Sparow" },
new CustomerDesign() { Name = "Jacky Chan" },
new CustomerDesign() { Name = "Dora Exploring" }
};
return vm;
}
private class MainWindowDesign : IMainWindow
{
public ObservableCollection<ICustomer> Customers { get; set; }
}
private class CustomerDesign : ICustomer
{
public string Name { get; set; }
}
}
And then in the XAML file:
<Window
x:Class="DesignTimeDataExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:TestDesignSampleData_SampleData="clr-namespace:TestDesignSampleData.SampleData"
Title="MainWindow"
Height="350"
Width="525"
d:DataContext="{x:Static TestDesignSampleData_SampleData:MainWindowDesignFactory.ViewModel}">
<Grid>
<ListBox ItemsSource="{Binding Customers}" />
</Grid>
</Window>
The important elements here are the d:DataContext attribute in the XAML (which does pretty much what you expect it to, set a DataContext while you are in the designer) and the actual MainWindowDesignFactory class, which has a static ViewModel property to bind to.
There are other ways, including using:
d:DataContext="{d:DesignInstance Type={x:Type TestDesignSampleData_SampleData:MainWindowDesign}, IsDesignTimeCreatable=True}"
But I've had more luck with the Factory/Static property combination (the design time creation of an instance using the DesignInstance attribute is a bit wonky, using a manually created Factory gave me more control and less arcane designer errors).
The code above is a little rough but it should give you a starting point.
Okay. So what I need to do is to initialize a ViewModel using a constructor. The problem is I can't create the constructor due lack of knowledge. I'm new to MVVM (or c# in general for that matter) and had to get some help to implement this code:
public class ViewModel
{
private static ViewModel instance = new ViewModel();
public static ViewModel Instance
{
get
{
return instance;
}
}
}
However, I fail to create a constructor to place this code.
DataContext = ViewModel.Instance
It is meant to go into two different pages to pass a value between TextBoxes.
I'm also confused as to whether I should put the ViewModel in both the main window and the page or in just one of the two.
So, anyone can help?
Follow this pattern:
This part is how your model classes should look like,
Even if you use entity framework to create your model they inherit INPC.. so all good.
public class Model_A : INotifyPropertyChanged
{
// list of properties...
public string FirstName {get; set;}
public string LastName {get; set;}
// etc...
}
each view model is a subset of information to be viewed, so you can have many view models for the same model class, notice that in case your make the call to the parameter-less c-tor you get auto instance of a mock model to be used in the view model.
public class ViewModel_A1 : INotifyPropertyChanged
{
public Model_A instance;
public ViewModel()
{
instance = new instance
{ //your mock value for the properties..
FirstName = "Offer",
LastName = "Somthing"
};
}
public ViewModel(Model_A instance)
{
this.instance = instance;
}
}
And this is for your view, if you view in the ide designer you will have a mock view model to show.
public class View_For_ViewModelA1
{
public View_For_ViewModel_A1()
{
//this is the generated constructor already implemented by the ide, just add to it:
DataContext = new ViewModel_A1();
}
public View_For_ViewModel_A1(ViewModel_A1 vm)
{
DataContext = vm;
}
}
XAML Side:
<Window x:Class="WpfApplication1.View_For_ViewModel_A1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ViewModel="clr-namespace:WpfApplication1"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance ViewModel:ViewModel_A1, IsDesignTimeCreatable=True}"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
</Grid>
</Window>
In a more advanced scenario you would want to have a single view model class to relate to several model classes.. but you always should set a view to bind to a single view model.
if you need to kung-fu with your code - make sure you do that in your view model layer.
(i.e. creating a view-model that have several instances of different model types)
Note: This is not the complete pattern of mvvm.. in the complete pattern you can expose command which relate to methods in your model via your view-model and bind-able to your view as well.
Good luck :)
I basically follow this pattern:
public class ViewModelWrappers
{
static MemberViewModel _memberViewModel;
public static MemberViewModel MemberViewModel
{
get
{
if (_memberViewModel == null)
_memberViewModel = new MemberViewModel(Application.Current.Resources["UserName"].ToString());
return _memberViewModel;
}
}
...
}
To bind this to a page is:
DataContext = ViewModelWrappers.MemberViewModel;
And if I'm using more than 1 ViewModel on the page I just bind to the wrapper.
DataContext = ViewModelWrappers;
If you or anybody else, who's new to the MVVM, gets stuck here, for example at the "INotifyPropertyChanged could not be found". I recommend trying some example-MVVM's or tutorials.
Some I found useful:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
https://www.youtube.com/watch?v=EpGvqVtSYjs&index=1&list=PL356CA0B2C8E7548D