How Can I bind DataContext to a Generic ViewModel in XAML? - c#

Suppose we have a generic View model like this:
public class MyViewModel<T> : INotifyPropertyChanged where T : Class1
{
private T _objectModel;
public MyViewModel(T object)
{
_objectModel= object;
}
public event PropertyChangedEventHandler PropertyChanged;
}
When I want to bind this View Model to DataContext of my UserControl in XAML, I can not! XAML editor does not find My View Model class. How should I refer to a generic type in XAML?
<UserControl.DataContext>
<s:MyViewModel<T>/> // How should I write this here????
</UserControl.DataContext>
In the above code s is an alias for my workspace, and If I convert my generic View Model to a concrete class it works normally.

When working with XAML, you cannot instantiate a view model with a generic parameter in XAML code.
To work around this, you need to make use of inheritance, here's an example:
public abstract class ViewModel<T>
Usage:
public class MovieViewModel : ViewModel<Movie>
...
public class GenreViewModel : ViewModel<Genre>
Creating a new class for each model seems to be a bit stupid, however this simply isn't true. By making the assumption that each view model contains one model, you've pretty much set yourself up for following this pattern in all view models, as your base view model enforces this constraint.
I personally use the pattern of using a ViewModel<T> base class, where T is the model.
It's certainly a good idea to keep the logic separated from your base view model. A view model for each model is in fact a very good pattern to implement.
There is another way you can achieve what you're after, this is simply removing the generic from the view model base, consider the example:
public class ViewModel
{
public object Model { get; protected set; }
}
Now, if you populate Model with let's say a Movie, then XAML will see it as a Movie, and not an object. Now this is pretty nifty as far as your XAML side goes, however, when you start working with this model in C#, then you're going to have all sorts of problems as you'll have to cast the object to whatever type you are using. So I wouldn't recommend this at all.
Another method of getting around this would be to set the DataContext in code-behind, and if you're going to do that, then, well, only God can save you now. The main ideas around the MVVM design pattern is the separation of View logic and the Business layer (View Models), as soon as your View starts instantiating view models then that nice separation is lost.
Now saying that, there's nothing stopping you from doing this, I've said this many times before. MVVM is a design pattern, not the law, if you want to set the DataContext in code-behind, then fair enough, however it's important that you are aware of the implications.

You could create a class that inherits from your generic ViewModel and use that
public class PersonViewModel : ViewModel<Person>
XAML:
<UserControl.DataContext>
<s:PersonViewModel/>
</UserControl.DataContext>

You're not capable of setting a generic viewmodel in XAML because XAML requires known types at compile time.
Dependency injection is your best bet
public class MyControl : UserControl{
public MyControl(Object viewModel){
this.DataContext = viewModel;
}
}

If your ViewModel is derived from a base class, let's say NonGenericViewModel then you can assign in code behind an object of type NonGenericViewModel to the DataContext. Using this way you still have the benefits of generics and the data binding will also work because the bindings will be made during runtime, no matter what type of object you assign to DataContext as long as it has properties, collections, etc required by your xaml controls.
BaseViewModel<T> : NonGenericViewModel { ... }
NonGenericViewModel : INotifyPropertyChanged { ... }
And in code behind, in the ctor of your xaml.cs:
NonGenericViewModel nonGenVM = new BaseViewModel<person>();
this.DataContext = nonGenVM;
Even this is correct and working:
this.DataContext = new BaseViewModel<Person>();
It depends if you need or not the class NonGenericViewModel in other places.

Related

Call a CodeBehind Method from ViewModel

