When implementing INotifyPropertyChanged, do navigation properties need to implement it too? - c#

When implementing INotifyPropertyChanged (using Prisim) the code below makes sense, you'd want to know when a property changes.
[DisplayName("Media Type Id"), Display(Name = "Media Type Id")]
public int MediaTypeId
{
get { return this._MediaTypeId; }
set { this.SetProperty(ref this._MediaTypeId, value); }
}
private int _MediaTypeId;
But im a but confused when it comes to navigation properties.
Do I implement it? To me this would make sense if i was to do something like artist.Album = new Album();
But what if only needed to change a property like artist.Album.name = "NEW_NAME" (assuming Album.name implements INotifyPropertyChanged)
Would the code below still be necessary?
[DisplayName("Album"), Display(Name = "Album")]
public Album Album
{
get { return this._Album; }
set { this.SetProperty(ref this._Album, value); }
}
private Album _Album;
Or will this work just as well
public virtual Album Album { get; set; }
Same for navigation collections.
[DisplayName("Playlists"), Display(Name = "Playlists")]
public ICollection<Playlist> Playlists
{
get { return this._Playlists; }
set { this.SetProperty(ref this._Playlists, value); }
}
private ICollection<Playlist> _Playlists
Or
public virtual ICollection<Playlist> Playlists { get; set; }

As you understand, you implement INotifyPropertyChanged (INPC) in order for the UI to update when a property on the model changes. So in your case, if you have something that is data binding to the Album property, it must implement INPC if there is a chance that it might change. Instead of using regular collection, you have a class called ObservableCollection that already implements INPC for you so you don't have to.

Related

How to set listview itemssource to a viewmodel in Xamarin?

I'm trying to make a listview in xamarin show data from a restapi but have the option to filter the list or sort it based upon last name.
I've set the bindingcontext equal to the apiviewmodel which works. But I want to set the itemssource to a list which can be manipulated later instead of the binding context.
Here is the code that works:
Xaml:
<ListView x:Name="DirectoryListView" ItemsSource="{Binding ContactsList}" IsPullToRefreshEnabled="True">
Xaml.cs:
LocalAPIViewModel = new APIViewModel();
BindingContext = LocalAPIViewModel;
APIViewModel.cs:
private List<MainContacts> _ContactsList { get; set; }
public List<MainContacts> ContactsList
{
get
{
return _ContactsList;
}
set
{
if(value != _ContactsList)
{
_ContactsList = value;
NotifyPropertyChanged();
}
}
}
public class MainContacts
{
public int ID { get; set; }
public string FirstName { get; set; }
}
This all works fine. It's only when I add the following lines that it stops displaying the data in the listview:
xaml.cs:
LocalList = LocalAPIViewModel.ContactsList;
DirectoryListView.ItemsSource = LocalList;
I think I need to add these lines so that I can manipulate the list that's being displayed. Why is the list not being displayed? Is this not how it should be done?
According to your description and code, you use MVVM to bind ListView firstly, it works fine, now you want to use Viewmodel to bind ListView itemsource in xaml.cs directly, am I right?
If yes,I do one sample according to your code, that you can take a look, the data can display successfully.
public partial class Page4 : ContentPage
{
public APIViewModel LocalAPIViewModel { get; set; }
public Page4 ()
{
InitializeComponent ();
LocalAPIViewModel = new APIViewModel();
listview1.ItemsSource = LocalAPIViewModel.ContactsList;
}
}
public class APIViewModel
{
public ObservableCollection<MainContacts> ContactsList { get; set; }
public APIViewModel()
{
loadddata();
}
public void loadddata()
{
ContactsList = new ObservableCollection<MainContacts>();
for(int i=0;i<20;i++)
{
MainContacts p = new MainContacts();
p.ID = i;
p.FirstName = "cherry"+i;
ContactsList.Add(p);
}
}
}
public class MainContacts
{
public int ID { get; set; }
public string FirstName { get; set; }
}
so I suggest you can check ContactsList if has data.
Update:
I want to be able to search the list with a search bar and also order it by first or last names. I also want to be able to click on one of the contacts and open up a separate page about that contact
I do one sample that can meet your requirement, you can take a look:
https://github.com/851265601/xf-listview
So, to answer all your questions...
First, the binding.
Once you set the ItemsSource="{Binding ContactsList}" this means that anytime you signal that you have changed your ContactsList by calling OnPropertyChanged(), that is going to be reflected on the ItemsSource property (so, update the UI - that is why we put the OnPropertyChanged() into the setter). Thus, you do not need to manually set the ItemsSource every time you change it. (Especially from the View, as the View should have no knowledge of how the ContactsList is defined in the ViewModel.)
So you can completely remove those lines from the View's code-behind.
Next, the ordering and searching.
What OnPropertyChanged() does, is that it re-requests the bound property from the ViewModel, and updates the View according to that. So, just after OnPropertyChanged() is called, the getter of the bound property (ContactsList) is called by the View.
So, a good idea is to put the sorting mechanism into the getter of the public property. (Or the setter, when resetting the property.) Something like this:
public class ViewModel {
private ObserveableCollection<MainContacts> contactList { get; set; }
public ObserveableCollection<MainContacts> ContactList {
get {
return new ObservableCollection<MainContacts>(contactList
.Where(yourFilteringFunc)
.OrderBy(yourOrderingFunc));
}
set {
contactsList = value;
OnPropertyChanged();
}
}
//...
}
So, whenever your public property is called, it will sort the private property and return the collection that way.
Change public List<MainContacts> ContactsList to public ObservableCollection<MainContacts> ContactsList
in xaml.cs
instead of LocalList = LocalAPIViewModel.ContactsList;, put
ContactsList = new ObservableCollection(LocalAPIViewModel.ContactsList);
I think this will work, instead of setting ListView's Itemsource to 'LocalList'

