Best Practice for handling collection updates in .net-maui (MVVM) - mvvm

I have been working with MVVM and ObservableCollections for some time now, but it is still not clear to me what is the best practice for handling an update of a collection. When I add an item to the collection the UI gets notified and shows the new item in e.g. a ListView.
But I cannot see how this process works for the update of an item in the Collection. What I do now is to completely re-assign the collection and raise an OnPropertyChanged event but this updates the whole collection which seems like overkill and not really efficient.
Example use-case: The user edits an item and I want the change to be presented in the List or the Collection receives an update from a different service like a SignalR message.
I tried to assign new Values to an item of the ObservableCollection but it seems not to update the View even if I raise the OnPropertyChanged Event

From document From Data Bindings to MVVM,we could know that:
ViewModels generally implement the INotifyPropertyChanged interface,
which means that the class fires a PropertyChanged event whenever one
of its properties changes. The data binding mechanism in Xamarin.Forms
attaches a handler to this PropertyChanged event so it can be notified
when a property changes and keep the target updated with the new
value.
So if you want the UI updates automatically once changing the value of the property of the Item model in your List, you can implement interface INotifyPropertyChanged for your item model.
You can refer to the following code:
public class Item: INotifyPropertyChanged
{
private string _numType;
public string NumType
{
get => _numType;
set
{
SetProperty(ref _numType, value);
}
}
public string Name { get; set; }
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note:
1.Suppose you want to update UI automatically while modifying the value of NumType,you can add following code:
private string _numType;
public string NumType
{
get => _numType;
set
{
SetProperty(ref _numType, value);
}
}
2.If you want UI update automatically after adding or remove item from your datalist, you just define your list as follows:
public ObservableCollection<ItemModel> Items { get; set; }
For more information, check ObservableCollection Class.

Related

How to implement a State Pattern for Blazor pages using multiple components to build a page?

I have a Blazor page that utilizes multiple components within it - how can I implement a State pattern (ideally per-page) that would be able to handle the current state of a page?
Currently I have all of the state and state-manipulation being done on the page (and via injected Services), but I imagine it would be cleaner to implement a state pattern where each page has some kind of State object which then allows you to manipulate the page and its components in a strict manner.
Ideally the State object would implement INotifyPropertyChanged and be able to dynamically have its State updated, but I also don't hate the idea of having the State object relegate State-manipulation to methods on the object to make sure state isn't just 1-off updated on the Blazor page.
I've already tried to implement some kind of MVVM pattern, but that turned into more questions than answers.
I started to create a State object for the current page being worked on, but I'm not sure if I should basically just be putting most of the logic that was on the Blazor page in the State object, or if I should still have some data, but delegating the heavy lifting to the State.
eg: I have some code that used to be in the "OnAfterRenderAsync" function on the Blazor page, but I'm in the process of moving basically everything in there to a "LoadMatterDetails()" function in the State object that is handling that. Does this make sense, or should I only really have object State in the state object, and writing to & reading from the State object when particular pieces of information are available?
public class MatterDetailsState : IMatterDetailsState
{
private readonly IMatterDetailsService matterDetailsService;
private readonly NavigationManager navigationManager;
public bool EditMode { get; private set; } = false;
public int EditMatterId { get; private set; } = 0;
public Matter Matter { get; set; } = new();
public MatterPaymentOptionDetails PaymentDetails { get; set; } = new();
public List<MatterStatus> MatterStatuses { get; private set; } = new();
public MatterDetailsState(
IAppState appState,
IMatterDetailsService matterDetailsService,
NavigationManager navigationManager)
{
this.matterDetailsService = matterDetailsService;
this.navigationManager = navigationManager;
}
public async Task LoadMatterDetails()
{
// Query Params handling
var uri = navigationManager.ToAbsoluteUri(navigationManager.Uri);
var decryptedUri = HelperFunctions.Decrypt(uri.Query);
var queryParamFound = QueryHelpers.ParseQuery(decryptedUri).TryGetValue("MatterID", out StringValues uriMatterID);
if (queryParamFound)
{
EditMatterId = Convert.ToInt32(uriMatterID);
EditMode = !String.IsNullOrEmpty(uriMatterID) && EditMatterId > 0;
}
await LoadMatterStatuses();
if (EditMode)
{
Matter = await matterDetailsService.GetMatterByIdAsync(EditMatterId);
PaymentDetails = await matterDetailsService.GetMatterPaymentInfoByMatterId(EditMatterId);
}
}
private async Task LoadMatterStatuses()
{
MatterStatuses = await matterDetailsService.GetAvailableMatterStatusesAsync();
}
}
Basically, should I instead of having more or less the entire function in the State object, or only make the calls like setting Matter & PaymentDetails go through functions in the State object? Not sure what the standard for this is.
I've used Fluxor, which is a Flux/Redux library for Blazor, and have liked it. It holds all your state in an object which you can inject into your component for read access. You then manage state by dispatching actions from your components which are processed by effects or reducers which are essentially methods that process the action and make changes to state. It keeps everything neat, separated and very testable in my experience.
https://github.com/mrpmorris/Fluxor
There isn't a "standard", but applying good coding practices such as the "Single Responsivity Principle" and Clean Design principles drives you in a certain direction.
I divide the presentation and UI code into three:
UI - components and UI logic
State - data that you want to track state on.
Data Management - getting, saving,....
Each represented by one or more objects (Data Management is the ViewModel in MVVM).
You can see an example of this in this answer - https://stackoverflow.com/a/75157903/13065781
The problem is then how do you create a ViewModel instance that is scoped the same as the Form component. You either:
Scope the VM as transient - you can cascade it in the form if sub components need direct access to it. This is the approach in the referenced example.
Create an instance from the IServiceProvider using ActivatorUtilities and deal with the disposal in the form component.
If the VM implements IDisposable/IAsycDisposable the you have to do the second.
The following extension class adds two methods to the IServiceProvider that wrap up this functionality.
public static class ServiceUtilities
{
public static bool TryGetComponentService<TService>(this IServiceProvider serviceProvider,[NotNullWhen(true)] out TService? service) where TService : class
{
service = serviceProvider.GetComponentService<TService>();
return service != null;
}
public static TService? GetComponentService<TService>(this IServiceProvider serviceProvider) where TService : class
{
var serviceType = serviceProvider.GetService<TService>()?.GetType();
if (serviceType is null)
return ActivatorUtilities.CreateInstance<TService>(serviceProvider);
return ActivatorUtilities.CreateInstance(serviceProvider, serviceType) as TService;
}
}
Your form then can look something like this:
public partial class UIForm: UIWrapperBase, IAsyncDisposable
{
[Inject] protected IServiceProvider ServiceProvider { get; set; } = default!;
public MyEditorPresenter Presenter { get; set; } = default!;
private IDisposable? _disposable;
public override Task SetParametersAsync(ParameterView parameters)
{
// overries the base as we need to make sure we set up the Presenter Service before any rendering takes place
parameters.SetParameterProperties(this);
if (!initialized)
{
// Gets an instance of the Presenter from the Service Provider
this.Presenter = ServiceProvider.GetComponentService<MyEditorPresenter>() ?? default!;
if (this.Presenter is null)
throw new NullReferenceException($"No Presenter could be created.");
_disposable = this.Presenter as IDisposable;
}
return base.SetParametersAsync(ParameterView.Empty);
}
//....
public async ValueTask DisposeAsync()
{
_disposable?.Dispose();
if (this.Presenter is IAsyncDisposable asyncDisposable)
await asyncDisposable.DisposeAsync();
}
}

Is it ever appropriate to add an ICommand to your domain model objects?

I'm working on applying the MVVM pattern (and learning it in the process) for a Windows Store application.
Right now I am leaning towards having a 1:1 correspondence between View and ViewModel, where multiple ViewModels have a dependency on the same underlying Model.
For example, suppose I have an entity "Student". I have two ways to view the student: in a full-screen details page or as a list of students in a classroom. That results in the following View/ViewModel pairs:
StudentDetailsView/StudentDetailsViewModel
StudentListItemView/StudentListItemViewModel
At the moment I'm assuming my ViewModel will directly expose the Model, and my Xaml will bind to ViewModel.Model.property-name (I realize that's debatable).
Suppose I can perform some action on the Student from either View (e.g., "Graduate"). I want to have the Graduate behavior in my Model (to avoid an Anemic Domain Model), and I want to avoid duplicating behavior between ViewModels that depend on the same Model.
My intent is to have an ICommand (e.g., a RelayCommand) that I can bind a Graduate button to in the View. Here's my question:
Is there any reason not to make the ICommand a property of the Model class?
Basically that would mean something like the following (ignoring the need for a Repository):
public class Student {
public ICommand GraduateCommand { get { ... } }
void Graduate() { ... }
}
That way both StudentDetailsView and StudentListItemsView could have Xaml that binds to that command (where DataContext is StudentViewModel and Model is the public property):
<Button Command="{Binding Model.GraduateCommand}" />
Obviously I could just make Student::Graduate() public, create duplicate GraduateCommands on the two ViewModels, and have the execution delegate call Model.Graduate(). But what would be the disadvantage of exposing the behavior of the class via an ICommand rather than a method?
First of all, in many cases, it is perfectly fine to bind directly from the View to the Model, if you can implement INotifyPropertyChanged on the Model. It would still be MVVM. This prevents the ViewModel to be cluttered with a lot of "relay-directly-to-Model" code. You only include in the VM what can't be directly used by the View (need to wrap/denormalize/transform data, or Model properties don't implement INPC, or you need another validation layer...).
That said, Commands are a primary mean of communication between the View and the ViewModel.
There may be many receivers for the command (possibly on different ViewModels).
The Execute/CanExecute pattern often doesn't fit outside of the context of the VM.
Even if the real stuff is done in a method of the Model, Commands may have some logic other than just delegating to the model (validation, interaction with other VM properties/methods...).
When it comes to test your VMs, you can't stub the commands' behavior if they're outside of the VM.
For these reasons, Commands do not belong to the Model.
If you're concerned by code duplication across VMs, you can create a StudentViewModel from which both StudentDetailsViewModel and StudentListItemViewModel will inherit. StudentViewModel will define the Command and its common behavior.
If you use a model's property in your view, then you should stop calling that MVVM. You can move graduate command implementation into another class (let's say a Helper class) an share it between your ViewModels (during the initialisation).
GraduateCommand=new RelayCommand (GraduateHelper.Graduate, CanGraduate);
Wrong: put Graduate() into your entity.
EDIT
Extension methods for INotifyPropertyChanged
public static class NotifyExtension
{
public static void OnPropertyChanged(this INotifyPropertyChanged source, PropertyChangedEventHandler h, string propertyName)
{
PropertyChangedEventHandler handler = h;
if (handler != null) handler(source, new PropertyChangedEventArgs(propertyName));
}
public static bool SetProperty<T>(this INotifyPropertyChanged source,PropertyChangedEventHandler handler, ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
source.OnPropertyChanged(handler, propertyName);
return true;
}
}
And then :
public class Student:INotifyPropertyChanged
{
private string _name = "Name";
public string Name
{
get { return _name; }
set {
this.SetProperty<string>(PropertyChanged, ref _name, value, "Name"); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class MyViewModel :INotifyPropertyChanged
{
private Student _student=new Student();
public Student Student
{
get { return _student; }
set
{
this.SetProperty<Student>(PropertyChanged, ref _student, value, "Student");
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Finally Xaml:
<TextBlock Text="{Binding Path=Student.Name}"></TextBlock>

Getting JSON Serialization Entity Framework Self Reference Loop error even after ProxyCreation false when using explicit Include

JSON Serialization (ASP.Net Web API) fails because of self-referencing loop (it’s a common problem, Reason: an entity being requested lazy loads child entities and every child has a back reference to parent entity).
Work around I found, but doesn’t help me:
Use [JsonIgnore] for navigation properties to be ignored:
This solution works but doesn’t apply in my case. For Example: To get a Customer information along with his Orders, I would quickly add [JsonIgnore] to Customer property in Order class, but when I want to get an Order information along with the Customer details, since there’s [JsonIgnore] on Customer property, it won’t include Customer details.
Change JSON.Net Serializer Settings to Preserve References:
Can’t Preserve because I don’t need Circular referenced data.
Disable Proxy Creation at the Data Context and use explicit loading(this should ideally solve the problem):
Disabling proxy creation stops Lazy Loading and returns data without error, but when I explicitly Include child entities, I again the get the unexpected self-referencing loop error! The error is at the back-reference level to parent entity.
Any experiences along the same lines/suggestions?
I tried all the suggested solutions but didn't work. Ended up with Overriding the JSON.Net Serializer’s DefaultContractResolver to this:
public class FilterContractResolver : DefaultContractResolver
{
Dictionary<Type, List<string>> _propertiesToIgnore;
public FilterContractResolver(Dictionary<Type, List<string>> propertiesToIgnore)
{
_propertiesToIgnore = propertiesToIgnore;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
List<string> toIgnore;
property.Ignored |= ((_propertiesToIgnore.TryGetValue(member.DeclaringType, out toIgnore) || _propertiesToIgnore.TryGetValue(member.DeclaringType.BaseType, out toIgnore)) && toIgnore.Contains(property.PropertyName));
return property;
}
}
Then created a Static Class which returns a dictionary of Properties to be Ignored based on the Controller:
public static class CriteriaDefination
{
private static Dictionary<string, Dictionary<Type, List<string>>> ToIgnore = new Dictionary<string, Dictionary<Type, List<string>>>
{
{
"tblCustomer", new Dictionary<Type, List<string>>{
{
typeof(tblCustomer), new List<string>{
//include all
}
},
{
typeof(tblOrder), new List<string>{
"tblCustomer"//ignore back reference to tblCustomer
}
}
}
},
{
"tblOrder", new Dictionary<Type, List<string>>{
{
typeof(tblCustomer), new List<string>{
"tblOrders"//ignore back reference to tblOrders
}
},
{
typeof(tblOrder), new List<string>{
//include all
}
}
}
}
};
public static Dictionary<Type, List<string>> IgnoreList(string key)
{
return ToIgnore[key];
}
}
And inside every controller change the JSON Formatter something like:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new FilterContractResolver(CriteriaDefination.IgnoreList("tblCustomer"));
This is what I ended up settling on, hopefully it helps someone else.
Say the EF classes are structured like this:
public partial class MyEF
{
public virtual ICollection<MyOtherEF> MyOtherEFs {get; set;}
}
public partial class MyOtherEF
{
public virtual MyEF MyEF {get; set;}
}
To keep serialization form happening in JSON.NET, you can extend the class and add a method with the name "ShouldSerialize" + property name like so:
public partial class MyEF
{
public bool ShouldSerializeMyOtherEFs() { return false; }
}
If you wanted to get a little more fancy, you could add logic in the method so that it would serialize in certain cases. This allows you to keep serialization logic out of the EF Model First code creation as long as this code is in a different physical code file.
Instead of letting the Entity Framework generate the model, use Code First with an existing database. Now you are more in control.
See this blog entry from Scott Guthrie

Entity Framework 4.1 Implement INotifyPropertyChanged for POCO objects

I am implementing code first, MVC pattern and using Entity Framework 4.1. I put my problems in bold.
Lets assume (for simplify) that I have the following POCO object (Department) and I would like to know when it changes once contextDB.SaveChanges() is carried on and update it, so I implement the following:
public class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, e);
}
}
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
private string name;
[Required]
public string Name
{
get
{
return this.name;
}
set
{
if (this.name== value)
{
return;
}
this.name= value;
this.NotifyPropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
private string personInCharge;
[Required]
public string PersonInCharge
{
get
{
return this.personInCharge;
}
set
{
if (this.personInCharge== value)
{
return;
}
this.personInCharge= value;
this.NotifyPropertyChanged(this,
new PropertyChangedEventArgs("PersonInCharge"));
}
}
}
I have 3 projects(class libraries), for M(Model), V(View) and C(Controller).
From view(V), user generates and event, for example, adding a departament by pressing a button so the View which references the Controller(C), calls a method "Add" in the controller.
The controller(C) which references the Model(M), has access to the context because it instantiates the class which derives from dbContext in the Model, and through the context updates the entity "Department" by doing for example dbContext.departments.add(newDepartment).
When entity departments is updated in the model, NotifyPropertyChanged described above in the entity Department is raised but my problem starts here and is: how to say to the View, hey! entity departments has changed so view departments should be updated!
To achieve it, I have implemented observer pattern, I mean the view department which has a method called "Update", is attached to a collection of observers maintained by the model so the model, on a property change, iterates over this collection and call method "Update" for each view and the view updates.
My problem here is: I do not know how to subscribe to event PropertyChanged in the above class department from the view in order to once a property is changed in Department POCO object (class describe above), the model iterates over the observer collection that contains the observers attached and then calls the appropriate "Update" method for each view(observer) attached to the collection. or maybe it there another better way to do it rather than using INotifyPropertyChanged for POCO objects?
Also I see a problem of using INotifyPropertyChanged, I mean, for example, each time a depatment is added, the view will be updated twice as NotifyPropertyChanged is raised twice, one from Name property and another from PersonInCharge property. Another problem here: How to raise only one time the NotifyPropertyChanged event instead of twice?

MVVM: Delete a CustomerViewModel, but how to get the Customer model inside it?

I have a list of CustomerViewModels in a ComboBox. The selected CustomerViewModel I want to delete and also the Customer wrapped inside it to remove it from the repository.
But how can I access the Customer model inside the CustomerViewModel?
Just a suggestion, make your collection of customerviewmodels an ObserableCollection of CustomerViewModels.
what this buys you is a CollectionChanged Event that you could listen on with a delegate for changes to the collection ie deletion, so from there you could manipulate you model accordingly
http://msdn.microsoft.com/en-us/library/ms653375(VS.85).aspx
perhaps something like
public class CustomersViewModel: ViewModelBase
{
public ObservableCollection<CustomersViewModel> Customers { get; private set; }
public CustomersViewModel()
{
Customers = new ObservableCollection<CustomersViewModel>(GetCustomers());
Customers.CollectionChanged +=
(sender, args) =>
{
if (args.Action == NotifyCollectionChangedAction.Remove)
{
foreach (CustomerViewModel customerViewModel in args.NewItems)
{
DeleteCustomer(customerViewModel.Customer);
}
}
};
}
private void DeleteCustomer(Customer customer)
{
// Call into your repo and delete the customer.
}
private List<CustomersViewModel> GetCustomers()
{
// Call into your model and return customers.
}
... ICommands ect...
}
You might have already access to the Customer inside CustomerViewModel (the VieModel needs to expose the properties of the Customer so the View can databind on them; I usually do it by exposing the Customer or a copy of it directly).
The point is that you should not delete the Customer yourself. That's what the ViewModel is for, to expose an ICommand that deletes the associated Customer. Depending on which MVVM framework you are using, look into DelegateCommand or another equivalent.
Your CustomerViewModel would have a
public ICommand DeleteCommand { get; private set; }
and your View would bind a CommandTarget (probably a Button) to this command. When the command is executed a private method of CustomerViewModel will be run, and you can delete the Customer from there without exposing the deletion mechanism to other parts of the code. For example:
public CustomerViewModel()
{
this.DeleteCommand = new DelegateCommand(this.ExecuteDeleteCommand);
}
private void ExecuteDeleteCommand()
{
// remove the Customer from the ObservableCollection of customers
// and also delete it from the database, or do anything else you want
}