Is it MVVM friendly to use other ViewModels as bindable properties? - c#

First a little backstory: I am studying Xamarin for a month now, and I am about to start my first project.
I have a need where I have like 4 nested "generations" of a relational database, that I have to include in one View.
When I start to nest stuff, I am forced to move some Commands (ViewModel code) into the Model.
I want to avoid this at all cost, thus the question arise:
Is it MVVM friendly to use other ViewModels as bindable properties, like in the following Example?
ViewModels:
public class MainViewModel : FreshBasePageModel
{
public ObservableCollection<OtherViewModel> OtherCollection { get; set; }
}
public class OtherViewModel: FreshBasePageModel
{
public Command SomeCommand { get; set; }
}
And use it like this in the Views:
<ContentPage>
<ListView ItemsSource={Binding OtherCollection} SelectedItem={Binding SomeCommand}>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
This approach seems Okay to me, but this is my first ever MVVM project, and I wonder if this is how you do stuff.
I use FreshMvvm as the backing framework, and it uses conventions for bindings, so the view is automatically bound to its namesake partner.
Also, if you'd like to look at my nested lists, see below:
Thanks for your time,

By default this behaviour isn't supported because a control only has a DataContext o BindableContex and this class is where the compiler look for your binding properties and commands.
In MVVM, you should only use a ViewModel for each View so your approach isn't frequent.
However I think that you could use the code that you proposed if you use the Path binding property and you have other control which its BindableContex is OtherViewModel

Related

What is proper way of data binding / setting source to my DataGrid [WPF]

I'm begginer at wpf so please be patient with me :)
I have stored 40.000 articles in MySql database, and when I click on a button I'm opening a window that loads that articles, and I did it on this way:
/// <summary>
/// Interaction logic
/// </summary>
public partial class ArticlesAdd : Window
{
public ObservableCollection<MyArticles> articlesList = ObservableCollection<MyArticles>(ArticlesController.SelectAll());
public ArticlesAdd()
{
this.InitializeComponent();
// Setting source to my DATAGRID when this window is loaded/opened
dataGridMyArticles.ItemsSource = articlesList;
}
}
But I saw some examples are setting ItemsSource directly on DataGrid Control like this (IN XAML PART):
<DataGrid Name="dataGridMyArticles" ItemsSource="{Binding Source=articlesList}" AutoGenerateColumns="False">
But I don't know how this works and how this should be implemented because I'm using dataGridMyArticles.ItemsSource = articlesList;
Is that ItemsSource="{Binding Source=articlesList}" on a XAML side faster than my code behind binding ?
and would it IsAsync=True make data binding faster/opens window faster or smth like that?
So how can I bind that list to my DataGrid without using code behind, and is that approach faster than setting DataGrid's source there in my Class constructor.. ?
Thanks guys
Cheers
Binding an element in a view to a source property of a view model is common practice when you follow the Model-View-ViewModel (MVVM) design pattern. MVVM has nothing to do with performance but it is the recommended design pattern to use when developing XAML based UI applications.
It won't make your application faster, but if you do it right it will make the application easier to maintain, test and develop. You can read more about the motivations for implementing an application using MVVM pattern on MSDN: https://msdn.microsoft.com/en-us/library/hh848246.aspx. There are a lot more resources available online if your Google or Bing for it.
In your particular example, you would define a view model class which holds the list of articles:
public class ArticlesViewModel
{
public ObservableCollection<MyArticles> ArticlesList { get; private set; }
public ArticlesViewModel()
{
ArticlesList = ObservableCollection<MyArticles>(ArticlesController.SelectAll());
}
}
Set the DataContext of the view to an instance of this class:
public partial class ArticlesAdd : Window
{
public ArticlesAdd()
{
this.InitializeComponent();
DataContext = new ArticlesViewModel();
}
}
You can then bind to any public property of the DataContext/view model:
<DataGrid Name="dataGridMyArticles" ItemsSource="{Binding Source=ArticlesList}" AutoGenerateColumns="False">
You probably also want to call the ArticlesController.SelectAll() method on a background in order to prevent the UI from freezing during the time it takes to collect the data from the database, but that's another story that is not directly related to MVVM and the use of bindings.

MVVM and WPF structure