Binding to an object

I have a simple Person model:
public class Person
{
public string FirstName { get; set; }
public DateTime LastUpdated { get; set; }
}
Lets say that I have a View that has a TextBox, which is binded to LastUpdated field:
<TextBox Grid.Column="1" Margin="5" Text="{Binding Person.FirstName}"/>
Now I need to implement PropertyChanged somehow. Let's use Prism Snippet.
What I need is to perform SetProperty on a Person class:
private Person person;
public Person Person
{
get { return person; }
set { SetProperty(ref person, value); }
}
and NOT on the field LastUpdated in Person class:
private DateTime? lastUpdated;
public DateTime? LastUpdated
{
get { return lastUpdated; }
set { SetProperty(ref lastUpdated, value); }
}
This is not the matter of dependencies in the model. I got the model through DataContract ( WCF service ) so I cannot changed it. So is there a way to observe a class for changes and bind class field to some UI control.
So is there a way to observe a class for changes and bind class field to some UI control.
No. You need to raise the PropertyChanged event for the object of the property that you are actually binding to.
If you get the Person object from some third-party service for which you cannot modify the code to raise the PropertyChanged event in the setter of the FirstName property, you should not bind to these objects.
Instead you should create your own view model class and bind to this one. The view model can simply wrap the WCF class, e.g.:
public class PersonViewModel
{
private readonly Person _person;
public PersonViewModel(Person person)
{
_person = person;
}
public string FirstName
{
get { return _person.FirstName; }
set { _person.FirstName = value; RaisePropertyChanged(); }
}
}
If you're using Prism, then you likely are using the MVVM pattern. If so, then the one approach is using the view model for binding. Instead of exposing Person as a property, expose the individual properties you want to bind against - FirstName and LastUpdated:
Create a property on the view model that forwards calls to your model.
Bind your view to the view model property.
You can freely implement your change notifications in the view model.

MvvmCross; How to RaisePropertyChange from another ViewModel

