I have a view that has a group of images I get from a web service
I receive them in a list of this class:
public class ImageModel
{
public int Id { get; set; }
public string Name { get; set; }
public string imageUrl { get; set; }
}
under each image I show an up-vote button, so I added another bool property to the model above:
public bool UpVoted { get; set; }
the ListView that shows these images is bound to an ObservableCollection<ImageModel > , I want to change the voting icon through a converter that convert the value of UpVoted to the corresponding icon, when the user click the voting icon: a command execute this method:
private void OnVoting(ImageModel image)
{
Images.Single(x => x.id == image.id).UpVoted = !image.UpVoted;
}
the problem is that the UI is not updated, and to make sure that I understood the problem I turned the model to a View model and made the required changes to the UpVoted property (I'm using MVVM light library)
bool upVoted;
public bool UpVoted
{
get { return upVoted; }
set
{
Set(ref upVoted, value);
}
}
and it works now,
so I need to bind the UpVoted to the UI, so it's updated whenever it changed
first
your model class must inherit from MvxNotifyPropertyChanged
public class ImageModel : MvxNotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
private bool upVoted ;
public bool UpVoted
{
get { return upVoted ; }
set { upVoted = value; RaisePropertyChanged(() => UpVoted ); }
}
}
then with MvxValueConverter you ready to go
Mustafa's answer mentions a class that is specific to MvvmCross library.
Another alternative is TinyMvvm.
If you wish to write your own MVVM (or understand how MVVM works),
the general pattern is to implement INotifyPropertyChanged: Implement Property Change Notification, which I discuss here.
A convenient way to implement INotifyPropertyChanged, is to make a base class that does that implementation, then inherit from that base class. You can use the code in that sample as your base class. Or use a slightly different implementation, that avoids having to manually pass the property name as a string:
using System.ComponentModel;
using System.Runtime.CompilerServices;
// Use this as base class for all your "view model" classes.
// And possibly for your (domain) model classes.
// E.g.: "public class MyLoginViewModel : HasNotifyPropertyChanged".
// OR "public class MyLoginModel : HasNotifyPropertyChanged".
// Give it whatever name you want, for ViewModels I suggest "ViewModelBase".
public class HasNotifyPropertyChanged : INotifyPropertyChanged
{
// --- This is pattern to use to implement each property. ---
// This works for any property type: int, Color, etc.
// What's different from a standard c# property, is the "SetProperty" call.
// You will often write an IValueConverter (elsewhere) to use in XAML to convert from string to your property type,
// or from your property type to a type needed in your UI.
// Comment out this example property if you don't need it.
/// <summary>
/// Set to "true" at end of your initialization.
/// Then can use Property Trigger on Ready value=true in XAML to do something when your instance is ready for use.
/// For example, load something from web, then trigger to update UI.
/// </summary>
private bool _ready;
public bool Ready
{
get => _ready;
set => SetProperty(ref _ready, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (property == null || !property.Equals(value))
{
property = value;
RaisePropertyChanged(propertyName);
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Again, an alternative to the above code is to use an existing MVVM library.
For another alternative, that doesn't require writing "SetProperty(..)" or "OnPropertyChanged(..)" in all of your property setters, google for info about using Fody/PropertyChanged. Then you wouldn't need any of the above code; your class would simply inherit from INotifyPropertyChanged. (And in app startup, you call a method that "injects" the needed logic into all properties of all INotifyPropertyChanged classes.)
Acknowledgement: The code pattern in example above is based on one of the open source libraries. It might be from TinyMvvm.
you do not say which sort of container that you are using but not all controls are set to support two way notification by default. so you may have to add a
Mode=TwoWay
to get notifications from the back end that data has changed. Or as the previous answer by Mustafa indicated you may need to verify that your class is implementing the InotifyPropertyChanged event with mvvm light.
Related
I've been learning C# and WPF lately and keep seeing people using attributes to "inject" or modify code inside of a class or method like this and I'm wondering how/if it is even possible to make something of the sort using custom attributes:
Here is an older example of how an attribute from a NuGet package (FodyWeavers) shortens code by automatically raising the PropertyChanged event when any setter belonging to a public property in the class is called:
[ImplementPropertyChanged]
public MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public string MyProperty { get; set; }
}
Without the attribute, this would have to look like:
public MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private string myProperty;
public string MyProperty
{
get { return myProperty; }
set
{
myProperty = value;
PropertyChanged(this, new PropertyChangedEventArgs());
}
}
}
Everywhere I look online I keep running into the same brick wall saying "custom attributes are just metadata and do nothing by themselves without external code". My question is, where is this external code placed so that attributes can be recognized and acted upon? What would this external code look like so that such functionality is possible? Is this even possible with custom attributes and reflection alone or is there something else behind the scenes that I'm missing?
Thanks.
This question already has answers here:
Implementing INotifyPropertyChanged - does a better way exist?
(35 answers)
Closed 1 year ago.
I've got a MVVM-ish application which ended up with a model with way too many property change notifications. Specifically, I'm sometimes missing some notifications because there's too many of them.
For example I end up with properties like this:
public string CustomEmail {
get => customEmail;
set
{
customEmail = value;
OnChanged("CustomEmail");
OnChanged("IsSendAllowed");
OnChanged("IsNotFaxEmail");
}
}
Is there a better way to organise it? For example is there a way to mark a property [DependsOn("CustomEmail")] bool IsNotFaxEmail { ... }?
Or if most of the properties are used for bindings, should I be going all in on converters instead? I'd rather not end up with a silly number of converters like {Binding CustomEmail, Converter=EmailIsFaxToElementVisibilityConverter}.
Am I missing some simpler solution?
I don't often find so many dependencies but I can outline a solution I've seen.
Create an attribute. Call it AlsoRaise attribute which takes a string parameter.
You can probably think of a better name. But I think dependson isn't quite right because it's the other way round.
[AlsoRaise(nameof(IsSendAllowed))]
[AlsoRaise(nameof(IsNotFaxEmail))]
public string CustomEmail
You then have something can drive your list of other properties you're going to raise change notification for as well as CustomEmail.
In a static constructor you fill a dictionary<string, string[]> using those attributes. You iterate public properties, grab those attributes.
In OnChanged you look up your property name in that dictionary and raise property changed for the property name plus any strings you find. Or none of course.
I may have forgotten some part, while since I saw this implementation.
You may write a NotifyPropertyChanged method that accepts multiple property names. It does not really reduce the amount of code, but at least allows to make only one method call.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(params string[] propertyNames)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
foreach (var propertyName in propertyNames)
{
propertyChanged.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
}
public class ViewModel : ViewModelBase
{
private string customEmail;
public string CustomEmail
{
get => customEmail;
set
{
customEmail = value;
NotifyPropertyChanged(
nameof(CustomEmail),
nameof(IsSendAllowed),
nameof(IsNotFaxEmail));
}
}
}
I think PropertyChanged.Fody is exactly what you are looking for. It is a specialized library for Fody, which is a code weaving tool. It enables manipulating the IL code of an assembly on build e.g. for adding boilerplate code to classes like implementing property changed notifications.
PropertyChanged.Fody is very powerful in that it can automatically implement INotifyPropertyChanged. Below is an example from the official repository. It is all you have to write, Fody will add (weave in) the rest.
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName => $"{GivenNames} {FamilyName}";
}
Furthermore, dependent properties like GivenNames and FamilyName in this example are detected and will notify a property change for FullName if they are changed, too. There are lots of options to configure property changed notifications manually through attributes like:
AlsoNotifyFor
DoNotNotify
DependsOn
...
These attributes are the most useful for your scenario. Your sample would be reduced to:
[AlsoNotifyFor("IsSendAllowed")]
[AlsoNotifyFor("IsNotFaxEmail")]
public string CustomEmail { get; set; }
Or the other way around with DependsOn:
[DependsOn("CustomEmail")]
public string IsSendAllowed { get; set; }
[DependsOn("CustomEmail")]
public string IsNotFaxEmail { get; set; }
For more examples and an overview of all attributes and other powerful mechanisms of Fody, have a look at the GitHub Wiki.
Is there a simpeler way to bind many properties?
So if you have a Person class with properties: lastname, firstname, birthday, gender, title, ...
Now I do this for every property on the ViewModel:
public string _LastName;
public string LastName
{
get { return _LastName; }
set { _LastName = value;
RaisePropertyChanged("LastName"); }
}
And on the XAML page this binding:
<TextBlock Text="{Binding FirstName}" />
Now image if Person object has like 20 properties..
So my question is can I do this in a simpeler way?
You only need to raise the PropertyChanged event from the setter of a data-bound property if you actually intend to update the property dynamically at runtime. Otherwise you could use auto-implemented properties without any custom logic:
public FirstName { get; set; }
There is also a NuGet package called Fody that can turn simple public properties into full INotifyPropertyChanged implementations for you automatically: https://github.com/Fody/PropertyChanged
If you use a third party MVVM framework, it also might have a code snippet to create property with INotifyPropertyChanged.
If you use Catel, you can download templates and snippets here:
http://catelproject.com/downloads/general-files/
And here's implementation for Caliburn.Micro:
https://github.com/winterdouglas/propc
The simplest solution is to use the POCO mechanism provided by a free DevExpress MVVM Framework.
POCO will automatically implement INotifyPropertyChanged and raise the PropertyChanged event for all public virtual properties in your view model.
All magic happens when you use the ViewModelSource class to create your view model. You can create your view model in XAML:
<UserControl ...
DataContext="{dxmvvm:ViewModelSource Type=local:MyViewModel}">
Or in code-behind:
this.DataContext = ViewModelSource.Create(() => new MyViewModel());
PREMISE
In a default MVVM scenario, your ViewModel don't have to raise notifications on every property.
Typical case: you get some Person from a database, show them on a View, modify them via TextBoxes and other controls, and click "Save" re-sending them to the database. You can do this by setting the DataContext on the View every time you call the database. This action raises a first update on the bound properties of the control and of every sub-control, so all the getters of the ViewModel's bound properties are called one time and the View get populated with the ViewModel's values. When you modify something on the View, that binding carries the modification to the corresponding ViewModel's property (even a simple plain get-set property).
In this case, you're just fine with something like:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
//and so on...
}
You need to raise notifications for the ViewModel's properties only if the View must listen to some property's change. For example, this feature: the Button "Save" is enabled if and only if the Name on the Person is not empty. Here, clearly the Button must be able to see when the Name property changes, so that property setter must raise the PropertyChanged event.
A possible implementation:
Use this as base class for ViewModels:
protected abstract BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetAndNotifyIfChanged<T>(
ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
NotifyPropertyChanged(propertyName);
}
}
}
In a derived class you can write every get-set property like this:
class MyViewModel : BaseViewModel
{
public string MyProp
{
get { return _MyProp; }
set { SetAndNotifyIfChanged(ref _MyProp, value); }
}
private string _MyProp;
}
The type T and the parameter propertyName are automatically inferred.
This is the shortest piece of code you could write, and is not so different from a normal full-property:
public string NormalProp
{
get { return _ NormalProp; }
set { _NormalProp = value; }
}
private string _MyProp;
If you don't want to write all this code every time, use a code snippet.
I have a ViewModel like this
Public class AboutPageViewModel
{
public AboutPageViewModel()
{
AppName = Settings.MyAppName;
}
private string _appName;
public string AppName
{
get{return _appName;}
set{_appName = value; RaisePropertyChanged("AppName");}
}
}
Now in a static class
public static class Settings
{
public static string MyAppName{get;set;} = "LOL"
}
How do I notify the ViewModel everytime MyAppName is changed, and update it to the Binded UI?
Thanks!
As you define it in your question, Settings isn't a static class (ah, I see in comments that was a typo, and it's static in your code). It should not be static. PropertyChanged notifications on a static class are theoretically possible but it's not worth your time to mess with, and there's no need to bother.
Have Settings implement INotifyPropertyChanged, just like your viewmodel. When MyAppName changes, Settings should raise PropertyChanged, just as AboutPageViewModel does when its own AppName property changes.
Now give Settings a static property called Instance:
public static Settings Instance { get; private set; }
static Settings()
{
Instance = new Settings();
}
And handle its PropertyChanged event in AboutPageViewModel:
public AboutPageViewModel()
{
AppName = Settings.Instance.MyAppName;
Settings.Instance.PropertyChanged += (s,e) =>
{
// If you're in C#6:
//if (e.PropertyName == nameof(Settings.MyAppName))
if (e.PropertyName == "MyAppName")
{
AppName = Settings.Instance.MyAppName;
}
}
}
Option Number Two
Arguably a better option; I've done it this way more than once.
In comments, #MikeEason makes the very good point that this could also be done with a custom *Changed event such as MyAppNameChanged, which has two advantages: It lets you go back to a static class, and it lets you skip the check on the property name, which is extra code and also a "magic string". Working with INotifyPropertyChanged we get a little bit numb to the danger of magic strings, but they are in fact bad. If you're in C#6, you can and absolutely should use the nameof() operator, but not all of us are in C#6 just yet. My main responsibility at work is an application that we're hoping to migrate to C#6 this summer.
public static event EventHandler<String> MyAppNameChanged;
private static String _myAppName = "";
public static String MyAppName {
get { return _myAppName; }
set {
if (_myAppName != value)
{
_myAppName = value;
// C#6 again. Note (thanks OP!) you can't pass this for sender
// in a static property.
MyAppNameChanged?.Invoke(null, value);
}
}
}
The drawback of this is that, well, this class is called Settings, not Setting. Maybe it's got a dozen properties changing here and there. That gets to be a real thicket of distinct property-changed events ("so what?" you may ask -- and you may have a point). My tendency is to stick with PropertyChanged if there's a whole sheaf of them, and to add an event if the class has only one or two important properties that somebody needs to keep an eye on. Either way is annoying in my view; try both and you'll eventually settle on a preference.
You don't need to store value in ViewModel if you already have it somewhere (I assume what you are not going to change it in ViewModel itself):
public class AboutPageViewModel : INotifyPropertyChanged
{
public string AppName => Settings.MyAppName;
}
And as for View to know when this property is changed you need 2 things: 1) there should be a way to inform ViewModel when value is changed 2) rise PropertyChanged(nameof(AppName)) (notice INotifyPropertyChanged).
Several possibilities to make it:
Settings should rise event when MyAppName value is changed, ViewModel subscribe to it and rises PropertyChanged;
Store initial value, check periodically if value is changed;
Use another type which implement INotifyPropertyChanged, bind to that type property instead, this will update view automatically if that type rises PropertyChanged.
You have to implement INotifyPropertyChanged interface on Settings class!
then use the same piece of code like this:
private string _myAppName;
public string MyAppName
{
get{return _myAppName;}
set{_appName = value; RaisePropertyChanged("MyAppName");}
}
I have a report object (i.e. a business object) which has several dozen fields to populate. Each field by itself has INotifyPropertyChanged implemented. There is an accessor property for the active report called ActiveReport.
What I want to do is be able to Close the current report, without necessarily opening a new one, and be able to automatically create a report object when the user starts to enter data again.
Here is a rough idea of the structure. ActiveReport is the current report. The GUI is able to directly set the fields of the subclass (name/email) through binding. I want a new BusinessObject to be created when name is being set, but ActiveReport is null. One additional caveat, the report object is auto-generated from XSD files, so I'd rather not have to modify those.
class ControlClass {
public BusinessObject ActiveReport { get; set; }
}
class BusinessObject {
UserInfo field1 { get; set; }
}
class UserInfo : INotifyPropertyChanged {
DependencyProperty name;
DependencyProperty email;
}
I thought of the following scenarios:
Accessor property.
The binding does not seem to use the accessor.
Inserting a check into all event handlers.
I'd rather not have to resort to this -- this breaks the rationale behind using MVVM.
Multibinding
This would require the use of a converter class and instance, and that seems like overkill.
Converter
I thought to ask if there were any other good programming models for this in WPF.
You could create a behavior.
in it, you check if (AssociatedObject.DataContext as ReportObject) is null
and if it is, clear all your fields / set your datacontext / whatever
This should do the trick:
public class ControlClass
{
public BusinessObject ActiveReport { get; set; }
private UserInfo _editableUserData
public UserInfo EditableUserData
{
get { return _editableUserData; }
set
{
if (_editableUserData != null)
_editableUserData.PropertyChanged -= UserDataChanged;
_editableUserData = value;
if (_editableUserData != null)
_editableUserData.PropertyChanged += UserDataChanged;
RaisePropertyChanged("EditableUserData");
}
}
private void UserDataChanged(object sender, PropertyChangedEventArgs e)
{
if (ActiveReport == null)
ActiveReport = new BusinessObject(EditableUserData);
}
}