I've been recently trying to re-write a WinForm application to WPF. I've been trying to implement an MVVM structure into my application because it's starting to look a lot like Winforms where I need to name my controls x:Name and referencing them all the time. Essentially, I'm not using the power of what MVVM provides.
One thing I'm having trouble wrapping my head around, is the Window. Every time I create a Window, it generates a partial class. My question is, how does that tie in to MVVM? The confusion starts as to what content this class should contain. Does it have a single DataContext binding? What about button events?
From my understanding (at the moment), is that this "partial" class should have very minimal code, perhaps only to bind your ViewModel in your constructor:
this.DataContext = new ViewModel();
and the rest of the functionality should come from your ViewModel with the help of binding things on XAML. However, each ViewModel should be tied to a single Model. But what happens when this particular Window calls for many Models, such as a Client, Products, etc? Do you make a single ViewModel class that somehow does everything?
The essence of my question lies within the contents of this particual "partial" class and its relation to the ViewModel.
Looks like you've got two questions:
How the heck does this partial class thing fit in with MVVM?
How do I structure all the view models and models and stuff?
1) What you are referring to as a "partial class" is often called "code-behind" when discussing WPF. This is because in that in non MVVM patterns it usually has all the actual C# code that sits behind the xaml layout - i.e. "code-behind".
You're correct that a good indication of a good MVVM implementation is minimal/no code-behind. As you mentioned, usually all it will have is binding the DataContext to the ViewModel - and in lots of frameworks this is all handled for you and you don't even need that. In my MVVM projects every partial class looks like this:
namespace MyApp.Views
{
public partial class GeneratorView : CreatableView
{
public GeneratorView()
{
InitializeComponent();
}
}
}
Part of the nice thing with MVVM is that all the spaghetti event updating/connection code that you had to have in WinForms is all bypassed by using bindings. But as you noted, you can still do this with WPF but it's generally considered bad practice, and definitely with MVVM.
With all content and commands being bound directly to the ViewModel through data binding, the partial class need not have any content at all. There's lots of interpretations, but on a basic level:
The View lays things out and shows things on the screen.
The View Model contains UI specific data and logic. It deals with commands from the view, and may utilize business services.
The Model represents your data. Think of the stuff that's going in a database or file system.
2) MVVM is not strict in how you structure it. Generally the convention is you have ViewModel for every View. However, I believe it's fine to have a ViewModel without a View. Especially simple ones.
But what happens when this particular Window calls for many Models, such as a Client, Products, etc? Do you make a single ViewModel class that somehow does everything?
Not at all, it seems like you've got this all-or-nothing monolithic ViewModel in your head. If these were really simple structures, I'd do it like this:
However, you might want a dedicated ClientView or ProductView, and instead embed them in the main view:
The key thing is your ViewModels may contains other ViewModels, arrays of ViewModels. Similarly, your View can embed other View's to display it's ViewModels - or not. If they're simple, or you're just, say, listing a few properties (Maybe when you click an 'info' button a dialog shows up and that has the full View for said ViewModel, but in the list you just want the Name and Cost.
It's flexible. On thing is, often the Window isn't even part of the MVVM pattern. It's so "dumb" that it doesn't even have a ViewModel (also, what if you want to embed your app into another app or something?).
It's more flexible to have a high-level "AppViewModel" and all your Window does is contain that (often not even bothering with a WindowViewModel, it's not really a concern).
You can use more than one model in a view model. The aim of the view model is to abstract from the business layer (models, services).
For creating and keeping the instances you may use IoC (Inversion of Control) containers. There are many IoC containers available to use in .NET applications such as Castle Windsor, Autofac and so on (see List of .NET Dependency Injection Containers (IOC)). You just need to instantiate a view model object by necessary model objects, for example like this:
public class ViewModel
{
private readonly IClientModel _clientModel;
private readonly IProductModel _productModel;
public ViewModel(IClientModel clientModel, IProductModel productModel)
{
_clientModel = clientModel;
_productModel = productModel;
}
// Logic of your view model
}
Also you need to configure object dependencies and scopes (your model will be a singleton or a new instance of the type). The container injects dependencies when it creates the objects.
Also I recommend to read the article MVVM - IOC Containers and MVVM.
Yes, the way I approach it anyway, is to keep my Window dumb. How dumb? I guess it depends on the application. If I am just trying to throw a proof of concept or something with low importance together then I will cut a few corners. If I am working an a large application that will need to be maintained then I am going to be more strict, I may even frown on setting the ViewModel from the constructor in that case.
However, each ViewModel should be tied to a single Model
I don't know that I agree with that. That is not how I have approached MVVM, anyway. I would say that every View should be tied to a ViewModel. Within the ViewModel it may be the case that you are only dealing with one model but I have also had great success using a single ViewModel to expose multiple models to a View in a coherent way as well.
Here is an example of a template I use as a jumping off point in some of my smaller projects. I like to use explicit ViewModel properties in my Window and Views but you don't have to; you could modify this to use the DataContext property instead.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public object ViewModel
{
get { return (object)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
"ViewModel",
typeof(object),
typeof(MainWindow));
}
In the code behind the ViewModel propoerty is just a dependency property of the Window. I will bind the Window content to this property. In this case it is an object but it could be some base ViewModel class or an interface if you want.
In my Window's markup I add a DataTemplate for each ViewModel to the Window's resources. If everything is wired together correctly WPF's implicit data templating will take over and make sure the correct view is rendered whenever the ViewModel property is changed.
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:Example.ViewModels;assembly=Example"
xmlns:views="clr-namespace:Example.Views;assembly=Example">
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModels:FirstViewModel}">
<views:FirstView ViewModel="{Binding }" />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:SecondViewModel}">
<views:SecondView ViewModel="{Binding }" />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{Binding ViewModel, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
</Grid>
</Window>

Binding properties of multiple tree views to the same ViewModel

I am working on a MVVM implementation, where i'll spawn multiple views (side by side) each containing a tree control.
each of the views will have a similar tree, with a copy of [almost] all the same items.
I would like to synchronize the IsExpanded property on all the view/TreeView's..
meaning, if i collapse one node, i would like all of them to collapse (and some goes for column widths etc).
One way to do this, would be to bind all views to the same viewmodel, and have a DependencyProperty on that ViewModel, and set up the binding as Two Way on each view. However, i need each view to be bound to a separate viewmodel so that it can display unique values. I just need to synchronize a few properties of the tree, such as IsExpanded and Width.
What would be the best approach here?
You can use Prism and EventAggregator service from it to exchange data between view models.
There's no reason you can't have different collections within a single ViewModel, if that is the best design option. Especially if your multiple Trees / Collections are filtered from some 'complete set'; it might actually make more sense.
Just add multiple collections to your ViewModel, and bind to them.
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<MyItem> FirstTreeCollection
{
get
{
// whatever you need to do here
}
}
public ObservableCollection<MyItem> SecondTreeCollection
{
get { /* etc */ }
set { /* etc */ }
}
// etc
public bool Collapsed
{
get;
set;
}
}
and your Views should bind accordingly
// in your first view that contains a tree
<UserControl x:Class="View1" ...>
<TreeView Name="FirstTree"
ItemsSource={Binding FirstTreeCollection}
Collapsed={Binding Collapsed} ... >
// & in your second view that contains a tree
<UserControl x:Class="View2" ...>
<TreeView Name="SecondTree"
ItemsSource={Binding SecondTreeCollection}
Collapsed={Binding Collapsed} ... >
To clarify, I'm suggesting that you use a single ViewModel for all of these Tree-containing Views.
The ViewModel won't need a DependencyPropery--it will just need to expose a property that implements INotifyPropertyChanged.
The two ViewModels will need to have some way of sharing state, and exposing a property that represents that state. How you share the state depends heavily on how your ViewModels are instantiated (and probably other factors). For example, if your two VMs are being instantiated by some parent object, the parent may create one instance and pass it to both VMs in their constructors.
If you display the treeview's using xaml, you can bind every treeview to the first treeview spawned.
For example you can use some binding like this:
<TreeView Name="FirstTreeView" />
<TreeView Name="SecondTree"
IsExpended = {Binding Path=IsExpanded, ElementName=FirstTreeView, Mode=TwoWay}/>

