How to expose Command classes to XAML views through View Models - c#

I have a simple MVVM architecture where I'm using a view model to bind commands to a xaml view. My commands are fairly generic and I'll be binding them to a couple of views so I've put them into their own classes implementing the ICommand interface. Inside of the view model I have public accessors like so:
private ICommand _myCommand;
public ICommand MyCommand
{
get
{
if (_myCommand == null)
{
_myCommand = new MyCommand(_injectedModel);
}
return _myCommand ;
}
}
This all works and I'm happy with the architecture but ... I have tons of these functions. They all roughly do the same thing - expose the command as a public property, check if a private command already exists and if so use it otherwise create a new command. It's a bit "boilerplate" for my taste.
I would like a nice way to abstract this. I could create a method that looks up commands based on an identifier from some sort of command map. I could just create all of my commands in the view models constructor (rather than doing so lazily).
What would you consider best practice? Should I avoid instantiating a new command inside each view model and have a central command lookup?

I could just create all of my commands in the view models constructor (rather than doing so lazily).
I often do this. The cost of a command is fairly cheap, in most implementations. Depending on your implementation, it's likely just a small class with a single delegate reference (or a pair of delegate references). This is unlikely to be significant enough overhead, IMO, to warrant the lazy construction.
I often write this as:
// Constructor
public MyViewModel()
{
this._injectedModel = SetModel();
this.MyCommand = new MyCommand(_injectedModel);
}
ICommand MyCommand { get; private set; }

You seem to be looking for Caliburn.Micro. It maps ViewModel methods to x:Name in XAML.
You won't need the "boilerplate" code for commands anymore.
Example:
<Button x:Name="Save" />
Will call:
public void Save()
{
//Save logic
}
On button click.
Notice that at no point i made any Command, it's all done behind the scene.
This is a very simple example, you can add parameter(s) to the call and make it use a different event than Click.

Related

How to pass reference to class instance when creating viewmodel