i am trying to learn the MVVM design pattern by coding a little App using Xamarin.Forms.
I safe all the data in the Android File-System (XML) to make it persistent. But at the moment all the methods to safe the Data are in the ViewModel and i am trying to move those Methods to the CodeBehind (Model) in order to respect the MVVM design pattern.
After i moved all the Methods to the CodeBehind i am not able to call the functions within my ViewModel even though they are within the same namespace and declared as public.
The Method DeleteCar(...) within my ViewModel tries to call the method ToCarsFile(...) which is in CodeBehind:
public void DeleteCar(object sender)
{
AllCars.Remove(sender as Car);
ToCarsFile(AllCars); //This is underlined in red
}
CodeBehind (.xaml.cs):
public void ToCarsFile(ObservableCollection<Car> CarsList)
{
--SOME CODE FOR DATA PERSISTENCE--
}
I get the error "Error CS0103: The name 'ToCarsFile' does not exist in the current context (CS0103) (ProjectCars)"
Probably a stupid mistake but what am i doing wrong?
Lots of things going on here:
1st. Since functions are declared in classes, then you need an instance of the class to call it
public class SomeModel {
public void ToCarsFile(ObservableCollection<Car> CarsList)
{
--SOME CODE FOR DATA PERSISTENCE--
}
}
So to call it, you need something like this
var modelObject = new SomeModel();
modelObject.ToCarsFile(/*data*/);
Your xaml.cs should not be your model. Both ViewModel and Model should be independent from the view (the xaml.cs class is your view class)
So there is something in your MVVM wiring that needs some refining

How to implement the Model in MVVM

