Hi, this is about seperation of concerns and is about binding a listview item to a seperate viewmodel instead of the model itself. I was looking for a way to clean up my models that implemented a few properties only for presentation purpuses. Then I stumbled upon this post: "burkharts dot net slash apps slash blog slash advanced-listview-bindings" <- I am not yet allowed to post the source...
But maybe because I'm fairly new to Xamarin.Forms and startet learning with FreshMvvm I do not understand one or two points that are going on right here.
I hope it is ok if I cite the code passages.
So, instead of this common scenario where the model (dog listview item) is directly tied to the view
// viewmodel [ImplementPropertyChanged] //I use Fody here to not have to bother with INotifyPropertyChanged public class DemoListViewPageModel { public ObservableCollection<Dog> DogList public DemoListViewPageModel() { DogList = new ObservableCollection<Dog>(); DogList.Add(new Dog() { Name = "Rex", Race = "German Sheppard" }); DogList.Add(new Dog() { Name = "Barney", Race = "Poodle" }); DogList.Add(new Dog() { Name = "Jimmy", Race = "Beagle" }); DogList.Add(new Dog() { Name = "Rob", Race = "Labrador" }); } } // model // Should be something that makes a bit of sense, so why not dogs [ImplementPropertyChanged] public class Dogs { public string Name {get; set} public string Race {get; set;} } // view <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="..." xmlns:x="..." x:Class="Examples.DemoListViewPage"> <ContentPage.Content> <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center"> <ListView ItemsSource="{Binding DogList}" HasUnevenRows="True"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout HorizontalOptions="Start" > <Label x:Name="Name" Text="{Binding Name}" TextColor="Navy" FontSize="20"/> <Label x:Name="Race" Text="{Binding Race}"/> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage.Content> </ContentPage>
he recommends to introduce another viewmodel for the listview items like this:
// dog item viewmodel public class DogsItemViewModel { private Dog DogObject; ICommand AddToCartCommand; public DogsItemViewModel(Dog theDog) { DogObject = theDog; AddToCartCommand = new Xamarin.Forms.Command(AddToCart); } public string Name => DogObject.Name; public string Race => DogObject.Race.ToUpper(); public bool IsChecked { get; set; } void AddToCart() { WebService.Instance.AddToCart(DogObject); // Whatever this will do ;-) } } // dog item view <?xml version="1.0" encoding="UTF-8"?> <ViewCell xmlns="..." xmlns:x="..." x:Class="YourNameSpace.DogItemView"> <StackLayout HorizontalOptions="Start" > <CheckBox State = "{Binding IsChecked}" /> <Label x:Name="Name" Text="{Binding Name}" TextColor="Navy" FontSize="20"/> <Label x:Name="Race" Text="{Binding Race}"/> <Button Text="Add to Cart" Command="{Binding AddToCartCommand}"/> </StackLayout> </ViewCell> // listview pages view <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="..." xmlns:x="..." x:Class="Examples.DemoListViewPage"> <ContentPage.Content> <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center"> <ListView ItemsSource="{Binding DogList}" HasUnevenRows="True"> <ListView.ItemTemplate> <DataTemplate> <DogItemView BindingContext = "{Binding}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage.Content> </ContentPage>
First of all: Does this line mean <DogItemView BindingContext = "{Binding}" />
that the path of the binding context is set to Path=. which means it set to DogsItemViewModel()?
If so, how is the constructor of DogsItemViewModel called with the listview item from DogList?
Is there any other approach you could recommend to stop pulluting the model with view-bound properties?
@stev_e said:
"the old school way":<ListView ItemsSource="{Binding DogList}" // a listview with an item source that is an observable collection of the model
He states that:
- It violates MVVM in exposing the model behind it because the DataTemplate binds to model objects
- The moment we want to store additional data for each item or execute a command from an ViewCell it get’s difficult
Without reading the entire article I can't be sure what is really being said but...
That is not an "old school way" of anything. That's how we do things. And it does not violate MVVM concepts at all.
This beeing said he brings in the viewmodel in the lower part. If my explaination does not make sense to you: Would you mind to read the article? It can be found here (burkharts.net/apps/blog/advanced-listview-bindings/). Can you then tell me if this code still does not make any sense to you? I think this is such an interesting point to discuss
from the article:
The moment we want to store additional data for each item or execute a command from an ViewCell it get’s difficult
No it doesn't. I do it all the time. Article 2018-105 of my tutorial shows doing just that. You just have to have the right syntax to use a command on the UI code behind, the model or the VM. Not difficult at all.
Both both enhancements need additional code in the ViewModel to figure out which item the Command should be exceuted on which should not be the job of the ViewModel
There's nothing to figure out. Is the sender
argument. Or the CommandParameter
argument. Depending on if you are using the .Click
event or a Command
.
Introducing ViewModels for each ListView item
Oh hell no. There is no way I would do such a thing in a real-world enterprise-grade app. Every ListView
gets its own ViewModel? Not on my life. Just keeping all those VM's in sync with the actual collection of data (dogs) is an accident waiting to happen let alone a confusion factor I wouldn't wish on my worst enemy. He's saying if there were 10 views and they each had a ListVIew of dogs then they would all have their own VM duplicating all the dog data? No way.
I have to get back to work but.... I think you get my perspective. But the beauty of coding is that 10 developers will have 10 different styles and preferences and in most cases they are all right. There is no a single way to do anything. There are always 20 right but different ways to accomplish anything in software. I'm just saying that I would never do what that author is proposing. Maybe the next guy loves that pattern. But I've never worked anywhere that would find it acceptable. Just one man's experience.
Answers
I'm not sure if my approach is correct (haven't looked into it in details yet for a better approach) but at least it works :-)
Instead of having a ObservableCollection I have a ObservableCollection
Hi Lars, I think you made a typo
What exactly do you use and what not? Can you explain a bit more?
argh think the system removed < and > and in between.
Instead of
ObservableCollection<Dogs>
I use
ObservableCollection<DogsItemViewModel>
Hi Lars, thank you for your suggestion
But I would prefer to get the ObservableCollection of Dogs way working.
That's not really how it should go. Your
ObservableCollection
should be of a model datatype, not a VIewModel. 90% of the time you don't have collections of ViewModels. You can. And I do in like... 2 cases... where the ViewModel is the features the user has permissions to use. But otherwise aListView
would bind to a collection of things... not a collection of ViewModels.Since you are using pets as your example, feel free to look at my 2017 tutorial app which also uses pets. I think it will be a smooth translation of concept for you since we're dealing with the same type objects. I find everyone grasps the concept of animals and pets so they are good learning classes to use.
https://redpillxamarin.com/2016/12/14/red-pill-xamarin-introduction-to-this-series/
My 2018 series leads up to (so far) an example of binding controls WITHIN a
ListView
so you can direct commands to the model, UI code behind or the ViewModel - the 3 common things everyone asks about from aListView
. Without having to go that odd route you've described of using ViewModels as the collection type.http://redpillxamarin.com/2018/03/12/2018-101-vs2017-new-solution/
Hi @ClintStLaurent as mentioned I know it's not the right/best solution - but it works and I just haven't got the time find the better solution.. yet.
I look forward to have a look at your examples!
Hi Clint, I just want to make sure everybody knows that my initial question had a bit different direction and that it is not answered yet (at least I think from what I'm reading). After re-visiting your blog I'm not quite sure if the solution for my problem is also in there (e.g. shall I use a composite control to avoid binding to a model directly?).
Maybe rephrase the question then, if it wasn't addressed. What question can we help you with?
Hi Clint, thank you for helping!! So:
At first I would like to get an understanding how the referenced code (it is not mine) is supposed to work (because I did not manage to get it working). Especially the line
<DogItemView BindingContext = "{Binding}" /
>confuses me. My questions were: Does this line mean that the path of the binding context is set to Path=. which means it set to DogsItemViewModel()? If so, how is the constructor of DogsItemViewModel called with the listview item from DogList?
The idea behind all that is to seperate the view from the model (that resides in a ObservableCollection and is so directly connected to the view, right?) by putting a view model between them. It does not intend to replace the model by a viewmodel inside the ObservableCollection. In my eyes this seems to be quite good MVVM coding practice that I want to adopt as a newcomer
The full explaination (that is maybe better understandable than mine) can be found in the referenced link I had to "encode", because I'm not allowed to post links.
Further, if this approach does not work how could one seperate the model (like a dog) from the view (listview item with direct access to models properties). And here I am not sure if you tried to answer this question by pointing to your blog.
I hope I was able to give an understandable explaination of my problem
No. That is in the DateTemplate. Which is applied to each element of the binded list. Each Dog in the collection of Dogs; which should be a model, not a viewmodel. A dog. Not a DogViewModel.
So the
DogItemView
binding context is set to the Dog model. Which makes sense. The view for a given dog uses that dog out of the collection for its binding context. The View to present "Zeus" uses the instance of "Zeus" as its binding context.honestly, I'm not sure that's accurate to what you've actually done. Because as you said you've done something odd that works even if you don't understand why. So if your collection is ViewModels instead of models then I'm as confused about what you do have versus what you should have as you might be.
That's exactly what the 2017 tutorial project I linked you to does. It presents a collection of pets using a pet view. Please don't be offended if I'm not going to spend hours rehashing what has already been done as a step-by-step walk through. That's why I did the walkthroughs with all the nice screenshots and very wordy detailed explanations.
Hi Clint, please excuse my persistence
And I am absolutely not offended by any serious try to help me. But I still believe that we are not talking about exactly the same. Let me explain again.
There is the upper part of the code that represents "the old school way":
All of the above code is not from me. I copied it from another website for better understanding of my concern. I did try to get it working in a project but couldn't. But I think the original author was able in his project
<ListView ItemsSource="{Binding DogList}" // a listview with an item source that is an observable collection of the model
He states that:
This beeing said he brings in the viewmodel in the lower part. If my explaination does not make sense to you: Would you mind to read the article? It can be found here (burkharts.net/apps/blog/advanced-listview-bindings/). Can you then tell me if this code still does not make any sense to you? I think this is such an interesting point to discuss
Hm ok. Now that I have read the article for the thousandth time, I notice that I have missed something that is not reflected in the code.
The improved version links to the same DogList as the former version of the code does:
<ListView ItemsSource="{Binding DogList}" HasUnevenRows="True"> // In the articles code snippet that is still a ObservableCollection<Dog> DogList
What he says in the text but does not reflect in the code is that
... our page’s ViewModel no longer exposes a ObservableColletion<Dogs> but ObservableColletion<DogsItemViewModel>
Now everything slowly fits together (including your explanation). I thought until now that the dog list remains a observable collection of dog in his code. Damn...
Thank you!!!
Without reading the entire article I can't be sure what is really being said but...
That is not an "old school way" of anything. That's how we do things. And it does not violate MVVM concepts at all.
from the article:
No it doesn't. I do it all the time. Article 2018-105 of my tutorial shows doing just that. You just have to have the right syntax to use a command on the UI code behind, the model or the VM. Not difficult at all.
There's nothing to figure out. Is the
sender
argument. Or theCommandParameter
argument. Depending on if you are using the.Click
event or aCommand
.Oh hell no. There is no way I would do such a thing in a real-world enterprise-grade app. Every
ListView
gets its own ViewModel? Not on my life. Just keeping all those VM's in sync with the actual collection of data (dogs) is an accident waiting to happen let alone a confusion factor I wouldn't wish on my worst enemy. He's saying if there were 10 views and they each had a ListVIew of dogs then they would all have their own VM duplicating all the dog data? No way.I have to get back to work but.... I think you get my perspective. But the beauty of coding is that 10 developers will have 10 different styles and preferences and in most cases they are all right. There is no a single way to do anything. There are always 20 right but different ways to accomplish anything in software. I'm just saying that I would never do what that author is proposing. Maybe the next guy loves that pattern. But I've never worked anywhere that would find it acceptable. Just one man's experience.
I will have a deeper look to all of that. It seems to be very promising. Thank you very much for all the detailled information. Have a nice day!
I ended up here because I found the same article as you @stev_e .
burkharts dot net slash apps slash blog slash advanced-listview-bindings
I also found it very promising. Using a Separated ViewModel for a ListView Item makes a lot of things easier. I'm trying this approach here. But I couldn't find anyone else on the web using this pattern. Since the last time you were trying it, what are your conclusions @stev_e ?