I am developing a program that contains a main view and 5 user controls. I have created the XAML and created a view-model to sit behind each of these views in which the view is bound too.
I have a main Program class and I want to have some other classes such as product, testTool, etc.
When the application starts I load mainWindow, that will then create the mainWindowViewModel and in turn create the Program class.
When a user presses a button I want the mainWindowViewModel to display userControl1 but I want userControl1ViewModel to be able to see the Program class and access its properties and methods.
I keep reading things like "pass the instance of the class in by reference" which is fine but if userControl1View creates userControl1ViewModel how can I pass a reference to the 'program' class created at the start of the program?
This is what dependency injection is designed to solve.
First of all, if you're doing MVVM then you should be able to run your entire application without creating any views at all i.e. only view models. If you have a MainWindow with a ChildView (say) then in general you match those with corresponding view models:
public MainViewModel : ViewModeBase
{
public ChildViewModel MyChild {get; } // gets assigned later
Then in your XAML:
<Window ...
<local:ChildView DataContext="{Binding MyChild}" />
Sometimes you'll need MyChild to display different views, each of which will have its own corresponding view model, and you may need to change it at run-time. In those cases MyChild will need to be of type object (or some common base class) and will also need to support property change notification:
public class MainViewModel : ViewModelBase
{
private object _MyChild;
public object MyChild
{
get { return this._MyChild; }
set
{
if (this._MyChild != value)
{
this._MyChild = value;
RaisePropertyChanged(() => this.MyChild);
}
}
}
}
Then in your XAML you create a ContentControl instead:
<Window ...
<ContentControl ="{Binding MyChild}" />
With this is place you then use DataTemplate in your window or application Resources section to specify which views are matched to which view models:
<DataTemplate DataType="{x:Type vm:FooViewModel}">
<view:FooView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BarViewModel}">
<view:BarView />
</DataTemplate>
So now if you do something like this in your MainViewModel...
this.MyChild = new FooViewModel();
...the ContentControl is automatically populated with a control of type FooView. Furthermore, it's DataContext will automatically be set to the instance of FooViewModel that you created. And you then re-assign it like so:
this.MyChild = new BarViewModel();
...then the FooView will be replaced with a BarView.
So with DataTemplating in place all you have to worry about is passing references of your ViewModels into each other, and that's where dependency injection comes in. It's a big topic, I suggest you go and read up on it, but the idea is you create all of your view models via the DI framework (instead of the new operator) and let it glue all the bits together. Your Products, for example, might be part of a repository class that manages all of them, so you start by declaring an interface:
public interface IProductRepository
{
Products[] AllProducts();
Product GetProductByName(string name);
... etc ...
You then create an actual class that implements this interface and during setup you give your dependency framework the rules for what it should do whenever anything requests an IProductRepository (use a single instance, create a new one etc). Then, whenever anything in your entire application needs to access the product repository, all it has to do is declare a property with an [Inject] tag (this is if you use Ninject, each library has it's own way of doing this):
public class MyClass
{
[Inject]
public IProductRepository ProductRepo {get; set;} // <-- gets injected
Now, when you create an instance of type MyClass the dependency injection framework will create it for you and automatically initialize ProductRepo using the rules you provided it.
That's a very simple overview of how DataTemplating and Dependency Injection work in MVVM, once you start using them you'll wonder how you ever managed without. The main issue in your question, as far as I can tell, is that you're trying to get your view models to talk to each other. In general that's not how MVVM is implemented. View models communicate via services that get injected into them As a general rule of thumb their job is to serve as the conduit between those services and the front-end GUI elements, not each other.
What you're talking about is not actually simple process, what you're talking about is architecture to get the references you expect where you expect them. This can be solved a rather huge number of ways, so I'm going to throw out a fairly unsound but extremely quick example below. Architectural problems are noting inline with // HACK:s
Typically, you'll want the Models coming from a central location, such as database backing, which controls handing over the proper instance.
public abstract class Model
{
// HACK: Don't bother wiring up OnPropertyChanged here, since we don't expect ID to get updated that often, but need a public setter for the Provider
Guid ID { get; set; }
}
// HACK: While using a generic here makes for readable code, it may become problematic if you want to inherit your models
public class ModelProvider<TModelType> where TModelType : Model, new()
{
// HACK: Use better dependency injection than this
private static ModelProvider<TModelType> _instance = new ModelProvider<TModelType>();
public static ModelProvider<TModelType> Instance => _instance;
private ModelProvider() { }
// TODO: Make this into a dictionary of WeakReferences so that you're not holding stale data in memory
ConcurrentDictionary<Guid, TModelType> LoadedModels = new Dictionary<Guid, TModelType>();
private TModelType GenerateModel(Guid id) => new TModelType { ID = id };
private TModelType LoadKnownModel(Guid id)
{
throw new NotImplementedException("Implement a data store to get known models");
}
public TModelType GetNew() => LoadedModels.AddOrUpdate(Guid.NewGuid(). GenerateModel);
public TModelType GetById(Guid id) => LoadedModels.GetOrAdd(id, LoadKnownModel);
}
And then your ViewModels have access to
ModelProvider<Product>.Instance.GetById(WellKnownGuid);
For testing, WellKnownGuid might as well be a static id in Program

Should i create a Class for each button in the interface?

I have a question, i watched a lot of tutorial about MVVM but i'm still confused.
I have an interface with more than one button on it, i have to implement the ICommand interface for bind the command to the View.
I did it like this:
MainSolution
Main Solution
Model
SomeClass.cs
ViewModel
Commands
SomeCommands.cs
SomeViewModel.cs
Ok, now in the interface i have more than one button, each one do separate things, example, one is for start a thread the other one for cancel it and a third one for another thing.
Should i create a separate class, that implement ICommand interface, for each button that i have on the View?
Main Solution
Model
SomeClass.cs
ViewModel
Commands
StartCommands.cs
CancelCommands.cs
OtherCommands.cs
SomeViewModel.cs
I'm asking this because when i implement the ICommand interface i have only one "Execute" method, and only one "CanExecute" method. What is the common way to implement multiple button on a View through binding?
I searched for example online without any luck... A lot of them are so confusing, and surely not for a newbie like me.
The other thing is when i have multiple View and Multiple ViewModel, should i create multiple Commands Folder for nesting it?
Main Solution
Model
SomeClass.cs
ViewModel
FirstCommands
StartCommands.cs
CancelCommands.cs
OtherCommands.cs
SecondCommands
StartCommands.cs
CancelCommands.cs
OtherCommands.cs
FirstViewModel.cs
SecondViewModel.cs
IMHO having a separate class for each command in a classic MVVM approach is an overkill. Use something like the MVVMLight's RelayCommand (and the generic one if you want to pass a parameter).
Then you can define your commands as your ViewModel members and provide Execute/CanExecute implementation as part of your ViewModel as well:
public RelayCommand MyCommand { get; }
public MyView()
{
//[...]
this.MyCommand = new RelayCommand(this.ExecuteMyCommand, this.CanExecuteMyCommand);
}
public void ExecuteMyCommand()
{
//do work!
}
public bool CanExecuteMyCommand()
{
//optional - control if command can be executed at a given time
}
And you can bind to MyCommand in XAML (assuming your View is bound to your ViewModel):
<Button Content='WORK!' Command='{Binding MyCommand}' />
What you want is a DelegateCommand class. You can use Prism, or just google around and steal one; here's one on StackOverflow that I haven't tested but it looks legit. If you don't need a parameter for a command, just ignore the parameter argument.
A DelegateCommand implements ICommand, and calls an Action or Action<object> that you pass to its constructor, like so (from the StackOverflow answer linked above):
public DelegateCommand AddFolderCommand { get; protected set; }
public MyViewModel(ExplorerViewModel explorer)
{
AddFolderCommand = new DelegateCommand(ExecuteAddFolderCommand, (x) => true);
}
public void ExecuteAddFolderCommand(object param)
{
MessageBox.Show("this will be executed on button click later");
}
This should have been included in WPF, but wasn't, for whatever reason.

How to access to all viewmodels via the viewmodel base?

I've created a ViewModel class that have inside the implementation of INotifyPropertyChanged, now I also have other ViewModels that inherit from the ViewModel (base).
All working good actually but I have a doubt.
So let's say that I've in the CustomerViewModel an ObservableCollection called Price, like this:
private ObservableCollection<Models.Price> _price = new ObservableCollection<Models.Price>();
public ObservableCollection<Models.Price> Price
{
get { return _price; }
}
this ObservableCollection should be populated by other classes, 'cause I need to access to the same resource.
I really don't understand how can I do this in mvvm. I though to a Singleton ViewModel, so something like this defined in the base VM:
public static ViewModel Instance { get; set; }
So import all the childs VM into the base and access them via ViewModel.Instance.Price;
but doesn't seems a good practice for me. Any idea?
With this implementation, you can share the same Datasource to all ViewModels
public class PriceGenerator {
private PriceGenerator() {
this.Prices = new ObservableCollection<Price>();
this.Generate();
}
void Generate() {
//Generate Objects here
this.Prices.Add(generatedPrice);
}
public ObservableCollection<Price> Prices {
get;
}
private static PriceGenerator _instance;
public static PriceGenerator Instance => _instance ?? (_instance = new PriceGenerator());
}
There are generally two approaches to this.
Even if you don't have a real database/repository, implement a singleton class that simulates this. This singleton class should also implement INotifyPropertyChanged (and/or INotifyCollectionChanged as appropriate). All ViewModels will be able to access this simulated repository, and interested ViewModels can choose to subscribe to this repository's PropertyChanged callback. For your question, it is generally tidier to have a repository that handles just prices, rather than having a simulated repository that stores 101 different information.
Have a main ViewModel. Some people would visualize that the MainWindow being the main view, with a corresponding main ViewModel. This ViewModel is intentionally made a singleton, which other ViewModels can access through static call. This main ViewModel is basically acting just like #1 - it is like a repository that stores 101 different information. That main ViewModel is likely to look untidy, but it is simply to trace where things are stored - if you need any data, it's probably in there.
Personally, I prefer to use the first approach. lokusking's answer is an example of this approach. Although his "repository" does more than storing data.

Model not implementing INotifyPropertyChanged

In the context of the MVVM pattern, how would one structure the ViewModel when Models do not implement the INotifyPropertyChanged interface?
I like to keep my Models as simple as possibile and implementing the INotifyPropertyChanged interface only for binding purposes seems like unwanted complexity. That's why most of the times i require my VMs to wrap model properties like in the following example:
class ViewModel : INotifyPropertyChanged
{
private Model model;
public int MyProperty
{
get { return model.MyProperty; }
set
{
if (value != model.MyProperty)
{
model.MyProperty = value;
// Trigger the PropertyChanged event
OnPropertyChanged("MyProperty");
}
}
}
/* ... */
}
This will make bindings work ok, including two-way ones.
Now, what would happen if a command executes a model method with complex logic (affecting the value of many properties of different objects)? The model is not implementing INotifyPropertyChanged so there's no way we can know it was updated. The only solution that comes to my mind is to use messaging (mediator pattern) to inform all VMs of the execution of the method so that each VM fires the PropertyChanged event for each potentially affected property:
// Sample ICommand.Execute() implementation
public void Execute(object parameter)
{
var model = (Model)parameter;
model.VeryComplexMethod();
// Just an example, the string "VeryComplexMethodExecuted" is
// sent to all listening VMs. Those VMs will in turn fire the
// PropertyChanged event for each property that may have changed
// due to the execution of the complex model method.
Messaging.Broadcast("VeryComplexMethodExecuted");
}
Please share your ideas, thanks.
Declare your members virtual and use something like Castle Dynamic Proxy to inject change notification automatically:
http://ayende.com/blog/4106/nhibernate-inotifypropertychanged
This has to be used with care when your models are being created in your data layer because it returns a new instance entirely. Your database code will think the object has changed and serialize it back out again which will in turn have a huge impact on performance. Fortunately all the good ORMs provide mechanisms for you to substitute in class wrappers at creation time.

Questions regarding appropriate use of ViewModelLocator in MVVM

I am working on a WPF/MVVM app using MVVM Light. Right now my ViewModelLocator is pretty standard; it includes a static constructor that registers the ViewModels via SimpleIoc and has properties returning the current instances of the ViewModels.
I don't know how appropriate this is, but I have been exploring using instances of ViewModelLocator in ViewModels to access properties of other ViewModels and to change ContentControl in one of my views. If there are any major problems with doing that, please let me know so that I can find a way around it. For example, I may have something in a ViewModel like:
private ViewModelLocator _viewModelLocator = new ViewModelLocator();
private void SomeMethod()
{
_viewModelLocator.OtherViewModel.SomeProperty = something;
}
In a different ViewModel I have the following:
private ViewModelLocator _viewModelLocator = new ViewModelLocator();
public ViewModelBase CurrentViewModel { get; set; }
private void SomeMethod()
{
CurrentViewModel = _viewModelLocator.SomeViewModel;
}
In this case, CurrentViewModel is bound to a ContentControl in my view.
At the moment being able to do this is very convenient but I'd like to get some input from more experienced programmers to make sure that I'm not shooting myself in the foot. If there is a problem with it, are there more acceptable routes I can take to achieve the same results?
Now, if there is nothing wrong with the aforementioned approach, I would like to know if it would be appropriate and/or acceptable to make ViewModelLocator static. To try things out, I did a quick changeover to a static ViewModelLocator. In my MainWindow.xaml, I set the DataContext to:
DataContext="{Binding Source={x:Static vm:ViewModelLocator.Main}}"
...and going back to the first example, I can use:
private void SomeMethod()
{
ViewModelLocator.OtherViewModel.SomeProperty = something;
}
and:
public ViewModelBase CurrentViewModel { get; set; }
private void SomeMethod()
{
CurrentViewModel = ViewModelLocator.SomeViewModel;
}
Right now the program works fine using a static ViewModelLocator, but it is in its infancy, so I'd like to know if this is something that could be a viable option in the future or if I should stay away from a static ViewModelLocator altogether.
Any advice or input on these issues would be greatly appreciated. I am still fairly new to programming and I would like to learn techniques that will serve me well in the long run.
If there are no glaring issues with what I'm doing here, please let me know as well.
Thanks.
It is considered incorrect to reference other view models from view models. This breaks the decoupling that is supposed to make you project more testable and maintainable. If I need to have properties accessable from a number of view models I create a service like iUniversalAppDataService and then use the dependency injection built into MVVM-Light to resolve when the vewimodel is created.
ie this is the constructor of your viewmodel:
public New(IUniversalAppDataService AppDataService)
{
_MyAppDataService = AppDataService;
}
That way any changes/properties within that service are available to any viewmodel that implements the service.
the service needs to be declared in the viewmodellocator as well:
SimpleIoc.Default.Register<IUniversalAppDataService , UniversalAppDataService >
I have used this method to create navigation services that respond to user navigation and of course data services from databases or web data services. I strongly suggest using this service approach because it is WAY easier to maintain in the long run should there be changes to your underlying data model or app architecture.

Categories

Resources