I am currently studying the MVVM pattern. So thus far I developed a simple demo programm which contains a view and a viewmodel with commands etc. Now I want to implement a Model but I am not quite sure how to do so. My demo contains a view with a textbox and a button. When the Button gets pressed a Command is launched.
The text from the textbox should be written in a textfile with upper cased letters. This functionality should be part of my model. How do i call this functionality from my viewmodel? Should the viemodel contain a instance of the model class and call a methode in the command execute? And how does the viewmodel get data from a model?
Thank you very much for your help!
Yes. You could instantiate a model object in the viewmodel and have it save the text in a textfile (Or whatever you want your application to do)
class ViewModelDefault : INotifyPropertyChanged
{
// Bound to your textbox
public string TextboxProperty { get; set;}
// Instantiate modellayer in viewmodel
private ModelClass _modelClass = new ModelClass();
// RelayCommand property -> bound to button on viewmodel
// Will execute method "ExecuteCommand" that contains a call to a method in the ModelClass
public ICommand ExecuteModelMethod
{
get {
RelayCommand relayCommand = new RelayCommand(ExecuteCommand);
return relayCommand;
}
}
// Method that the RelayCommand will execute.
private void ExecuteCommand()
{
_modelClass.SaveTextInTextfile(TextboxProperty);
}
...
}
In the code above I've made an example of how this could be done using RelayCommand.
RelayCommand is a class that makes use of delegates such as Action and Func. This means you can pass a method into the RelayCommand object and have it execute it.
What RelayCommand allows you to do is basicly binding a method through delegate to UI control in the view layer.
Read up on Delegates if you wish to study further on that topic.
Delegates (C# Programming Guide)
Usually for data storage and retrieval, I create a separate class called repository.
Your view model has an instance of the repository (or better: an interface of it).
In the repository class, you can do your file access.
By the way: If your view model just knows the interface of the repository, you could replace it later with a database access, and the view model would not be affected.
The view model can then interact with the repository i.e. call it methods when the command code in the view model executes.
You write...
"The text from the textbox should be written in a textfile with upper
cased letters. This functionality should be part of my model."
The model usually is just data, so a model class does not have functionality but only properties. Like I said: Do your data access in the view model or in the repository class.
In case of MVVM it would be good if the properties implement INotifyPropertyChanged , like the properties of your view model.
If you just want to write the content of a single text box, then your model would be a class with just one property.

Change current implementation of basic MVVM to adhere to SOLID pattern

I have been writing all my MVVM application with basic design pattern generally mentioned in MVVM examples available online. The pattern that I am following is described below:
Model
This section include DTO classes with their properties and Interfaces IDataService and like:
public class Employee
{
public string EmployeeName { get; set; }
public string EmployeeDesignation { get; set; }
public string EmployeeID { get; set; }
}
public interface IDataService
{
public Task<Employee> GetEmployeeLst();
}
Proxy
This layer contains Dataservice calls which implement IDataservice like:
public class DataService : IDataService
{
public async Task<Employee> GetEmployeeLst()
{
// Logic to get employee data from HTTPClient call
}
}
ViewModel
This layer contains ViewModel and reference to Model and Proxy layer from which all data is received:
public class BaseViewModel
{
public BaseViewModel(INavigationService nav, IDataService data, IAESEnDecrypt encrypt, IGeoLocationService geoLocation, IMessageBus msgBus, ISmartDispatcher smtDispatcher)
{
}
// This also include common methods and static properties that are shared among most of the ViewModels
}
All the ViewModel inherits BaseViewModel. Each viewModel also contains Delegatecommand which is executed when UI triggers an event. Which then fetches the data from the server by calling DataService in proxy layer and perform business logic and populates the properties in ViewModel which is binded to the view. For each View there is a VM which is binded to the Datacontext of the View.
ViewModel is also responsible for starting an animation I have used trigger to start storyboard which is binded to my enums in VM for state change of these trigger as in example in: http://www.markermetro.com/2011/05/technical/mvvm-friendly-visual-state-management-with-windows-phone-7/
View
In this layer I have all my Views, Usercontrols and business logic with implementation of certain dependencies like GeoLocation Service, AES encryption, NavigationService between Views etc.
Every View has .xaml and .xaml.cs file. In .xaml.cs file I have binded the data context of the view with VM like this:
this.DataContext = App.IOConatiner.GetInstance<DashboardViewModel>();
and from here on all binding happens.
My problem is that recently I had the knowledge that this pattern is not following a SOLID design pattern which I got know in this answer of my question:
Simple Injector inject multiple dependency in BaseClass
I am trying very hard to change my design as per the suggestion given in my previous question's answer. But I am not able to get some of the things like:
Currently View Datacontext is binded to ViewModel hence all the controls are controlled by a property in VM. How would I change this to your above mentioned pattern with Processor/Service or DialogHandler?
I am using Delegatecommands which are binded to command property of UI element. Execution of these command certain action happens like animation, usercontrol is displayed. How to do it in command pattern?
How can I start changing my current implementation to accommodate all those changes with best possible approach?
First of all an answer to your question 3
How can I start changing my current implementation to accommodate all those changes with best possible approach?
This is the very first step you need to take. It is not a case of some smart refactoring of your current code. You will need to take a step back and design the application. I once read some nice blog about (re)design.
Before starting to write any code, define how many different basic types of views you will want to show to the user? E.g.:
Just show (any type of) data
Edit data
Alert user
Ask user for input
...
When you defined your different requirements, you can translate this to specific interfaces that are tailor made for the job they serve. For example a view that lets the user edit data will typically have an interface that will look something like:
public interface IEditViewModel<TEntity>
{
public EditResult<TEntity> EditEntity(TEntity entityToEdit)();
}
Once you every detail of this design in place, you must decide how you will show your views to the user. I used another interface for this to handle this task for me. But you could also decide to let a navigation service handle this kind of task.
With this framework in place, you can start coding your implementations.
Currently View Datacontext is binded to ViewModel hence all the controls are controlled by a property in VM. How would I change this to your above mentioned pattern with Processor/Service or DialogHandler?
This will not change in this design. You will still bind your view to your viewmodel and set the datacontext to the viewmodel. With a lot of views the use of an MVVM framework like Caliburn Micro will come in handy. This will do a lot of the MVVM stuff for you, based on Convention over Configuration. To start with this model, would make the learning curve even higher, so my advice to start of by hand. You will learn this way what happens under the covers of such an MVVM tool.
I am using Delegatecommands which are binded to command property of UI element. Execution of these command certain action happens like animation, usercontrol is displayed. How to do it in command pattern?
I'm not sure if the command pattern you mention here is the command pattern I advised you in the previous answer. If so, I think you need to reread this blog, because this is totally unrelated to the commands I think you mean in this question.
Animation and that sort of stuff is the responsibility of the view, not the viewmodel. So the view should handle all this stuff. XAML has a lot of ways to handle this. More than I can explain here. Some ideas: Triggers, Dependency Properties
Another option: Code behind! If the logic is purely view related IMO it is not a mortal sin to place this code in the code behind of your view. Just don't be temped to do some gray area stuff!
For commands that just perform a method call in your viewmodel, ICommand is still possible and MVVM tools like Caliburn will do this automagically...
And still: Loose the base class....
Why are you injecting all these services in your viewmodel base class if the viewmodel base class does not make use of these services himself ?
Just inject the services you need in the derived viewmodels that do need those services.

WPF MVVM - Accessing properties of other ViewModels using delegates

I have a MainViewModel, which features PersonViewModel and a HouseViewModel as properties. HouseViewModel has the property GetRooms. What is the best way to access this property from the PersonViewModel?
My solution at the minute is to pass through an instance of MainViewModel to PersonViewModel, then I can call MainViewModel.HouseViewModel.GetRooms. However, this seems a little wasteful.
I am happy to pass a function as a delegate, but I can't seem to do this with a Property. I have searched for an example of this and only come up with overly complicated techniques. I'm assuming there must be a simple way of doing this, as it seems like a common problem. Can anyone point out a strong example?
Or is there another, alternative method that I haven't considered?
If a method has to be shared across two viewmodel, it should be defined in base viewmodel or a service. The best way is a common Service class should hold all common methods like GetRooms, CheckIn, CheckOut, etc. And this service should be provided to every viewmodel using Dependency Injection.
public class HomeViewModel
{
public HomeViewModel(IRoomService roomservice)
{
}
}
public class PersonViewModel
{
public PersonViewModel(IRoomService roomservice)
{
}
}

Extending WPF application

I've got a WPF MVVM application. One of my views has a user control that needs to be customizable for each installation. It's basically a sketch of the customers installation with some labels etc. bound to a viewmodel.
Now my problem is that this user control is different on each site/installation. One approach is to load the xaml from a file/database runtime using a xaml reader. This works but since my viewmodel is generic I have to bind to methods instead of properties and I can't load a xaml with objectdataprovider.
Currently I'm trying to see if MEF can be used so that I can create the user control as a plug-in. So what I'm looking for now is this:
how can I define a user control with view/view model that exports a contract for MEF
How can my parent view (in my wpf app) load the imported user control
Any tips are appreciated, or maybe someone has a different approach?
I suggest you look into Prism in combination with MEF. It has a notion of Modules (plug-ins in your case) and Regions (mechanism of dynamically loading views).
You will be able to export a view using a simple attribute:
[ViewExport(RegionName = RegionNames.MyRegion)]
public partial class MyView : UserControl {
public MyView() {
this.InitializeComponent();
}
[Import]
public MyViewModel ViewModel {
set { DataContext = value; }
}
}
[Export]
public class MyViewModel : ViewModelBase
[
...
}
And in your main application XAML you will be able to import the plugin's views like this:
<ContentControl Regions:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.MyRegion}"/>
One thing I'd consider is the design where you need to install a custom View for each installation. Instead, I'd look to make that View more generic. This will make your design more simple in the long run. Plus, you are setting up for a maintenance nightmare with a different installation for every installed base.
It's a little difficult to tell from your description, but it sounds like the View is a collection of some kind of an object (some kind of drawing with a label or something). Therefore, I'd treat it as such.
I'd create a base abstract class that describes what every object that your View could show. Since I don't have more information, I'll call this thing a "DrawingObject" for lack of a better term. This class would hold all information common to all objects in your View. Note that ObservableItem is a class that implements INotifyPropertyChanged, and SetProperty sets the value in that base class and raises PropertyChanged.
abstract class DrawingObject : ObservableItem
{
Point mPosition;
public Point Position
{
get { return mPosition; }
set { SetProperty("Position", ref mPosition, value); }
}
String mLabelText;
public String LabelText
{
get { return mLabelText; }
set { SetProperty("LabelText", ref mLabelText, value); }
}
}
Then, derive more custom objects from that base class:
class Counter : DrawingObject
{
public Counter() : base()
{
}
}
Your ViewModel would then just have a collection of these objects, using the base class. The set may be private, because you will probably get the objects from someplace in the constructor (i.e. the database, or a flat file, or...)
class ViewModel : ObservableItem
{
public ViewModel() : base()
{
// Call something to populate DrawingObjects property
PopulateDrawingObjects();
}
ObservableCollection<DrawingObject> mDrawingObjects =
new ObservableCollection<DrawingObject>();
public ObservableCollection<DrawingObject> DrawingObjects
{
get { return mDrawingObjects; }
private set { mDrawingObjects = value; }
}
}
Then, your View would bind to this collection and draw them appropriately (I'll leave that as an exercise for the implementer).
One extension that I didn't show is that the DrawingObject may need to implement the appropriate serialization functionality.
Obviously, this is a rough sketch of the design, and may have a couple of errors (I did it from my head), but hopefully it's enough to go on.

Categories