UI design using MVVM pattern

I'm trying to choose the best way to implement this UI in MVVM manner. I'm new to WPF (like 2 month's) but I have huge WinForms experience.
The ListBox here act's like a TabControl (so it switches the view to the right), and contains basically the Type of item's displayed in tables. All UI is dynamic (ListBox items, TabItems and Columns are determined during run-time). The application is targeting WPF and Silverlight.
Classes we need for ViewModel:
public abstract class ViewModel : INotifyPropertyChanged {}
public abstract class ContainerViewModel : ViewModel
{
public IList<ViewModel> Workspaces {get;set;}
public ViewModel ActiveWorkspace {get;set;}
}
public class ListViewModel<TItem> where TItem : class
{
public IList<TItem> ItemList { get; set; }
public TItem ActiveItem { get; set; }
public IList<TItem> SelectedItems { get; set; }
}
public class TableViewModel<TItem> : ListViewModel<TItem> where TItem : class
{
public Ilist<ColumnDescription> ColumnList { get; set; }
}
Now the question is how to wire this to View.
There are 2 base approaches I can see here:
With XAML: due to lack of Generics support in XAML, I will lose strong typing.
Without XAML: I can reuse same ListView<T> : UserControl.
Next, how to wire data, I see 3 methods here (with XAML or without doesn't matter here). As there is no simple DataBinding to DataGrid's Columns or TabControl's TabItems the methods I see, are:
Use DataBinding with IValueConverter: I think this will not work with WPF|Silverlight out of the box control's, as some properties I need are read-only or unbindable in duplex way. (I'm not sure about this, but I feel like it will not work).
Use manual logic by subscribing to INotifyPropertyChanged in View: ViewModel.PropertyChanged+= ....ViewModel.ColumnList.CollectionChanged+= ....
Use custom controll's that support this binding: Code by myself or find 3d party controls that support this binding's (I don't like this option, my WPF skill is too low to code this myself, and I doubt I will find free controls)
Update: 28.02.2011
Things get worser and worser, I decided to use TreeView instead of ListBox, and it was a nightmare. As you probably guess TreeView.SelectedItems is a readonly property so no data binding for it. Ummm all right, let's do it the old way and subscribe to event's to sync view with viewmodel. At this point a suddenly discovered that DisplayMemberPath does nothing for TreeView (ummmm all right let's make it old way ToString()). Then in View's method I try to sync ViewModel.SelectedItem with TreeView's:
private void UpdateTreeViewSelectedItem()
{
//uiCategorySelector.SelectedItem = ReadOnly....
//((TreeViewItem) uiCategorySelector.Items[uiCategorySelector.Items.IndexOf(Model.ActiveCategory)]).IsSelected = true;
// Will not work Items's are not TreeViewItem but Category object......
//((TreeViewItem) uiCategorySelector.ItemContainerGenerator.ContainerFromItem(Model.ActiveCategory)).IsSelected = true;
//Doesn't work too.... NULL // Changind DataContext=Model and Model = new MainViewModel line order doesn't matter.
//Allright.. figure this out later...
}
And none of methods I was able to think of worked....
And here is the link to my sample project demonstrating Control Library Hell with MVVM: http://cid-b73623db14413608.office.live.com/self.aspx/.Public/MVVMDemo.zip
Maciek's answer is actually even more complicated than it needs to be. You don't need template selectors at all. To create a heterogeneous tab control:
Create a view model class for each type of view that you want to appear as tab items. Make sure each class implements a Text property that contains the text that you want to appear in the tab for its item.
Create a DataTemplate for each view model class, with DataType set to the class's type, and put the template in the resource dictionary.
Populate a collection with instances of your view models.
Create a TabControl and bind its ItemsSource to this collection, and add an ItemTemplate that displays the Text property for each item.
Here's an example that doesn't use view models (and that doesn't implement a Text property either, because the objects I'm binding to are simple CLR types), but shows how template selection works in this context:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:coll="clr-namespace:System.Collections;assembly=mscorlib">
<DockPanel>
<DockPanel.Resources>
<coll:ArrayList x:Key="Data">
<sys:String>This is a string.</sys:String>
<sys:Int32>12345</sys:Int32>
<sys:Decimal>23456.78</sys:Decimal>
</coll:ArrayList>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Int32}">
<StackPanel Orientation="Horizontal">
<TextBlock>This is an Int32:</TextBlock>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Decimal}">
<StackPanel Orientation="Horizontal">
<TextBlock>This is a Decimal: </TextBlock>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</DockPanel.Resources>
<TabControl ItemsSource="{StaticResource Data}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DockPanel>
</Page>
Of course in a real MVVM application those DataTemplates would use UserControls to map each type to its view:
<DataTemplate DataType="{x:Type my:ViewModel}">
<my:View DataContext="{Binding}"/>
</DataTemplate>
Maciek and Robert already gave you some ideas on how to implement this.
For the specifics of binding the columns of the DataGrid I strongly recommend Meleak's answer to that question.
Similar to that you can use attached properties (or Behaviors) and still maintain a clean ViewModel in MVVM.
I know the learning curve for WPF is quite steep and you're struggling already. I also know that the following suggestion doesn't help that and even makes that curve steeper. But your scenario is complex enough that I'd recommend to use PRISM.
I wrote an article and a sample application with source code available, where I discuss and show the problems I have mentioned here and how to solve them.
http://alexburtsev.wordpress.com/2011/03/05/mvvm-pattern-in-silverlight-and-wpf/
In order to connect your ViewModel to your View you need to assign the View's DataContext. This is normally done in the View's Constructor.
public View()
{
DataContext = new ViewModel();
}
If you'd like to see your view model's effect at design time, you need to declare it in XAML, in the View's resources, assign a key to it, and then set the target's DataContext via a StaticResource.
<UserControl
xmlns:vm="clr-namespace:MyViewModels
>
<UserControl.Resources>
<vm:MyViewModel x:Key="MyVM"/>
</UserControl.Resources>
<MyControl DataContext={StaticResource MyVM}/>
</UserControl>
(The above is to demonstrate the design-time trick works)
Since you're dealing with a scenario that includes a container such as the TabControl I'd advocate considering the following things :
Hold your child ViewModels in a Property of type ObservableCollection
Bind the TabControls ItemsSource to that property
Create a new View that derives from TabItem
Use a template selector to automatically pick the type of the view based on the type of the view model.
Add IDisposable to yoour child ViewModels and create functionality to close the views.
Hope that helps a bit, if you have further questions let me know.

M-V-VM, isn't the Model leaking into the View?

The point of M-V-VM as we all know is about speraration of concerns. In patterns like MVVM, MVC or MVP, the main purpose is to decouple the View from the Data thereby building more flexible components. I'll demonstrate first a very common scenario found in many WPF apps, and then I'll make my point:
Say we have some StockQuote application that streams a bunch of quotes and displays them on screen. Typically, you'd have this:
StockQuote.cs : (Model)
public class StockQuote
{
public string Symbol { get; set; }
public double Price { get; set; }
}
StockQuoteViewModel.cs : (ViewModel)
public class StockQuoteViewModel
{
private ObservableCollection<StockQuote> _quotes = new ObservableCollection<StockQuote>();
public ObservableCollection<StockQuote> Quotes
{
get
{
return _quotes;
}
}
}
StockQuoteView.xaml (View)
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:StockQuoteViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="listBoxDateTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Symbol}"/>
<TextBlock Text="{Binding Price}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemTemplate="{StaticResource listBoxDateTemplate}" ItemsSource="{Binding Quotes}"/>
</Grid>
</Window>
And then you'd have some kind of service that would feed the ObservableCollection with new StockQuotes.
My question is this: In this type of scenario, the StockQuote is considered the Model, and we're exposing that to the View through the ViewModel's ObservableCollection. Which basically means, our View has knowledge of the Model. Doesn't that violate the whole paradigm of M-V-VM? Or am I missing something here....?
I'm more familiar with MVC than MVVM, but it's generally accepted that the View will have knowledge of the Model. As long as the Model has no knowledge of the View this is okay.
If this really is a concern for whatever reason, check out the "Passive View" design, where the View knows nothing more than the raw data fed to it.
In MVVM the view model is something in between the view and the model which exposes data from the model in a way that can be handled easily by the view. In a strict MVVM application the view does not know about the model, only about the view model.
In your concrete example the view model should not be called StockQuoteViewModel but StockQuotesViewModel (be aware of the plural) because the view model is exposing many stock quotes by a specific ui collection which is easy to handle by the view (because ObservableCollection<T> implements INotifyCollectionChanged<T>). The type of items in the collection should be a view model (e.g. StockQuoteViewModel) which exposes data from a single StockQuote object. In such a view model you can add logic like adding a $-symbol to Price and so on.
It is often easier to expose some model objects in a view model, but the correct way would be to create a view model for each model class.
Best Regards,
Oliver Hanappi
No. You are not exposing StockQuote. You are only specifying an (loosely typed) interface in the view. The view knows only two properties: Symbol and Price. You can easily replace StockQuote with anything else as long as it implements those.
Check video: Jason Dolinger on MVVM. It will answer your question.
Also, see SO question wpf mvvm confusion for additional resources.
My understanding is that ViewModels are to Models as Properties are to Fields. This is a very loose analogy, but it does imply you're not properly insulated if your View is directly accessing your Model. Just as with trivial properties in a class wrapping private fields, you end up with a lot of duplication and boilerplate code when wrapping relevant Model properties in ViewModel properties for consumption by the View. This is something that bothers me with this pattern and I'm still not decided on whether the benefits are worth the bloat.
In this particular example, I think it would be overkill to create a VM for each StockQuote instance, as you're likely not doing any significant logic for the View that represents an individual StockQuote. I think it's much cleaner and more maintainable in these small cases to simply bind to the Model class directly. Creating a VM for the small case would reduce coupling, but it would also increase complexity and I think it's a case-by-case judgment call as to whether this is beneficial.
Maybe I have this wrong but isn't the idea of the viewmodel to encapsulate the model completely. For instance you have stock quotes exposed to the view but they should be mapped to properties native to the viewmodel which would then be binded to. This is to necessitate "cleaning" that might be needed during the transfer of data to the model/view.
That way the view only ever knows the viewmodel. It also means that if the model was not legacy it could be implemented as an interface and further reduce the coupling between the viewmodel.

Categories

Resources