I have a ShoppingCart listView with items that is bound to ShopingCartViewModel. When I click to an item it takes me to the ItemInfoFragment which is bound to ItemInfoViewModel.
In ItemInfoFragment I have a button which deletes the item and removes it from the ShoppingCart listview.
My problem is; After i delete the item and press backbutton to return to my previously activity, the ShoppingCart listView still shows the Item that I deleted.
My Question is; How to RaisePropertyChange in ShoppingCartViewModel when i exit the ItemInfoFragment?
I believe you have a few options:
Shared Persistent Storage
If you use a storage/caching solution like SQLite or Realm etc. Which can be used to read and modify the same shopping cart data between pages. You can then use view life cycle events (OnResume[Android] or ViewWillAppear[iOS]) to retrieve the latest from the cache.
Alternatively if the shopping cart data size is small you could read/write it to MvvmCross Settings Plugin. You will just have to serialize and deserialize your objects as you can only save basic types like strings, bools, int etc.
Dependency Injection Shared Instance
You can create an in memory cache via using a shared class instance the can be shared between multiple ViewModels. This classes properties can bind directly to your various views. Any changes to the list will update all views that bind to it. One thing to note is that you will have to manually handle clean up if you require the memory space occupied by the this instance class.
Example:
Example model
public class ItemInfo
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
Shared class instance and interface
public interface ISharedShoppingCart
{
MvxObservableCollection<ItemInfo> ShoppingCartItems { get; set; }
}
public class SharedShoppingCart : MvxNotifyPropertyChanged, ISharedShoppingCart
{
MvxObservableCollection<ItemInfo> _shoppingCartItems;
public MvxObservableCollection<ItemInfo> ShoppingCartItems
{
get { return _shoppingCartItems; }
set { SetProperty(ref _shoppingCartItems, value); }
}
}
Make sure to register the class and interface
public class App : MvxApplication
{
public override void Initialize()
{
/* Other registerations*/
Mvx.LazyConstructAndRegisterSingleton<ISharedShoppingCart, SharedShoppingCart>();
}
}
Example usage in shared ViewModels
public class ShopingCartViewModel : MvxViewModel
{
readonly ISharedShoppingCart _sharedShoppingChart;
public ShopingCartViewModel(ISharedShoppingCart sharedShoppingChart)
{
_sharedShoppingChart = sharedShoppingChart;
}
public MvxObservableCollection<ItemInfo> ShoppingCartItems
{
get { return _sharedShoppingChart.ShoppingCartItems; }
set { _sharedShoppingChart.ShoppingCartItems = value; }
}
}
public class ItemInfoViewModel : MvxViewModel
{
readonly ISharedShoppingCart _sharedShoppingCart;
public ItemInfoViewModel(ISharedShoppingCart sharedShoppingCart)
{
_sharedShoppingCart = sharedShoppingCart;
}
void RemoveItemFromCart(int id)
{
_sharedShoppingCart.ShoppingCartItems
.Remove(_sharedShoppingCart.ShoppingCartItems.Single(x => x.Id == id));
}
}
Pub/Sub
You could send messages back to the shopping cart ViewModel using the MvvmCross Messenger Plugin.

How to identify which properties of an object changed in a List<T>

