Simplify mvvm objects WPF [duplicate] - c#

This question already has answers here:
Better way to trigger OnPropertyChanged
(6 answers)
Closed 6 years ago.
I want to simplify the property declarations in my classes. The problem is the getter and setter definition. I am doing exactly the same for hundreds of properties. All properties are created like this, where the method "LogPropertyChanged" also RaisePropertyChange.
public class PCS_MA_V1_ALARMSTAT : ViewModelBase
{
private Boolean _ActionAlarmHighHigh;
public Boolean ActionAlarmHighHigh
{
get
{
return _ActionAlarmHighHigh;
}
set
{
if(value!= _ActionAlarmHighHigh)
{
_ActionAlarmHighHigh = value;
LogpropertyChanged("ActionAlarmHighHigh", oldVal, newVal);
}
}
}
private Boolean _ActionAlarmLowLow;
public Boolean ActionAlarmLowLow
{
get
{
return _ActionAlarmLowLow;
}
set
{
if(value!= _ActionAlarmLowLow)
{
_ActionAlarmLowLow = value;
LogpropertyChanged("ActionAlarmLowLow", oldVal, newVal);
}
}
}
}
Now i think this syntax is much to complex, and a huge hasle to work with. Is there a way where i could create the class like this:
public class PCS_MA_V1_ALARMSTAT: ViewModelBase
{
public Boolean ActionAlarmHighHigh { get; set; }
public Boolean ActionAlarmLowLow { get; set; }
}
And then monitor the instance. If a property has changed i run LogPropertyChanged on that particular property. Is this possible?

You can't override the default behavior of get and set, unfortunately.
What you can do is write a ViewModelBase like this. It's a common idiom. My code below is mostly stolen from this answer, and Prism's BindableBase has a similar SetProperty<T> method, but I didn't go looking for their implementation. There can't be all that much to it.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace HollowEarth.MVVM
{
public class ViewModelBase : INotifyPropertyChanged
{
#region SetProperty
protected virtual bool SetProperty<T>(ref T backingField,
T newValue,
[CallerMemberName] String propName = null)
{
if (EqualityComparer<T>.Default.Equals(backingField, newValue))
{
#if DEBUG
System.Diagnostics.Trace.WriteLine(
$"No change in {propName} == {backingField}");
#endif
return false;
}
#if DEBUG
System.Diagnostics.Trace.WriteLine(
$"Changing {propName} from {backingField} to {newValue}");
#endif
backingField = newValue;
OnPropertyChanged(propName);
return true;
}
#endregion SetProperty
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(
[CallerMemberName] String propName = null)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propName));
}
#endregion INotifyPropertyChanged
}
}
And use it like so:
public class MyViewModel : ViewModelBase
{
#region Whenever Property
private DateTime? _whenever = default(DateTime?);
public DateTime? Whenever
{
get { return _whenever; }
set { SetProperty(ref _whenever, value); }
}
#endregion Whenever Property
}
I write Visual Studio snippets to generate properties. You said that you're generating all this stuff programmatically so snippets aren't an immediate requirement, but it's something to keep in mind.
Another possibility, if maintainability of generated code is a concern, to use partial classes. Put the generated code in separate files that are never edited by hand (much like Form1.Designer.cs/Form1.resx in winforms), and maintain the input files that are used to generate those files. Set up your generator as a build action for whatever your input files are.
For a meta-level witticism on the subject of maintainability, write the partial class generator in Perl.

Related

Is there anyway to avoid calling NotifyChanged in the setter of each method in Xamarin? [duplicate]

