I've been struggling for a while now with how to best "interact" with settings on a binding level in a WinRT/UWP app. I've been looking for best practices regarding this, but I haven't found clear answers. What I've done so far in my apps is the following:
Define a BindableBase which implements INotifyPropertyChanged.
Create an AppSettings class which inherits from BindableBase and looks a bit like this:
public class AppSettings : BindableBase
{
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
public string MySetting
{
get
{
if (!localSettings.Values.ContainsKey("MySetting"))
localSettings.Values["MySetting"] = "Some default value";
return localSettings.Values["MySetting"].ToString();
}
set
{
localSettings.Values["MySetting"] = value;
RaisePropertyChanged();
}
}
}
Define a ViewModel which has a an AppSettings property:
public class SettingsViewModel
{
public AppSettings Settings { get; set; } = new AppSettings();
}
Bind to the Settings property in the View:
<TextBlock Text="{Binding Settings.MySetting, Mode=TwoWay}">
I've seen and used implementations in the past that had a settings service, but these applications didn't need the settings changes to go in effect immediately. So what I'm basically asking is: If changes to settings should go immediately into effect, is the above implementation a good way to bind to settings? If not, what do you recommend?
I was very inspired by this article so I came up with something a tad bit more elegant to avoid having to copy and paste the property name (key) so many times. I use the following:
public class SettingsViewModel : BindableBase
{
public string MyProperty
{
get
{
return Get("MyProperty", "SomeDefaultValue");
}
set
{
Set("MyProperty", value);
}
}
public T Get<T>(string PropertyName, T DefaultValue)
{
//If setting doesn't exist, create it.
if (!App.Settings.ContainsKey(PropertyName))
App.Settings[PropertyName] = DefaultValue;
return (T)App.Settings[PropertyName];
}
public void Set<T>(string PropertyName, T Value)
{
//If setting doesn't exist or the value is different, create the setting or set new value, respectively.
if (!App.Settings.ContainsKey(PropertyName) || !((T)App.Settings[PropertyName]).Equals(Value))
{
App.Settings[PropertyName] = Value;
OnPropertyChanged(PropertyName);
}
}
}
Assuming your App class defines the following property:
public static IPropertySet Settings
{
get
{
return Windows.Storage.ApplicationData.Current.LocalSettings.Values;
}
}
The latter is more convenient than writing,
ApplicationData.Current.LocalSettings.Values
And can still be accessed globally.
This seems fine in a general case. The only thing I'd change would be to only raise the notification if the data actually changes:
set
{
if (MySetting != value)
{
localSettings.Values["MySetting"] = value;
RaisePropertyChanged();
}
}
This will avoid raising notifications unnecessarily.
Depending on the frequency the values are read, you may want to keep your own in memory copy of the setting values (in a private field) instead of reading them from the container each time.
Related
I want to write a control derived from CheckedListBoxControl (DevExpress), and I need to add a property to the (DataBindings)
These are the standard Properties shown in the PropertyGrid:
So I can only choose between Tag and Text.
What I want is to add a third option called gttMasterField (which will be of type int, don't know if this matters)
I have been experimenting with the documentation but with no results.
These don't seem to cover exact what I am looking for, I don't know the correct search terms to find this, which makes it difficult to google for it. It will probably be somewhere in the documentation but also there I don't know on what terms to look for.
Create a Windows Forms user control that supports simple data binding
Create a Windows Forms user control that supports lookup data binding
Create a Windows Forms user control that supports complex data binding
Here is some code with comments that will also help to explain what I am searching for
public partial class gttDXManyToManyCheckedListBox : CheckedListBoxControl
{
private int _gttMasterField;
// This I want populated by setting the binding property MasterField
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
}
The project is a dotnet framework 4.7.2
To make a custom Property appear in the PropertyGrid's (DataBindings), decorate the Property with the BindableAttribute set to true:
[Bindable(true)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
Optionally also decorate with the desired DesignerSerializationVisibilityAttribute attribute
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
The class can also specify the default bindable Property, settings a DefaultBindingPropertyAttribute:
[DefaultBindingProperty("gttMasterField")]
public partial class gttDXManyToManyCheckedListBox : CheckedListBoxControl
{
private int _gttMasterField;
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
}
Important note:
the class should implement INotifyPropertyChanged -> a bindable Property is supposed to raise notification events. When the binding is set in the Designer, a BindingSource is generated to mediate the binding, but it requires that the objects involved send change notifications (mostrly to determine when the Property value is updated, usually as DataSourceUpdateMode.OnPropertyChanged).
For example:
using System.Runtime.CompilerServices;
[DefaultBindingProperty("gttMasterField")]
public partial class gttDXManyToManyCheckedListBox : CheckedListBoxControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _gttMasterField;
[Bindable(true)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; NotifyPropertyChanged(); }
}
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
}
Setting DesignerSerializationVisibility.Content implies initialization. If a BindingSource is used, this object supports initialization on itself, the Attribute is not strictly required.
It could be set to DesignerSerializationVisibility.Hidden, though, depending on the use case.
Consider a WPF dialogue with lots of input fields, which are bound to properties in a view-model. E.g.
...
<TextBox Text="{Binding FirstName}">
...
public string FirstName {
get { return mFirstName; }
set {
if (mFirstName == value) return;
mFirstName = value;
OnPropertyChanged("FirstName");
}
}
As there are tens of fields like this, I would like to minimize the boilerplate C# code to be written. What options do I have?
If you have the option of using a base class, consider inheriting view model objects from something like this:
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertiesChanged(params string[] propertyNames)
{
foreach (string propertyName in propertyNames)
{
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
}
}
}
Example usage, showing that the boilerplate is greatly reduced:
public sealed class ViewModel : BindableBase
{
private string name;
public string Name
{
get { return name; }
private set { SetProperty(ref name, value); }
}
}
(If you can't use a base class (e.g., you already have one or are using properties on framework elements), you still have the option of adding similar support directly in the class in question.)
I can make your code a little easier to transform into a snippet.
if (mFirstName != value) {
mFirstName = value;
OnPropertyChanged("FirstName");
}
If just the time taken to write it is a pain, and you're using WPF a lot, snippets may also be of use. I know in Sublime Text, VS Code, and Visual Studio, Snippets can be invaluable. Otherwise, I think it's as bare bones as you can get, unless there's something I am not seeing
I use Fody to inject property changed code at compile time. Your class gets an [ImplementPropertyChanged] attribute, then your { get; set; } properties become notifying properties in the compiled code.
https://github.com/Fody/PropertyChanged
First, as I guess you already use Microsoft.Prism, you can drop the string and profit from CallerMemberNameAttribute behind the scenes for you, so that your code would look like this:
public string FirstName {
get { return mFirstName; }
set {
if (mFirstName == value) return;
mFirstName = value;
OnPropertyChanged();
}
}
This is also equivalent to c# 6.0 nameof(FirstName) operator.
Second, you can dig into AOP and abstract the boilerplate to an attribute. One of the AOP frameworks that deals with this is PostSharp and using it your code could look like this:
[NotifyPropertyChanged]
public class Customer
{
public string FirstName { get; set; }
Though it's not free, and AOP has it's drawbacks (thanks Evk).
Similar questions have been asked 1,2, and there does not seem to be optimal answer right now sadly, as it's everyones pain.
I have a WPF-Application for controlling a WCF-RESTful service, i.e. for starting, initializing and stopping it. Therefore I have a MainWindow UI which contains a UserControl to configure settings. When I initialize my service, some data is loaded into DependencyProperties and ObservableCollections to display it in the GUI. Here is the part of the method where I update these settings:
public partial class MainWindow : Window {
private void InitializeService (bool reInitialize = false) {
var restService = (RestService)this.ServiceHost.SingletonInstance;
var settings = restService.GetSettings();
//UCSettings is the "x:name" of the embedded UserControl "UserControlSettings" in this window
this.UCSettings.ExecutionTimes.Clear();
settings.ExecutionTimes.ForEach(x => this.UCSettings.ExecutionTimes.Add(x));
this.UCSettings.TableConfigurationLoader = settings.Timer.Find(x => x.Name == "TableConfigLoader");
}
}
public partial class UserControlSettings : UserControl {
public ObservableCollection<ExecutionTime> ExecutionTimes { get; set; }
public static readonly DependencyProperty TableConfigurationLoaderProperty = DependencyProperty.Register("TableConfigurationLoader", typeof(Setting), typeof(UserControlSettings), new FrameworkPropertyMetadata(default(Setting)));
public Setting TableConfigurationLoader {
get { return (Setting)this.GetValue(TableConfigurationLoaderProperty); }
set { this.SetValue(TableConfigurationLoaderProperty, value); }
}
}
public class Setting {
public string Name { get; set; }
public bool IsEnabled { get; set; }
public int ExecutionTimeId { get; set; }
}
public class ExecutionTime {
public int Id { get; set; }
public string Value { get; set; }
}
In the Code-Designer (UserControlSettings.xaml.cs) these properties are used in some bindings for a ComboBox:
<UserControl x:Class="InsightTool.Gui.UserControlSettings" x:Name="UCSettings">
<ComboBox x:Name="CbConfigLoadingExecutionTime" ItemsSource="{Binding ElementName=UCSettings, Path=ExecutionTimes}" DisplayMemberPath="Value" SelectedValue="{Binding ElementName=UCSettings, Path=TableConfigurationLoader.ExecutionTimeId}" SelectedValuePath="Id"/>
</UserControl>
When I first load in the data with the InitializeService method, everything works fine. The ComboBox is filled with the data of the ObservableCollection and the matching value is selected automatically by the ExecutionTimeId.
When I try to "reinitialize" the service, I call the same method again, but the SelectedValue binding does not work anymore. I checked the values of these properties in the debugger, but they are set correctly in this method again. What am I doing wrong here? Some samples:
Correct display first load:
Incorrect display seconds load:
TableConfigurationLoader is a dependency property. That means a lot of things, but one of them is that when you change the value of TableConfigurationLoader to a different instance of Setting, an event is raised, and this Binding handles that event and updates SelectedValue on the combo box:
SelectedValue="{Binding ElementName=UCSettings, Path=TableConfigurationLoader.ExecutionTimeId}"
However, Setting.ExecutionTimeId isn't a dependency property. It's a regular .NET CLR property, which doesn't notify anybody of anything when its value changes. So if you change the ExecutionTimeId property of the same old Setting that's already in TableConfigurationLoader, nobody knows and nothing happens.
Since Setting is not a control class, you don't particularly need or want its properties to be dependency properties. Instead, you can treat it as a viewmodel. In implementation terms, all a viewmodel really is, is any class that implements INotifyPropertyChanged. With changes to Setting shown below, I think the binding should work as you expect, if I correctly understand your problem. I've changed IsEnabled so it will raise PropertyChanged as well; you may not actually need that, but it's illustrative.
You may need to do the same with your ExecutionTime class.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public class Setting : ViewModelBase
{
public string Name { get; set; }
#region IsEnabled Property
private bool _isEnabled = false;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (value != _isEnabled)
{
_isEnabled = value;
OnPropertyChanged();
}
}
}
#endregion IsEnabled Property
#region ExecutionTimeId
private int _executionTimeId = 0;
public int ExecutionTimeId
{
get { return _executionTimeId; }
set
{
if (value != _executionTimeId)
{
_executionTimeId = value;
OnPropertyChanged();
}
}
}
#endregion ExecutionTimeId
}
There are three (ish) mechanisms in WPF for notifying things that properties have changed, and you need to be using one or another somehow if you want things to update correctly:
Dependency properties of dependency objects: For properties of controls
INotifyPropertyChanged: For properties of viewmodels
INotifyCollectionChanged: For collections.
A collection property should also raise INotifyPropertyChanged.PropertyChanged when you assign a new collection instance to it. A given instance of the collection will handle raising its own events when its contents change.
ObservableCollection<T> and ReadOnlyObservableCollection<T> implement INotifyCollectionChanged so you don't have to; it's a big hassle to implement that one properly so you really don't want to go there.
Creating a new instance of Setting before referring to the actual object solved my problem. It seems that the reference to the specific property of Setting is lost, if I just "override" the existing instance of this property:
var settings = restService.GetSettings();
this.UCSettings.ExecutionTimes.Clear();
settings.ExecutionTimes.ForEach(x => this.UCSettings.ExecutionTimes.Add(x));
this.UCSettings.TableConfigurationLoader = new Setting();
this.UCSettings.TableConfigurationLoader = settings.Timer.Find(x => x.Name == "TableConfigLoader");
Try adding UpdateSourceTrigger=PropertyChanged to the binding like this
<UserControl x:Class="InsightTool.Gui.UserControlSettings" x:Name="UCSettings">
<ComboBox x:Name="CbConfigLoadingExecutionTime" ItemsSource="{Binding ElementName=UCSettings, Path=ExecutionTimes}" DisplayMemberPath="Value" SelectedValue="{Binding ElementName=UCSettings, UpdateSourceTrigger=PropertyChanged,Path=TableConfigurationLoader.ExecutionTimeId}" SelectedValuePath="Id"/>
I am trying to implement INotifyPropertyChanged for a lot of classes, and each of these classes have lots and lots of properties. I have been following this MSDN documentation for how to implement INofifyPropertyChanged, but their instructions don't seem to be practical in cases where a class has many many properties.
Currently most of my properties use the short hand:
public DateTime? DateClosed { get; set; }
But the documentation says that i need to add the following to each setter method:
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("DateClosed");
This means that I then need to declare a body for the get method and declare private variables to handle the actual getting and setting of properties. Like this:
private DateTime? _dateOfIncident = null;
public DateTime? DateClosed
{
get { return _dateOfIncident; }
set
{
_dateOfIncident= value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("DateClosed");
}
}
Does anyone know a way around this?
A few classes can easily be changed to implement INotifyPropertyChanged. But since you state you have a lot of classes with a lot of properties, it's a real burden to get this done manually or even with templates.
What you really need is a tool that does it for you, so I present you Fody and it's NotifyPropertyChanged plugin. What Fody does is weave some extra code in between your code at compile time. The only thing you have to do is add a single attribute on the classes you want to implement INotifyPropertyChanged and the rest is done for you.
[ImplementPropertyChanged]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
I'm not sure you're going to find a workaround here. Auto-properties, as you're using them now, are really just a compiler shorthand that get's converted to full properties with a backing field eventually anyway (at least, as I understand it).
The use of INPC is a routine that's sorta separate and apart from the duty of a normal property. It's notifying subscribers (usually, your view XAML) that the property in question has changed or is changing.
tl;dr -- you're not going to get around having to rewrite autoproperties to full properties with backing fields. But toolkits like MVVMLight have some great Visual Studio code snippets to make this relatively fast. Eventually you can even do this:
private string _someString;
public string SomeString
{
get { return _someString;}
set
{
//Set returns bool, so you can trigger other logic on it!
Set(() => SomeString, ref _someString, value);
}
}
This gives you some neat features:
Strong naming (unlike the magic string in your example)
Set only triggers INPC event if the value is different
Set returns boolean so you can perform more action if the value changed
MVVMLight is nice in that you don't have to use all its features, or even implement MVVM pattern. It just has a lot of nice 'tools' you can leverage.
There are a lot of patterns to do it, or you can buy a tool like PostSharp that will do it for you.
For example, here is one method of doing it:
public abstract class BaseNotifyPropertyChanged : INotifyPropertyChanged
{
private Dictionary<string, object> _valueStore = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
protected T Get<T>([CallerMemberName]string property = null)
{
object value = null;
if (!_valueStore.TryGetValue(property, out value))
return default(T);
return (T)value;
}
protected void Set<T>(T value, [CallerMemberName]string property = null)
{
_valueStore[property] = value;
OnPropertyChangedInternal(property);
}
protected void OnPropertyChanged([CallerMemberName]string property = null)
{
OnPropertyChangedInternal(property);
}
private void OnPropertyChangedInternal(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Which you then inherit from your classes:
public class PlainOldObject : BaseNotifyPropertyChanged
{
public int MyProperty
{
get { return Get<int>(); }
set { Set(value); }
}
}
Which takes care of the backing store and everything for you. You may want to add logic to only call the OnPropertyChangedInternal if the property actually changed (compare references or value), but I'll leave that as an exercise for you.
Simply use the Observable Object class. Instead of creating a DateTime property, you'd create an ObservableObject<DateTime> and you would just bind to DateClosed.Value.
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);