I've a Class say One with following properties
class One
{
public int Id {get; set;}
public string Name {get; set;}
public string Salary {get; set;}
public string Designation {get; set;}
}
Now I created a list of type One like this in a ViewModel
public class OneViewModel
{
public OneViewModel(){
lstOne = new List<One>();
}
List<One> lstOne {get;set;}
public int UserId {get;set;}
}
and added few objects to the list in ActionResult and passed it in model.
public ActionResult Index()
{
OneViewModel model = new OneViewModel();
model.lstOne.Add(new One{Id=1,Name="Sam",Salary="5000", Designation="Manager" });
model.lstOne.Add(new One{Id=2,Name="Akash",Salary="6000", Designation="Manager" });
model.lstOne.Add(new One{Id=3,Name="Sid",Salary="7000", Designation="Manager" });
return View(model);
}
Now when the post method is called I want to find out which objects were deleted or added in lstOne and which properties were changed of a given object in lstOne.
[HttpPost]
public ActionResult Index(OneViewModel model)
{
//what to do here, any ideas
}
I don't want to use an old approach of iterating through list and comparing objects and properties, is there any other way around. I saw ObservableCollection and NotifyCollectionChangedEventArgs on MSDN site, however, I was not able to get the right approach for implementing it in order to resolve my query.
I'm not sure about this, i didn't implemented anywhere
use ObservableCollection instead of List
In your viewmodel you have something like this.
public class OneViewModel
{
public OneViewModel()
{
ObservableCollection<One> observableColleciton = new ObservableCollection<One>();
observableColleciton.CollectionChanged += list_CollectionChanged;
}
public bool IsCollecitonDirty { get; set; }
static void list_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
isCollectionDirty = true;
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
isCollectionDirty = true;
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
break;
default:
break;
}
}
}
At your model you can something similar to this.
class One : INotifyPropertyChanged
{
private int _id;
public int Id
{
get { return _id; }
set
{
_id = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
IsModelDirty = true;
// PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool IsModelDirty { get; set; }
}
Use IsCollectionDirty to ensure your collection is changed and to ensure model is good you could try
observableColleciton.Any(g=>g.IsModelDirty)
Ok, lets analize that.
Ideal situation
Get collection from Index()
Modify or not, and set modification indicators in collection
Put collction into Index(OneViewModel model)
In Index(OneViewModel model) you can analize indicators and do work. Everything works fine.
But word isn't ideal and im bad man and i want to harm your app. Or something goes wrong and client works bad.
Bad situation
Get collection from Index()
Modify or not, and set modification indicators in collection somethig goes wrong and indicators not set.
Put collction into Index(OneViewModel model)
In Index(OneViewModel model) you can analize indicators and you cannot detect changes becouse indicators not set.
Or someone cretes client which uses your collection and he don't know he must set indicators and he don't do that. Or ... many many other situations.
You can rely only on what is 100% sure. In this case as I say in comment, Between calls the collection does not belong to you. You can not control what was happening to it and how.
Only good way is comparision.

ViewModel in Model - legal implementation?

SHORT
When it comes to MVVM, is it legal to have a ViewModel in a Model class due to polymorphism?
DETAILED
Imagine you have the following construct of 2 classes:
public class ArticleModel
{
public string Name { get; set; }
}
public class TransactionModel
{
public ArticleModel ArticleModel { get; set; }
}
So basically every transaction has an associated article. Therefore, if you wrap this in a ViewModel, it may look as follows:
ArticleViewModel
public class ArticleModelView
{
private ArticleModel _ArticleModel;
public ArticleModel ArticleModel
{
// basic get and set + notify
}
public string Name
{
// basic get and set + notify
}
}
TransactionViewModel
public class TransactionViewModel
{
private TransactionModel _TransactionModel;
public TransactionModel TransactionModel
{
// basic get and set + notify
}
public ArticleModel ArticleModel
{
// basic get and set + notify
}
}
The reason I'm asking is, if you declare a TransactionViewModel with a TransactionModel, you won't get the updates of the underlying ArticleModel. For example:
TransactionViewModel transactionViewModel = new TransactionViewModel
(new TransactionModel(new ArticleModel("sometestname")));
Now, when changing the name of the underlying ArticleModel:
transactionViewModel.ArticleModel.Name = "hi";
nothing will be notified of the changes made since I did not assign a new ArticleModel but instead just changed the name attribute.
If the ArticleModel property in TransactionModel would be an ArticleViewModel, the changes would have been reported. I could even implement a ArticleViewModel in the TransactionViewModel but then there could be still the chance that due to wrong access the changes may not be reported.
Any thoughts on this?
So it looks like you are trying to change the articlemodels name property but also notifyproperty change, which the way you are doing it won't work unless your model implements the inotifypropertychange. What you could do is somehting like:
public class TransactionViewModel
{
public ArticleModel CurrentArticleModel { get; set; }
public String Name
{
get { return CurrentArticleModel.Name; }
set
{
CurrentArticleModel.Name = value;
NotifyPropertyChange("Name");
}
}
Also if necessary, I don't see anything wrong with your TransactionViewModel having an instance of a ArticleViewModel. I would assume that the ArticleViewModel would be bound to its own usercontrol or something though

Categories

Resources