I've started to learn WPF\MVVM approach and get bit confused.
I've:
class ModelAAA {
public List<Foo> Foos{get; protected set;}
//..
public void Boo()
{
//Some complex logic updating Foos
}
}
class ViewModelAAA{
private ModelAAA _modelAAA
public ObservableCollection<Foo> Foos{get; protected set;}
public void ViewModelAAA(ModelAAA modelAAA)
{
this._modelAAA = modelAAA;
this.Foos = new ObservableCollection(modelAAA.Foos)
}
public void Boo()
{
this._modelAAA.Boo();
//What should I do here?
}
}
So if I use Boo method of view model, what is proper view to update collection in ViewModel. I've got few ideas, but they all seems to by ugly. Should I manauly recreate\change viewModel Foos each time? As I understad ObservableCollection is not wrapper like object.
P.S. I'm want to make it whitout using ObservableCollection in model
Your Model does not need to use ObservableCollection, but has to notify your ViewModel that something changed in the Collection.
this creates a copy of your List, which is indeed observable, but is not changed at all after that:
this.Foos = new ObservableCollection(modelAAA.Foos);
I would not recommend to create a new ObservableCollection, each time the Model-Collection changed. Instead implement the INotifyCollectionChanged in your Model-Collection and handle the events in your Viewmodel properly.
No, you do not need manually change it, as this is ObservableCollection, but you are changing original collection and not observable one.
To notify listeners of your Observable you need to act on Observable itself.
Example:
public void Boo()
{
this.Foos.Boo();
}
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
Let's say I have a third-party back-end [C# .NET Standard] library representing my Model; this library is used purely as a data processing back-end tool.
Clearly no GUI, only some public readonly registers are provided in order to allow other parent/owning code to observe the status of those registers. As a simple example:
public class MyModel
{
public int MyVariable { get; private set; }
public List<int> MyCollection { get; private set; }
...
}
Now I'd like to provide a WPF application acting as a view-panel/dashboard so that a user can see on a monitor the live situation; I opted for a MVVM approach.
First questions are: is it a correct design approach to assume Model should be left untouched, without adapting it to ViewModel (forgetting Model is a third-party library the code of I actually don't possess)? how can I efficiently and correctly write ViewModel so that it can extract/retrieve those registers from Model?
I have gone through a time-based update option (each second re-reading those registers from Model), which honestly seems a little bit unefficient; I can't figure out how to intercept Model variable changing or Model collection changing from ViewModel side.
Then, let's drop for a moment the constraint I don't possess library code and consider I can modify Model (again, is it a correct design?). I would make MyModel implement INotifyPropertyChanged and change List<int> MyCollection into ObservableCollection<int> MyCollection so that I can intercept data changing and have binding to update at screen. But then I fall into Dispatcher issue when the CollectionChanged event got fired. How can I perform collection update since Model has no access to Dispatcher (.NET Standard)? How can I suppress CollectionChanged fired by Model and fire it from ViewModel inside Dispatcher?
I have gone through a Dispatcher forwarding solution but again I think this is somehow wrong, not properly coded:
public delegate void DispatcherService(Action action);
public class MyModel
{
public int MyVariable { get; private set; }
public List<int> MyList { get; private set; }
public event DispatcherService MyDispatcherService;
public void AddElementToMyList(int element)
{
MyDispatcherService?.Invoke(() => MyList.Add(element));
}
}
public class MyViewModel
{
private MyModel _myModel;
public int MyModelMyVariable { get { return _myModel.MyVariable; } }
public List<int> MyModelMyCollection { get { return _myModel.MyCollection; } }
public MyViewModel(MyModel myModel)
{
_myModel = myModel;
_myModel.MyDispatcherService += ((action) => { Application.Current.Dispatcher.Invoke(action); });
}
}
Usually your model classes don't need to implement INotifyPropertyChaged and INotifyCollectionChanged. This are interfaces that are meant to be implemented by view model classes to provide very general notifications for the view. The model should expose specialized events to notify the view model about data changes.
Since the library is 3rd party, you can only use it the way the API allows it to be used. When the library exposes dynamic data, it will most likely expose related events to which your view model can subscribe to.
Otherwise your view model would have to poll the library for data changes (in this case you may look for a more serious alternative library).
In MVVM the model should never deal with any Dispatcher. Dispatcher is a UI related concept. It deals with the UI thread affinity of UI objects (DispatcherObject). It is also responsible to manage the job queue of the UI thread.
The model component has neither UI related objects nor does it rely on an UI or UI thread.
In your described scenario the correct approach would be to subscribe to the model events from your view model. Then populate a ObservableCollection, which the view model exposes to the view for data binding.
According to MVVM the model does never access the view model. Therefore it is only the view model that has to do the marshaling of the CollectionChanged event or any other access to an object, which is associated with the UI thread (e.g., in case the access occurred from a different thread than the UI thread).
Your corrected example could look like this:
public class MyModel
{
public int Property { get; private set; }
public List<int> DataCollection { get; private set; }
public event EventHandler DataIsReady;
public void AddElementToDataCollectionOnBackgroundThread(int element)
{
Task.Run(()
{
this.DataCollection.Add(element);
DataIsReady?.Invoke(this, EventArgs.Empty);
}
}
}
public class MyViewModel : INotifyPropertyChanegd
{
private MyModel Model { get; };
private int viewModelProperty;
public int ViewModelProperty
{
get => return this.viewModelProperty;
set
{
this.viewModelProperty = value;
OnPropertyChanged();
}
}
public ObservaleCollection<int> ViewItemsSource { get }
public MyViewModel(MyModel model)
{
this.Model = model;
this.Model.DataIsReady += OnModelDataIsReady;
}
private void OnModelDataIsReady(object sender, EventArgs e)
{
// Since the event was raised on a background thread,
// the view model is responsible to marshal the collection change to the UI thread
Application.Current.Dispatcher.InvokeAsync(
() =>
{
this.ViewItemsSource.Clear();
this.Model.DataCollection.ForEach(this.ViewItemsSource.Add));
});
// PropertyChanged is automatically marshalled
// to the UI thread by the framework --> no dispatcher needed
this.ViewModelProperty = this.Model.Property;
}
}
It's perfectly OK for your model to be a wrapper around some library objects. In a perfect world, those library objects would be so full-featured that they would support observation patterns such as INotifyPropertyChanged. Then they could be used in all sorts of contexts, such as directly being the model of your MVVM. In your case, since the library objects don't support observability, it may make sense for you to create wrapper classes that provide that observability. Otherwise, you'll end up compensating for that in your view-model (which is also perfectly acceptable, although I like my scopes to be very clean when defining architectural boundaries).
As for the dispatcher thing, my instinct is that a model should not exhibit any thread affinity -- if something updates the model on a thread, notifications go out on that same thread. Since views are inherently thread-bound, the responsibility for marshaling notifications would either be in the view-model or the directly in the view. Of course, you could always marshal the call which updates the model, so that notifications are only occurring on the UI thread.
I have a hierarchy of items like the following:
MainClass
{
List<Configuration> MyList = new List<Configuration>;
}
Configuration
{
CustomObject MyObject = new CustomObject;
string Property = "";
}
CustomObject
{
string InnerProperty = "";
}
If I want that MainClass gets notified from every change made to InnerProperty and Property, am I correct to assume that I have to transform the List into an ObservableCollection and that both Configuration and CustomObject should derive from INotifyPropertyChanged, right?
If I want only to get a notify when InnerProperty gets changed, and NOT Property, should only CustomObject derive from INotifyPropertyChanged or Configuration too (since I would lose the notification transmission to the parent)?
To get notification about InnerProperty and Property you don't need ObservableCollection in general. ObservableCollection is only for notifications about added\removed items. So you need to implement INotifyPropertyChanged on both Configuration and CustomObject. If you are only interested in changes of InnerProperty, INotifyPropertyChanged on Configuration is not necessary in case your MyObject property never changes and assigned in constructor. Otherwise, you need again to implement INotifyPropertyChanged on both. Same story with ObservableCollection by the way - if your list contains fixed list of items - you don't need that to receive notifications from properties. Otherwise you do.
If I want only to get a notify when InnerProperty gets changed, and NOT Property, should only CustomObject derive from INotifyPropertyChanged or CustomObject too (since I would lose the notification transmission to the parent)?
(I assume that for the second CustomObject in that sentence you actually mean Configuration.)
If you only change InnerProperty and MyProperty doesn't change, then you only need to have CustomObject implement INotifyPropertyChanged. There is no sense of changes propagating up the chain.
If you have a binding path:
{Binding Path=A.B.C.D}
Then if any of those properties change and you want the binding to update, then the relevant level must implement INotifyPropertyChanged. If a property is immutable (doesn't change) then there's no need to support notification of changes. You can think that the binding is listening for changes on each object returned through the evaluation of the binding path.
I'll try to answer your conceptual question. But code is nice...
I'll first just point to C#:: When to use events or a collection of objects derived from an event handling Interface?, which I think is quite helpful.
ObservableCollection takes care of raising events when items are added or removed. There is no need for INotifyPropertyChanged, that is automatically there. If you bind to an ObservableCollection in your XAML, then your XAML will register for those events, and you don't need to do anything yourself. However, nothing else is listening to the events, unless you register. You can do that like this:
myObservablecollection.CollectionChanged += myHandler;
But I don't think that is what you want. If you want to now when something is added to, or removed from MainClass.MyList, then making it an ObservableCollection will do that for you. But you want to know when some particular item in your list is modified, and ObservableCollection does not help with that.
Let's suppose you want a Configuration to do something when its CustomObject changes. Perhaps it sets Property (not a good name?) to MyObject.ToString(). Then you make CustomObject implement INotifyPropertyChanged, like this:
Class CustomObject : INotifyPropertyChanged
{
private string _innerProperty;
public string InnerProperty
{get { return _innerProperty; }
{set
{
_innerProperty = value;
OnPropertyChanged("InnerProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
Then in Configuration:
Class Configuration
{
...
public Configuration(...)
{
... // set up stuff
MyObject.PropertyChanged += myObjectChanged_DoSomethingAboutIt;
}
private void myObjectChanged_DoSomethingAboutIt()
{
DoSomething();
// for example:
Property = MyObject.ToString();
}
}
I hope this helps a bit.
More here: http://www.codeproject.com/Articles/41817/Implementing-INotifyPropertyChanged
Case
Say I have a Person class, a PersonViewModel and a PersonView.
Updating properties from PersonView to the Person model is simple enough. PersonViewModel contains a Person object and has public properties the PersonView binds to in order to update the Person model.
However.
Imagine the Person model can get updated by Service. Now the property change needs to be communicated to the PersonViewModel and then to the PersonView.
This is how I would fix it:
For each property on the Person model I would raise the PropertyChanged event. PersonViewModel subscribes to the PropertyChanged event of Person. PersonViewModel would then raise another PropertyChanged in order to update the PersonView.
This to me seems the most obvious way but I kind of want to throw this question out there in the hope of someone showing me a nicer way. Is it really this simple or are there better ways to mark the model as modified and update the respective properties on the ViewModel?
Additions
The PersonView's DataContext is PersonViewModel. Person gets populated from JSON and gets updated many times during its lifetime.
Feel free to suggest architectual changes for my particular case.
Answer
I marked aqwert as the answer of my question since it provided me with an alternative to the solution I already proposed.
When the view binds directly to the model (which is also the case when the ViewModel exposes the Model) you are mixing UI code and data code. The goal of MVVM is to separate these two code domains. That's what the ViewModel is for.
The view model has to have it's own properties the view can bind to. An example:
class PersonViewModel
{
private Person OriginalModel { get; set; }
public ValueViewModel<string> Name { get; set; }
public ValueViewModel<int> Postcode { get; set; }
protected void ReadFromModel(Person person)
{
OriginalModel = person;
Name.Value = OriginalModel.Name;
Postcode.Value = OriginalModel.Postcode;
}
protected Person WriteToModel()
{
OriginalModel.Name = Name.Value; //...
return OriginalModel;
}
}
Using such a ViewModel-design really separates your data objects from your user interface code. When the structure of the class Person is changed, the UI doesn't need to be fit accordingly, because the ViewModel separates them from each other.
Now to your question. As you can see in the example above, I used a generic ValueViewModel<T>. This class implements INotifyPropertyChanged (and some other stuff). When you receive a new Person instance, you only have to call ReadFromModel(newPerson) on your ViewModel to have the UI updated, because the ValueViewModels the View binds to will inform the UI when their value changes.
Here an extremely simplified example of the internal structure of the ValueViewModel:
class ValueViewModel<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value;}
set
{
_value = value;
RaisePropertyChanged("Value");
}
}
}
This is an approach we used in our MVVM library. It has the advantage that it forces the developer to clearly separate code from the designers concerns. And, as a side effect, it generates a standardized code layout in all your Views and ViewModels and thus improves code quality.
If the view is binding to the Model directly then as long as the service is using the same instance any changes to the model properties will be propogated to the view.
However if you are recreating a new model in the service then you will give the viewmodel the new model. I expect to see the model as a property on the view model so when you set that property all binding should be alerted to the change.
//in the ViewModel
public Person Model
{
get { return _person; }
set { _person = value;
RaisePropertyChanged("Model"); //<- this should tell the view to update
}
}
EDIT:
Since you state there are specific ViewModel logic then you can tailor those properties in the ViewModel
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Prop1") RaisePropertyChanged("SpecicalProperty");
...
}
public string SpecicalProperty
{
get
{
reutrn Model.Prop1 + " some additional logic for the view";
}
}
IN XAML
<TextBlock Text="{Binding Model.PropertyDirect}" />
<TextBlock Text="{Binding SpecicalProperty}" />
This way only both the Model and ViewModel propertys are bound to the view without duplicating the data.
You can get fancier creating a helper to link the property changes from the model to the view model or use a mapping dictionary
_mapping.Add("Prop1", new string[] { "SpecicalProperty", "SpecicalProperty2" });
and then find the properties to update by getting the list of properties
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string[] props;
if(_mapping.TryGetValue(e.PropertyName, out props))
{
foreach(var prop in props)
RaisePropertyChanged(prop);
}
}
So I've got these classes that expose a collection of child objects.
I don't want other classes adding or removing objects from collections because I need to wire into events in the child objects, so as they get added or removed I want to be able to do additional processing. But I really love the ease of manipulating generics internally.
Did I mention this is a WPF app so I need INotifySupport?
The best I can come up with is something like this.
public class foo : INotifyPropertyChanged
{
protected List<ChildFoo> _Children = new List<ChildFoo>();
public foo()
{
}
public void AddChild(ChildFoo newChild)
{
DoAttachLogic(newChild);
_Children.Add(newChild);
NotifyPropertyChange("Children");
}
public void RemoveChild(ChildFoo oldChild)
{
DoRemoveLogic(oldChild);
_Children.Remove(oldChild);
NotifyPropertyChange("Children");
}
public ChildFoo[] Children
{
get
{
return _Children.ToArray();
}
}
}
Are there serious flaws with this design that I'm not seeing?
Every time the Children property is accessed we get the overhead of converting list to an array.
Any advice on this would be great.
This is what I do for normal code:
Public Readonly Property Childern As ObjectModel.ReadOnlyCollection(Of Child)
Get
Return New ObjectModel.ReadOnlyCollection(Of Child)(_ChildernList)
End Get
End Property
For WPF code I would just expose a subclass of ObservableCollection.
You should use ObservableCollection as field in your class, you then have full access to modify collection. Then expose this as ReadonlyObservableCollection via property.
And if you dont change collection itself (eg. nochildren = new ObservableCollection(), you should make field readonly), then you dont need any kind of notifyPropertyChanged on this property, because it doesnt change and collection itself handles those events for its children.
public class Child
{
public int Value { get; set; }
}
class MyClassWithReadonlyCollection
{
private readonly ObservableCollection<Child> _children = new ObservableCollection<Child>();
public MyClassWithReadonlyCollection()
{
_children.Add(new Child());
}
//No need to NotifyPropertyChange, because property doesnt change and collection handles this internaly
public ReadOnlyObservableCollection<Child> Children { get { return new ReadOnlyObservableCollection<Child>(_children); } }
}
I changed the "add child" and "remove child" to protected since you are saying you don't want other classes modifying your collection. I changed your List to ObservableCollection so you can recieve collection changed notifications. Since you are using an IList there is no need to call ToArray(), just access directly.
try this:
public class foo : INotifyPropertyChanged
{
protected ObservableCollection<ChildFoo> _Children = new ObservableCollection<ChildFoo>();
public foo() { }
protected void AddChild(ChildFoo oldChild)
{
DoAttachLogic(newChild);
_Children.Add(newChild);
NotifyPropertyChange("Children");
}
protected void RemoveChild(ChildFoo oldChild)
{
DoRemoveLogic(oldChild);
_Children.Remove(oldChild);
NotifyPropertyChange("Children");
}
public ChildFoo this[int n]
{
get
{
return _Children[n];
}
}
}
You could subclass BindingList and set AllowNew/AllowRemove to false. In your Child Add/Remove methods, you can set it to true, make the changes, then set it back to false. (Of course, you need to hide set access to AllowNew/AllowRemove from outside callers as well).
Another option - subclass Observable collection and override the InsertItem, RemoveItem, etc methods to behave as AddChild/RemoveChild would behave. Then callers can still access it in familiar ways, but not bypass your custom logic.
Subclassing an existing collection class is probably going to be easier (for you and the consumer) than wrapping a collection in another class.
I am trying to improve performance by limiting my objects in memory by using a cache.
This creates problem with my ObservableCollection. Well, i think the code speaks for itself. I have "transformed" my ObservableCollection-derivative to this:
public class TrackCollection : INotifyPropertyChanged, IEnumerable {
private readonly List<Guid> _guids = new List<Guid>();
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public void Add(Track track) {
_guids.Add(track.Id);
}
public void Add(Guid trackId) {
_guids.Add(trackId);
}
public IEnumerator GetEnumerator() {
var tracks = new List<Track>(_guids.Count);
foreach(Guid id in _guids)
tracks.Add(MediaCache.Instance.GetTrack(id));
return tracks.GetEnumerator();
}
}
When i call the Add-methods i would like to call my NotifyPropertyChanged on the class it self.
I have a feeling that this is the wrong approach to this kind of problem, it feels like a workaround.
Anyways, is this the correct solution, and if so, how should i proceed?
The cache method looks like this:
private readonly List<Track> _tracks = new List<Track>();
public Track GetTrack(Guid id) {
foreach (var track in _tracks.Where(track => track.Id == id))
return track;
_tracks.Add(Database.Database.Instance.GetTrack(id));
return _tracks[_tracks.Count - 1];
}
If you want your collection to be observable by the binding system, you need to implement INotifyCollectionChanged. This will allow you to notify interested clients about changes in your collection.
On a side note, to notify listeners about a change in the whole class, you can notify about a change to a property with null passed for the name. This will cause all bindings to reevaluate the source.
I looked through the code a bit further and I'm confused about your intention. Every enumeration returns a brand new collection, which doesn't mesh well with the notion of an observable collection. The clients of an observable collection expect the individual instances of their items to stay consistent, so they only need to respond to restructuring, rather than refreshing the whole list. Could your clarify what you're trying to accomplish?
Instead of trying to handle your caching in the collection class, perhaps you can create a 'reference' class TrackReference and use a standard ObservableCollection. The class would look something like:
public class TrackReference
{
private Guid _id;
public Track Track
{
get { return MediaCache.Instance.GetTrack(id); }
}
public TrackReference(Track track)
{
_id = track.Id;
}
public TrackReference(Guid trackId)
{
_id = trackId;
}
}
Now you have a lightweight handle you can pass around (and observe in a collection) that gives you access to the full Track data when you're ready for it. This is also a good place to add any lightweight meta-data (track name, perhaps?) for your UI classes to Bind.