MVVM shared properties - c#

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.

Related

How to update WPF binding from a helper class

I'm sure this is a very basic question but I don't even know the technical term / jargon to Google and self-educate on.
I have created a simple model implementing INotifyPropertyChanged.
public class PushNotes : INotifyPropertyChanged
{
public string CompletePushNotes { get; set; }
}
Binding in cs:
evt_pushNotes = new PushNotes()
{
CompletePushNotes = "HelloThere"
};
this.DataContext = evt_pushNotes;
//snip later in code
Helpers.UpdateCompletePushNotes();
In XAML:
<xctk:RichTextBox x:Name="PushEmail" Text="{Binding Path=CompletePushNotes, Mode=OneWay}" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="40,398,40,40">
<xctk:RichTextBox.TextFormatter>
<xctk:PlainTextFormatter />
</xctk:RichTextBox.TextFormatter>
</xctk:RichTextBox>
Helper:
internal static class Helpers
{
internal static void UpdateCompletePushNotes()
{
//duhhhh what do I do now??
//If I create a new PushNotes it will be a different instantiation....???
}
}
Now this is all fine but I have a method in a helper class that needs to change the CompletePushNotes.
Again I know this is a simplistic / newbie question but I don't know what I need to learn.
So do I make my PushNotes class static, or singleton. Is there some global binding "tree" I can walk to find my instantiated and bound PushNotes class that is attached to the UI element?
Not looking for an a handout just need to know what it is I'm looking for.
TIA
Your PushNotes class does not implement the INotifyPropertyChanged interface. Once you have implemented it, you need to modify your CompletePushNotes property to have a backing field and in the setter of the property you can raise the PropertyChanged event to notify the UI of the source property update.
public class PushNotes : INotifyPropertyChanged
{
string completePushNotes;
public string CompletePushNotes
{
get
{
return completePushNotes;
}
set
{
completePushNotes = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Making the PushNotes class static will not help you. You seem to have a variable of some sort to the PushNotes instance (evt_pushNotes), so just do:
evt_pushNotes.CompletePushNotes = something;
If you have a helper class that does something, call the method in the helper class and get the value back or pass the PushNotes instance into the helper class as a parameter.
internal static class Helpers
{
internal static void UpdateCompletePushNotes(PushNotes pushNotes)
{
pushNotes.CompletePushNotes = something;
}
}

NET 4.5 XAML binding to static properties with updates, working but is there a better way?

Im sorry if it's a duplicate but I couldn't find the question similiar to this one.
From the stackoverflow other questions (this site is superb) I discovered how to bind XAML items to static class with event updates. It works that way:
XAML:
<Label x:Name="label_cc_CPUPOWER" Content="{Binding Path=(database:Database.CPU_CPUPOWER)}"...
Code:
public static class Database
{
#region Event firing
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
#endregion
//CPU
private static string _CPU_CPUCLOCK;
public static string CPU_CPUCLOCK
{
get { return _CPU_CPUCLOCK; }
set
{
if (value != _CPU_CPUCLOCK)
{
_CPU_CPUCLOCK = value;
NotifyStaticPropertyChanged("CPU_CPUCLOCK");
}
}
}
private static string _CPU_CPUPOWER;
public static string CPU_CPUPOWER
{
get { return _CPU_CPUPOWER; }
set
{
if (value != _CPU_CPUPOWER)
{
_CPU_CPUPOWER = value;
NotifyStaticPropertyChanged("CPU_CPUPOWER");
}
}
}
It works and Im really grateful to You that I don't have to figure this out all by myself BUT I have about 25-50 values to store like that. I Wonder if there's a way automate this instead of copy/paste with changing names in brackets?
I also thought about making an List for each object like CPU,RAM and just send EventChanged to the whole object but Im trying to minimize cpu usage and IMHO raising PropertyChanged to all objects while I just want to update two of five isn't good way to do that as whole object values would be refreshed.
Regards
Yes there are, check this out: PropertyChanged.Fody
Then all you have to do is to add :
using PropertyChanged;
...
[ImplementPropertyChanged]
public class MyClass
{
public object Object {get;set;}
// INotifyPropertyChanged implemented automatically # compile-time
}

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

Designing WPF classes for DataBinding

I have to build a chat app in WPF; I want to use DataBinding (still learning it) and want to do it in the right way. I've built a Buddy class in this way:
public class Buddy: INotifyPropertyChanged
{
private String _name;
private String _status;
public String Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
public String Status
{
get
{
return _status;
}
set
{
_status = value;
NotifyPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I don't know which is the best way to handle BuddyList. Should I create a BuddyList class with Add and List method and then DataBinding to an instance of that class? What is the best way to do it?
You can use INotifyCollectionChanged Interface to create your BuddyListClass
Look here for an example: Usage of INotifyCollectionChanged
Also you can use ObservableCollection<T> Class.
If you want to bind collections of items you should use the
ObservableCollection class
and another suggestions create and base class that implement INotifyPropertyChanged interface,
and derive from it each class that you want to bind to UI.
I think your class definition looks just fine.
Regarding the list question I would expose my list as readonly for binding, and all the add, delete, edit functionality I would keep it private. To avoid informing manually your view for changes to your collection, I would use an ObservableCollection but exposing it to public as ReadOnlyObservableCollection.

Categories