This question already has answers here:
Automatically INotifyPropertyChanged
(13 answers)
Closed 5 years ago.
The question says it all.
When using Xamarin this pattern comes up :
public string Property
{
get
{
return this.propertyField;
}
set
{
if (value != this.propertyField)
{
this.propertyField = value;
NotifyPropertyChanged();
}
}
}
And it seems like I I have to do this for each property I have.
Any way to avoid repetion?
There is!
The easiest way to do this is to install the PropertyChanged.Fody package (Nuget).
After you have installed this, you should have a weavers.xml file in the root of your project. Open it, to make sure it contains this:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<PropertyChanged/>
</Weavers>
Now, you just have to add the INotifyPropertyChanged interface on classes where you want to implement the notify functionality and that's it! For all public properties, the boilerplate code is generated at compile-time.
From the README:
You write this
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName => $"{GivenNames} {FamilyName}";
}
What gets generated/compiled is this
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get => givenNames;
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get => familyName;
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName => $"{GivenNames} {FamilyName}";
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
As you can see there is some cool stuff going on here. Dependencies between propeties are detected and accounted for. Also the shothand property notation is supported.
Furthermore, there are some attributes available to control how properties are treated. A few:
[DoNotNotify] exclude a property from generating the notify code
[AlsoNotifyFor(nameof(OtherProperty)] or [DependsOn(nameof(OtherProperty)] With this attributes you can also create a dependency between properties.
Examples can be found on the wiki on Github
You could take a look at PostSharp.
If you do not need any business logic withing your properties' getters or setters, you can also take a look at the ObservableDictionary approach. Like this one
In brief, it's a dictionary that sends PropertyChanged when an entry is changed, so you can make bindings to it's entries.

MVVM shared properties

There are some similar questions on SO, but they weren't quiet the same, so I'm posting this instead. I'm new to MVVM, so I'm trying to figure out how I can create a class that can hold properties that can be shared among views. So, if I set a property in one view, all the other views would get notified if its changed and would adjust their properties accordingly.
What I have now is rather very crude and is definitely not something I want to use. This is my common class that will hold all the properties:
public static class Common
{
private static string _title;
public static string Title
{
get { return _title; }
set
{
if (_title == value)
{
return;
}
_title = value;
OnPropertyChanged("Title");
}
}
public static void Load()
{
// set properties here
}
public static event PropertyChangedEventHandler PropertyChanged;
private static void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(typeof(SettingsWorker), new PropertyChangedEventArgs(name));
}
}
}
...and I have to subscribe to it from each ViewModel:
Common.PropertyChanged += Common_PropertyChanged;
private void Common_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Title = Common.Title;
}
But this is where the breakdown happens. I can get the property name from the PropertyChangedEventArgs, but I've no idea how to get the value. Therefore, I'm forced to update all the properties, and that can get nasty to maintain. The code is becoming a mess.
I'm basically trying to get properties that ViewModels can share. How can I accomplish this?
It looks like you just have some global data you want to show in multiple places. The most straightforward way to do this is to just make it like and normal ViewModel class and then make it available to each of your other ViewModels and expose it from them to bind to directly (rather than copying the property into each of them). You can do this using IOC, or make it available statically, more similar to how you have it now.
If you go the static direction, the key change you need to make is to use a singleton rather than a static class in order to allow property change notification to work. Bindings work with INPC on instances but not static classes. The Common class would look more like this:
public class Common : INotifyPropertyChanged
{
private static Common _instance = null;
protected Common()
{
}
public static Common GetInstance()
{
if (_instance == null)
_instance = new Common();
return _instance;
}
private string _title;
public string Title
{
get { return _title; }
set
{
if (_title == value)
return;
_title = value;
OnPropertyChanged("Title");
}
}
public void Load()
{
}
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventArgs ea = new PropertyChangedEventArgs(propertyName);
if (PropertyChanged != null)
PropertyChanged(this, ea);
}
public event PropertyChangedEventHandler PropertyChanged;
}
There are a lot of different ways you can then use this. Here's one of the more direct ones:
public class SomeViewModel : ViewModelBase
{
public Common CommonData { get; private set; }
public SomeViewModel()
{
CommonData = Common.GetInstance();
}
}
Then in XAML you can bind to the properties from the common class and get change notification, even across the different VM usages.
<TextBox Text="{Binding Path=CommonData.Title}"/>
There's also the option of making the singleton accessible as a property and binding to it directly from XAML using x:Static but that's a little different direction that what you were asking.
So if you have other views that want to get notified when a property changes I would assume you have a separate viewmodel for each of those views. In that case what you would be asking for is "How can viewmodels talk to other viewmodels?" For a good learning experience, I would recommend looking into the observer pattern. If you don't like that style, then I would recommend you look into using a MVVM Framework like "SimpleMVVM, Catel, or many others" (Just need to look some up). Then once you have that framework in you project, I would create a baseviewmodel class that all your viewmodels will inherit. Once that is done you can take advantage of the frameworks messenger system. Basically it would look something like:
public YourViewModel()
{
RegisterToReceiveMessages(MessageTokens.SomeToken, OnChangeCallYourMethodToAddress); //The MessageTokens is something you generally create ur self.
}
#region Notifications
void OnChangeCallYourMethodToAddress(object sender, NotificationEventArgs<SlideShowLocale> e)
{
SomeProperty = e.Data;
}
Then to send a message to "YourViewModel" From another ViewModel:
public AnotherViewModel
{
SendMessage(MessageTokens.SomeToken, new NotificationEventArgs(WhateverYouWantToSend));
}
So basically, by declaring the token you want, it then finds the viewmodel that is registered with that token and listens for any messages to come in.

Using INotifyPropertyChanged in a Class

C# allows the creation of a property as below:
public string SomeRandomText
{
get; set;
}
The framework handles the creation of the backing variable for this property. How can I have such a property and still have change notification?
Is this allowed in a class that implements INotifyPropertyChanged?
public string SomeRandomTextBeingNotified
{
get;
set
{
NotifyPropertyChanged("SomeRandomTextBeingNotified");
}
}
You can't use automatic properties when trying to use this. You'll need to creating a backing store:
private string _someRandomText;
public string SomeRandomText {
get { return _someRandomText; }
set
{
_someRandomText = value;
NotifyPropertyChanged("SomeRandomText");
}
}
To make code look cleaner, you can use attributes for INotifyPropertyChanged.
Easy usage of INotifyPropertyChanged with Property-Attribute
Have a look at this Use of Attributes... INotifyPropertyChanged
Actually you can, but you basically need to change the bytecode post C# compiler.
This may sound like a lot of work, but this is one of the easier postprocessing steps that for example PostSharp includes.
http://www.sharpcrafters.com/solutions/notifypropertychanged
http://www.sharpcrafters.com/blog/post/Recording-Automate-INotifyPropertyChanged-with-Karol-Waledzik-from-Internetium.aspx
A lot more functionality is available ;)
Otherwise note that
enter code hereenter code here`NotifyPropertyChanged("SomeRandomTextBeingNotified");
is bad code. I do all that in one field update method:
set
{
OnUpateField (ref _someRandomText, value);
}
The update method does all - check for equality (you do NOT want to trigger when new value = old value), then trigger updates as needed. It gets the property name through the calling method third parameter that is automatically set by the compiler. Alternatives are using a LINQ statement ( ref someRandomText, value, this->SomeRandomText). I never would love a string there that does not get renamed on refactoring ;)
If you don't have a base class, something like this is cake and very flexible:
public class NotificationObject : INotifyPropertChanged
{
private readonly Dictionary<string, object> Properties = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
protected TType Get<TType>(string propertyName)
{
object value;
return Properties.TryGetValue(propertyName, out value) ? (TType)value : default(TType);
}
protected void Set<TType>(TType value, string propertyName, params string[] dependantPropertyNames)
{
Properties[propertyName] = value;
OnPropertyChanged(propertyName);
if (dependantPropertyNames != null)
{
foreach (string dependantPropertyName in dependantPropertyNames)
{
OnPropertyChanged(dependantPropertyName);
}
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArts(propertyName));
}
}
}
This can be used like this:
public SomeObjectThatNeedsToNotifySomething : NotificationObject
{
public int SomeValue
{
get { return Get<int>("SomeValue"); }
set { Set<int>(value, "SomeValue", "SomeAggregateValue"); }
}
public int SomeOtherValue
{
get { return Get<int>("SomeOtherValue"); }
set { Set<int>(value, "SomeOtherValue", "SomeAggregateValue"); }
}
public int SomeAggregateValue
{
get { return SomeValue + SomeOtherValue; }
}
}
If you already have a base class and need to just implement the INotifyPropertyChanged interface, #Rob is correct, provide a backing field and fire the event.
There is no such thing as semi-automatic properties. Nevertheless, there are quite a few ways to implement INotifyPropertyChanged that don't require the burdensome imperative code.
1) Mentioned before: PostSharp, an aspect oriented and commercial project.
2) Creating a Castle DynamicProxy solution. A sample can be found here, actually there's plenty of others out there.
It's worthwhile investing some time in a generic solution, the boilerplate code can get vexing after a while and is prone to errors.

Replacing non-trivial getters with dependency properties

I've recently started experimenting with DataBinding and implementing DependencyProperties for my custom classes. It all works fine and the possibilities are exciting, however, I came across a problem that may be only solvable by slightly modifying the overall class design. And I want to make sure this is the only option and I'm not missing anything.
So, my class stores information about video files the user imports into the application. Among other properties, it contains:
public class VideoFile {
public string FilePath { get; protected set; }
public uint ID { get; protected set; ]
public string Extension { get { return Path.GetExtension(FilePath); } }
public string FileName { get { return Path.GetFilename(FilePath); } }
}
So, I've successfully replaced FilePath with an DependencyProperty. However, in the UI, I mostly want to display just the filename, which uses some logic to provide its value. As far as I know, here are my options:
I could simply create DependencyProperties for FileName and Extension, and set their value in the constructor, but that's redundant; I already have that information in the FilePath, so I want to avoid this option.
Create ValueConverters, one for displaying Filename and one for displaying Extension, and use them in my bindings.
I've only met ValueConverters briefly, so I'm not sure about it. Can I use them for this purpose? Or, have I just encountered one of the main reasons they exist? :)
And last but not least, can anyone think of a situation similar to this, when a ValueConverter is not the right way to go? I want to avoid jumping straight into them, only to realize it will not work because "that one" property just can't be expressed in this way.
You don't need DependencyProperties for this. You only need a DependencyProperty when you're going to set into a property using a MarkupExtension, and I doubt you're doing that with a model class (because you won't be declaring this class in Xaml!).
A much more lightweight way would be to use INotifyPropertyChanged. Here's a .NET 3.5-style implementation:
public class VideoFile : INotifyPropertyChanged
{
private string _filePath;
public string FilePath
{
get
{
return _filePath;
}
protected set
{
_filePath = value;
OnPropertyChanged("FilePath");
OnPropertyChanged("Extension");
OnPropertyChanged("FileName");
}
}
public uint ID { get; protected set; }
public string Extension { get { return Path.GetExtension(FilePath); } }
public string FileName { get { return Path.GetFileName(FilePath); } }
protected void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
(In .NET 4.5 this can be simplified somewhat thanks to the new [CallerMemberName] attribute.)
The only downside is that you require backing fields for your properties. However, there's a VS extension called NotifyPropertyWeaver that can automate part of this work and remove the need for explicit backing properties, too.
Don't duplicate data.
Prefer Binding and an IValueConverter, because that way, whenever FilePath changes, Extension and FileName will be updated in the UI as well.
You could also, of course, raise PropertyChanged for them in FilePath's setter but that's bad practice since FilePath should not have to care about who/what uses it.
The class then looks like:
public class VideoFile : INotifyPropertyChanged {
string m_FilePath;
public string FilePath
{
get { return m_FilePath; }
protected set
{
if(value != m_FilePath)
{
m_FilePath = value;
RaisePropertyChanged(() => this.FilePath);
}
}
}
public uint ID { get; protected set; }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged<T>(Expression<Func<T>> _PropertyExpression)
{
RaisePropertyChanged(PropertySupport.ExtractPropertyName(_PropertyExpression));
}
protected void RaisePropertyChanged(String _Prop)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(_Prop));
}
}
#endregion
}
Please note that PropertySupport is part of Prism, but you can do without it by calling RaisePropertyChanged("FilePath"), it's just neat to have type safety because if you change the property's name you will have a compile-time error.
As far as I understand you would like to just display File Name on UI. Then you may consider updating FileName property whenever FilePath dependency property is changed (OnChangedFilePath method). You can also check if FilePath is OK in ValidateFilePath method. Please note that FileName must be also a dependency property or supporting IPropertyChanged, otherwise UI will not be updated when you change it. You do not need to use a converter for this purpose.
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
private static object CoerceFilePath(DependencyObject d, object value)
{
return value;
}
private static bool ValidateFilePath(object Value)
{
return true;
}
private static void OnChangedFilePath(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string), typeof(ClassName),
new PropertyMetadata(#"C:\File.avi", OnChangedFilePath, CoerceFilePath),
new ValidateValueCallback(ClassName.ValidateFilePath));

how to implement observable int in wpf ViewModel?

In my mvvm ViewModel I have such field
public int Delta { get; private set; }
However when I update it like that:
Delta = newValue;
UI is not refreshed.
I was thinking that databinding will do that for me. For example I can declare collection as ObservableCollection and then databinding will work.
However there are no ObservableInt, how to say View that it need to be refreshed then?
Probably I should raise some event "notify property changed" or something?
You have two choices:
Implement the INotifyPropertyChanged interface on your class.
Inherit from DependencyObject and implement Delta as a DependencyProperty.
The simplest option is #1. You can implement the INotifyPropertyChanged interface on your class quite easily:
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set { _delta = value; PropertyChanged?.Invoke(nameof(Delta)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can read more about using and implementing dependency properties on MSDN.
While we're at it with improving the answer, some of the other new additions of c# 6.0 and 7.0 help make it ever more compact:
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get => _value;
set { _value = value; NotifyPropertyChanged(nameof(Value)); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This way, you aren't using any "embedded values" (i.e - the property's name) and are keeping the code refactor-safe.
And there's also no need for redundant code blocks due to c# 6.0 and 7.0's new Expression body features
Using #LBushKin's Answer, i modified it to
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value; }
set { _value = value; NotifyPropertyChanged("Value"); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(String propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and to set it up:
class MainWindow ...
// a bool with initial value of true
public static Prop<bool> optionBool { get; set; } = new Prop<bool>{ Value = true };
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// connect UI to be able to use the Prop
DataContext = this;
}
and to use it:
<Grid ...
<CheckBox Content="Da Check" ... IsChecked="{Binding optionBool.Value}"/>
There is also a Collection and 2-Properties version here:
Utils.ObservableProperties.cs (this repo contains several related classes)
Just implement INotifyPropertyChanged Interface in your class and use it to raise a PropertyChanged for your Property and then UI will update. If you are using an MVVM project template then there is a good chance you already have a helper method implemented you only need to use it.
MSDN INotifyPropertyChanged
GalaSoft MVVM Light Toolkit
The ObservableCollection raises events automatically but for your own properties you have to raise the events yourself.
A good example is here: http://www.codeproject.com/Tips/228352/Naming-Properties-in-MVVM?display=Print
I'd suggest using mvvm light: http://mvvmlight.codeplex.com, I used it in silverlight and wpf applications. Very easy to use and provides a messageing system between model, view model and view.
Adding on to https://stackoverflow.com/a/8316100/5725669, there is a new and easy way to do this without remembering to keep track of PropertyChanged?.Invoke(nameof(Delta)); in every location
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set {
_delta = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public YourClass()
{
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
It makes use of CallerMemberName for skipping manual entries for property name. More details on this MSDN Doc

Categories