简体   繁体   中英

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM