I am writing a C# app using the MVVM pattern and I was wondering what would be the best way to implement change tracking on a per property basis. I currently have INotofyPropertyChanged implemented and flag whether or not the whole object is dirty, but some of the requirements I have to implement is they want to be able to show an image by the text box on the UI for every property that has been changed.
Basically my View Models all have a private field that is my class containing the data from my DataAccess Layer. So basically a class will look like this:
private BusinessObj _data
public Name
{
get{ return _data.Name;}
set
{
if(_data.Name != value)
{
_data.Name = value;
PropertyChanged("Name");
IsDirty = true;
}
}
}
My data access layer is basically serializing and deserializing XAML profiles for configuring our products.
You can use ObservableCollection in your Model Class and have your UI subscribe the event when the observable business data collection changed.
in Model View
public class BusinessObjList : ObservableCollection<BusinessObj>
{
public BusinessObjList() : base()
public void AddBusinessObj
{
// your own manipulation
}
}
in UI
BusinessObjList BL = new BL();
BL.CollectionChanged += new NotifyCollectionChangedEventHandler(bl_CollectionChanged);
static void bl_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// handle the change in UI.
}
Related
Say I have a class that receives data over a TCP stream, parses it and changes it's properties accordingly.
public static class SomeClass
{
static bool myBool;
static string myMessage;
public static void ToggleBool()
{
myBool = !myBool;
// Do some other stuff here
}
public static UpdateMessage(string message)
{
System.Diagnostics.Debug.WriteLine(message);
ProcessMessage(message);
myMessage = message;
}
}
Now what I want to do is have a WPF "Debugging Window" that will visually display the settings. I want to basically run a loop that updates parts of the window accordingly.
Something like:
public partial class LogWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public Async Task UpdateUI()
{
while(checkForUpdates)
{
myCheckbox.IsChecked = await SomeClass.UpdatedBoolValue();
string newMessage = await SomeClass.NewMessageRCVD();
txtBox.Append(newMessage);
}
}
}
But that has 2 obvious issues. One, I have no idea how I would make a function that doesn't burn CPU by constantly checking with a while loop. I imagine I could use a getter/setter approach though. Two, I have to update both in order for that loop to run again.
What's the best approach to this? How do update just the parts of the UI that need to be updated?
EDIT: Similar question: Write an Async method that will await a bool
Depends on how complex a implementation/your needs are.
From your example if you made SomeClass implement INotifyPropertyChanged you could easily attach a WPF window to it, and through binding the window would update automatically without any form of a loop.
If your talking about multiple classes and you want to have them all display the property information in the same window, your best bet would probably be to create a queue. In each property you wish to keep track of have the setter write to the queue. (global or singleton) Then you can easily front that information in a window, or multiple via an Observer pattern. Can also set it up to it never writes to the queue in production, or with conditional compile statements production wouldn't even have the code if that is your desire.
The best way to do this is with data binding.
So we need to first define where our data is coming from. This is called the Context. This is going to come from a ViewModel which is an MVVM term. If you aren't aware of MVVM, don't worry, this can just come from any class you have. In the backend .xaml.cs code we need to add the class to our windows's DataContext. Here's what that looks like:
public partial class DebugView : Window
{
public DebugView()
{
InitializeComponent();
DataContext = new DebugViewModel();
}
}
And in our WPF's XAML file for the window we will have a label and textbox that is defined as such:
<Label Content="{Binding ClientCount, FallbackValue='Clients: 00'}" ... />
<TextBox Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}" ... />
The text of a label is it's "content" while the text of a textbox is just "text." We add the binding keyword in there and now the text for each will be linked to the variables ClientCount and Port, repstively. So our DebugViewModel class will look like this at first:
private string _ClientCount;
public string ClientCount
{
get { return _ClientCount; }
set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
}
private string _Port;
public string Port
{
get { return _Port; }
set { _Port= value; RaisePropertyChanged("Port"); }
}
Now you don't have a Function called RaisePropertyChanged() so what I did (and I think is common practice) was I made a base class that implements the INotifyPropertyChanged and handles all the work there.
So our base class, called BaseViewModel will inherit from the INotifyPropertyChanged class, and setup everything for us. That just looks like this (feel free to just copy paste and use as is):
using System.ComponentModel;
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string prop)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
// Other functions we want all ViewModels to have
}
and so then our DebugViewModel class will look like such:
public class ServerViewModel : BaseViewModel
{
private string _ClientCount;
public string ClientCount
{
get { return _ClientCount; }
set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
}
private string _Port;
public string Port
{
get { return _Port; }
set { _Port= value; RaisePropertyChanged("Port"); }
}
public DebugViewModel()
{
// Initialize to default values
ClientCount = $"Clients {server.clientCount}";
Port = $"{server.port}";
}
// Rest of code
}
And then when you start your program it will autopopulate the fields and you when you change the data in the textbox, the string will change, and vice versa. The UpdateSourceTrigger=PropertyChanged part of our XAML declaration makes it so that the variable is updated as soon as the data in the textbox is changed (default behavior is when the textbox loses focus. e.g. you tab to the next textbox or you click away).
This is pretty cool because you can validate input dynamically as it's typed, as well as not having to worry about switching to the UI thread to update the UI, and IMO makes the code look simpler just by having it bound like this.
So I have seen some responses to similar questions as this, but I was wondering if a certain paradigm that I am thinking of is even possible in C#. First, I'll lay out the issue:
I have a MVVM application that I am developing in C#. The model has properties that change, and when a single property changes in the model, it often times affects multiple properties in the view-model. So the view-model listens for changes on the model. And the view listens for changes on the view-model.
In my view-model, I end up getting some code that looks like this:
private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
string prop_name = e.PropertyName;
if (prop_name.Equals("some_property_on_the_model"))
{
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
}
else if (...)
{
... etc ...
}
}
This gets annoying because it just seems messy. And if I forget to edit this function after adding a new property to the view-model then it can easily lead to bugs.
So here is what I would like to do, but I don't know if this is possible. So I would like one of you to help me understand if it is possible or not.
It would be really cool if I could use C#'s "attributes" feature to take care of the property changed propagation.
So maybe something like this:
[ListenToModelProperty("some_property_on_the_model")]
[OnPropertyChanged("MyButtonVisibility")]
public Visibility MyButtonVisibility
{
get
{
if (model.some_property_on_the_model == true)
{
return Visibility.Visible;
}
else
{
return Visibility.Hidden;
}
}
}
private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
prop_name = e.PropertyName;
foreach (var property in view_model)
{
var attributes = property.GetCustomAttributes(typeof(ListenToModelPropertyAttribute));
var descriptions = attributes.Select(x => x.Description);
if (descriptions.Contains(prop_name))
{
notification_to_make = property.GetCustomAttributes(typeof(OnPropertyChangedAttribute));
string notification_string = notification_to_make[0].Description;
NotifyPropertyChanged(notification_string);
}
}
}
Please note that the above code is not meant to be real code. It will definitely not compile and will not work. But I would like to see if something like the above is possible in C#. Is it possible to do something like this using attributes? Or is there a library out there that makes something like this possible?
I have figured out how to do it! It is fairly simple. I will post the relevant code here, and those who are interested can find all the code at this github repository that I just made: https://github.com/davepruitt/model-subscribe
First, I created a custom attribute class. It is a simple class that takes an array of strings as a parameter to its constructor. This allows you to listen to multiple properties on the model for changes. It looks like this:
using System;
using System.Collections.Generic;
namespace TestPropagationOfPropertyChanges
{
[AttributeUsage(AttributeTargets.All)]
public class ListenForModelPropertyChangedAttribute : System.Attribute
{
public List<string> ModelPropertyNames = new List<string>();
public ListenForModelPropertyChangedAttribute (string [] propertyNames)
{
ModelPropertyNames.AddRange (propertyNames);
}
}
}
I then created my model. For simplicity's sake, it only contains two properties. They are strings that store a "first name" and a "last name":
using System;
using System.ComponentModel;
namespace TestPropagationOfPropertyChanges
{
public class Model : NotifyPropertyChangedObject
{
#region Constructors
public Model ()
{
}
#endregion
#region Private data members
private string _first = string.Empty;
private string _last = string.Empty;
#endregion
#region Public properties
public string FirstName
{
get
{
return _first;
}
set
{
_first = value;
NotifyPropertyChanged ("FirstName");
}
}
public string LastName
{
get
{
return _last;
}
set
{
_last = value;
NotifyPropertyChanged ("LastName");
}
}
#endregion
}
}
The view-model, in this case, has a "full name" property. So it wants to listen to any changes that happen to the first or last name on the model, and then react to changes on either of those. I realize this isn't the best "real world" scenario in which this kind of system would be used, but it does help illustrate the concept. The first part of my view-model is below:
using System;
namespace TestPropagationOfPropertyChanges
{
public class ViewModel : NotifyPropertyChangedObject
{
#region Private data members
//This is public for testing purposes
public Model _model = new Model();
#endregion
#region Constructors
public ViewModel ()
{
_model.PropertyChanged += ReactToModelPropertyChanged;
}
#endregion
#region Properties
[ListenForModelPropertyChangedAttribute(new string [] {"FirstName", "LastName"})]
public string FullName
{
get
{
return _model.FirstName + _model.LastName;
}
}
Finally, the view-model finishes with the method that reacts to changes on the model. Normally, in a large and complex application, this method could contain a large if-else statement with lots of calls to NotifyPropertyChanged. Instead, we now just iterate through the properties of the view-model and see which ones subscribe to the model's property that was changed. See below:
void ReactToModelPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//Get the name of the property that was changed on the model
string model_property_changed = e.PropertyName;
//Get a System.Type object representing the current view-model object
System.Type t = typeof(ViewModel);
//Retrieve all property info for the view-model
var property_info = t.GetProperties();
//Iterate through each property
foreach (var property in property_info)
{
//Get the custom attributes defined for this property
var attributes = property.GetCustomAttributes (false);
foreach (var attribute in attributes)
{
//If the property is listening for changes on the model
var a = attribute as ListenForModelPropertyChangedAttribute;
if (a != null)
{
//If the property that was changed on the model matches the name
//that this view-model property is listening for...
if (a.ModelPropertyNames.Contains(model_property_changed))
{
//Notify the UI that the view-model property has been changed
NotifyPropertyChanged (property.Name);
}
}
}
}
}
Overall, it works excellently, and is exactly what I needed. This code can easily be expanded upon to be even more functional for those interested.
I have three ViewModels: MainViewModel, PreferencesViewModel and ColourControllerViewModel - the latter 2 are properties of the MainViewModel.
ColourControllerViewModel is used for the 'ColourSelector' view, where various colours can be created and deleted. It contains an ObservableCollection of ColourViewModel, which has a property detailing the colour, and a bool property determining if it should be shown on the preferences tab (DisplayOnPreferences).
PreferencesViewModel is used for the 'Preferences' view, which contains a combo box of colours - this is represent by an ObservableCollection of ColourViewModel, and only those ColourViewModels where DisplayOnPreferences == true should be displayed.
My question is, what's the easiest way to do this? Currently, I am using an Action delegate called UpdateList() which passes the updated list from ColourControllerViewModel to MainViewModel, which in turn updates the PreferencesViewModel. I don't really like this though, it feels like there's a better way.
Should there be a single ObservableCollection of ColourViewModel on MainViewModel that is updated/accessed by either instance?
Here are the classes:
public class MainViewModel : ViewModel
{
private ColourMappingControllerViewModel _colourMappingControllerViewModel;
private PreferencesControllerViewModel _preferencesTabViewModel;
public MainViewModel()
{
// Initialise the database Handler
dbHandler = DatabaseHandler.DbHandlerInstance;
_colourMappingControllerViewModel = new ColourMappingControllerViewModel(dbHandler.GetColourMappingsList(), UpdateColourList);
_preferencesTabViewModel = new PreferencesControllerViewModel(dbHandler.GetPreferences, ColourMappingList)
}
public ObservableCollection<ColourMappingViewModel> ColourMappingList
{
get { return ColourMappingControllerViewModel.ColourMappingList; }
}
public void UpdateColourList(ObservableCollection<ColourMappingViewModel> colourList)
{
PreferencesTabViewModel.UpdateColourList(colourList);
}
}
public class ColourMappingControllerViewModel : ViewModel
{
public ColourMappingControllerViewModel(IEnumerable<ColourMapping> colourMappingsList, Action<ObservableCollection<ColourMappingViewModel>> updateColourListAction)
{
InitialiseCommands();
ColourMappingList = new ObservableCollection<IColourMappingViewModel>(InitialiseColourMappingsList(colourMappingsList));
}
public ICommand AddColourMappingCommand { get; set; }
private void InitialiseCommands()
{
AddColourMappingCommand = new DelegatingCommand(AddColourMapping);
}
private void AddColourMapping() // Attached to Command on View
{
var newColourMapping = new ColourMappingViewModel(
new ColourMapping());
ColourMappingList.Add(newColourMapping);
ColourMappingsCollectionView.MoveCurrentToLast();
UpdateColourMappingList();
}
private void UpdateColourMappingList()
{
UpdateColourListAction.Invoke(ColourMappingList);
}
}
public PreferencesControllerViewModel : ViewModel
{
public PreferencesControllerViewModel(object preferenceInfo, ObservableCollection<ColourMappingViewModel> colourMappingsList)
{
var pciTrendBlocks = pciBlocks;
ColourMappingsList = colourMappingsList;
}
public void UpdateColourList(ObservableCollection<ColourMappingViewModel> colourList)
{
ColourMappingsList = colourList;
}
}
I know the ObservableCollection class is being misused - it's probably not necessary on the Preferences as it will only be updated in ColourMappingController.
I would agree that you need a single ObservableCollection that is shared between views. This effectively becomes your "Model" in MVVM.
You may also want to enforce different access semantics by having a ReadOnlyObservableCollection that can be passed to your preferences VM etc. This ensures that only ColourControllerViewModel (Which gets the underlying ObservableCollection) can actually alter the collection.
In my apps I tend to have a separate data layer, but yes, for now it would be simplest to just add them to MainViewModel.
The alternative would be to have ColourControllerViewModel be the thing that owns the collection (and exposes it as a ReadOnlyObservableCollection), and have you MainViewModel just pass the collection into any other VM's that need it.
I have a big problem with MVVM design. I am trying to catch every PropertyChanged of my inner nested objects, including futhermore propertchanged of their nested objects, inside my ViewModel but I dont know how to do it.
Here is my structure:
class MyVM
{
public MyVM()
{
this.SomeData = new SomeData();
this.SomeData.NestedObj = new MyNestedDat();
this.SomeData.Str = "This tiggers propertychanged inside MyDat class";
// this triggers propertychanged event inside MyNestedDat class
this.SomeData.NestedObj.Num = 123;
}
// and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that?
public MyDat SomeData
{
get;
set;
}
}
class MyDat : INotifyPropertyChanged
{
private string str;
public string Str;
{
get { return this.str;}
set
{
this.str = value;
this.PropertyChanged(this, "Str");
}
}
publicMyNestedDat NestedObj
{
get;
set;
}
}
class MyNestedDat : INotifyPropertyChanged
{
private int num;
public int Num
{
get{ return this.num;}
set
{
this.num = value;
this.PropertyChanged(this, "Num");
}
}
}
How do i get this to work? I am really clueless where to start.
MyNestedDat class throws PropertyChanged, MyDat class throws propertychanged and i want to catch them all inside my viewmodel. How can i do that?
In my opinion there are a few conceptual things wrong with what you are asking. Just imagine you get a solution that works for your scenario (that you are happy with) and consider the following:
What happens if another layer is added? do you still expect it to work the same?
Should property changes be propagated (viewModel1.propA notifies viewModel2.PropA)?
Should property changes be transformed (viewModel1.SomeProp notifies ViewModel2.AnotherProp)?
Is performance a concern? how will this perform if you need to propagate the property changed events through many levels?
This should be raising alarm bells that the current approach is not the right path to tread.
What you need is a way to provide communication between your viewModels in a loosely coupled way so that you viewModels do not even need to know about each others existence. The beauty of this is that this will also work in other situations not just for property changes.
For your case of property changed events, one viewModel wants to know when something happens (it could be something other than a property changed event, remember). This means the other viewModel needs some way of saying "Hey, a property has changed" (or "My state has changed", "That database call has finished" etc).
Now in C# you can provide events which provide this feature....except, now your objects know about each other which leaves you with the same problem you had before.
To overcome this problem you need another object, a mediator (lets call it Messenger in this example), whose sole purpose is to handle the message passing between the objects so that they can live in ignorance of each other.
The general idea is this. In the viewModel that provides notifications you might do something like this:
public string MyProp
{
get { return _myProp; }
set
{
_mProp = value;
OnPropertyChanged("MyProp");
Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" });
}
}
And in the viewModel that is interested in the event you might do something like this:
public class ViewModel2
{
public ViewModel2()
{
Messenger.Subscribe<VMChangedMessage>(handleMessage);
}
private void handleMessage(VMChangedMessage msg)
{
// Do something with the information here...
}
}
Notice that the two viewModels never reference each other. They are now loosely-coupled.
There are a number of pre-existing implementations already available and it isn't difficult to create your own (the messenger basically keeps a list of objects that are interested in a certain message and iterates the list when it needs to notify the interested parties). There are a few things that can be implemented differently (some implementations just pass string messages around rather than encapsulating the information in objects, and some handle the clean-up of observers automatically).
I would recommend using Josh Smiths (excellent) MVVM Foundation which includes a messenger class. It's also open source so you can see how it works.
There is no clear constraint about what PropertyName should contains in PropertyChangedEventArgs.
See Subscribe to INotifyPropertyChanged for nested (child) objects.
Here is an example :
class A : BaseObjectImplementingINotifyPropertyChanged {
private string m_name;
public string Name {
get { return m_name; }
set {
if(m_name != value) {
m_name = value;
RaisePropertyChanged("Name");
}
}
}
}
class B : BaseObjectImplementingINotifyPropertyChanged {
private A m_a;
public A A {
get { return m_a; }
set {
if(m_a != value) {
if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged;
m_a = value;
if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged;
RaisePropertyChanged("A");
}
}
}
private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) {
RaisePropertyChanged("A." + e.PropertyName);
}
}
B b = new B();
b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); };
b.A.Name = "Blah"; // Will print "A.Name"
The best thing to do here is to separate the idea of a Model and a ViewModel.
By having a ViewModel object that is flatter than the Model you can avoid this scenario. Using an automatic mapping tool like Automapper then allows you to map the Model to the ViewModel and vice versa.
https://github.com/AutoMapper/AutoMapper/wiki/Flattening
class MyDatViewModel : INotifyPropertyChanged
{
public string Str
{
// ... Get Set
}
public int NestedObjNum
{
// ... Get set
}
}
// Configure AutoMapper
Mapper.CreateMap<MyDat, MyDatViewModel>();
// Perform mapping
MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);
I'm working on building up an MVP application (C# Winforms). My initial version is at Critique my simple MVP Winforms app ... Now I'm increasing the complexity. I've broken out the code to handle two separate text fields into two view/presenter pairs. It's a trivial example, but it's to work out the details of multiple presenters sharing the same model.
My questions are about the model:
I am basically using a property changed event raised by the model for notifying views that something has changed. Is that a good approach? What if it gets to the point where I have 100 or 1000 properties? Is it still practical at that point?
Is instantiating the model in each presenter with NoteModel _model = NoteModel.Instance the correct approach? Note that I do want to make sure all of the presenters are sharing the same data.
If there is a better approach, I'm open to suggestions ....
My code looks like this:
NoteModel.cs
public class NoteModel : INotifyPropertyChanged
{
private static NoteModel _instance = null;
public static NoteModel Instance
{
get { return _instance; }
}
static NoteModel()
{
_instance = new NoteModel();
}
private NoteModel()
{
Initialize();
}
public string Filename { get; set; }
public bool IsDirty { get; set; }
public readonly string DefaultName = "Untitled.txt";
string _sText;
public string TheText
{
get { return _sText; }
set
{
_sText = value;
PropertyHasChanged("TheText");
}
}
string _sMoreText;
public string MoreText
{
get { return _sMoreText; }
set
{
_sMoreText = value;
PropertyHasChanged("MoreText");
}
}
public void Initialize()
{
Filename = DefaultName;
TheText = String.Empty;
MoreText = String.Empty;
IsDirty = false;
}
private void PropertyHasChanged(string sPropName)
{
IsDirty = true;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sPropName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
TextEditorPresenter.cs
public class TextEditorPresenter
{
ITextEditorView _view;
NoteModel _model = NoteModel.Instance;
public TextEditorPresenter(ITextEditorView view)//, NoteModel model)
{
//_model = model;
_view = view;
_model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
}
void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TheText")
_view.TheText = _model.TheText;
}
public void TextModified()
{
_model.TheText = _view.TheText;
}
public void ClearView()
{
_view.TheText = String.Empty;
}
}
TextEditor2Presenter.cs is essentially the same except it operates on _model.MoreText instead of _model.TheText.
ITextEditorView.cs
public interface ITextEditorView
{
string TheText { get; set; }
}
ITextEditor2View.cs
public interface ITextEditor2View
{
string MoreText { get; set; }
}
This approach is good. However, if you are looking at having hundred (thousands even!) of Properties then I think you might have a God class (anti-pattern). There aren't many good classes with 100 properties. Instead consider breaking up your model into smaller classes. Furthermore, you don't need to have a separate event for each property. If the model is changed at all you can fire a single event (which might include information describing the change) and the views can handle it from there.
I would avoid using the Singleton pattern unless you actually are sure you want it to apply. Instead, change the constructor for all your views to take in an instance of the model.
Remember, in any layered application, it's normal for the domain model to transcend all layers.
Thus, I would have your presenter pass your Note instance to the view (which no doubt is a Control of some sort), and then let databinding through a BindingSource take over. Once you're using databinding, then the controls will automatically listen to the PropertyChanged event and update accordingly without the need for extra code on your part. Event-based notification is the appropriate use here no matter how many properties are being monitored as only the objects that care about the change will take action (vs. having many objects taking action unnecessarily).
Typically, you get your entity instances from a lower layer. For example, you could implement a service that returns your Note instances. Anytime you ask that service for Note #3, it returns the same instance of Note that it created from persisted data. You could further more add another item to your business layer to supplement your presenters - it could be call a WorkItem or a Controller. All of your presenters could consult their WorkItem instance to get the current instance of Note upon which the user will be working.
I would consider looking into examples of how the Composite Application Block (or CAB) uses these patterns to create smart client applications. It's all design patterns and OO principles, the implementation of which is well worth the effort.
To Question 1: Implementing INotifyPropertyChanged seems to be a good idea to me. Probably you would however split the many properties into some classes.
To Question 2: I am currently using a Singleton pattern for sharing my MVP Model with multiple presenters. I am happy so far, as this guarantees, that there is really ever only one instance of my model.