First of all, I do use the NotifyPropertyChanged implementation for Change handling and not MVVM implementation.
That said i have the following construct:
[NotifyPropertyChanged]
public interface ISpecimen : INotifyPropertyChanged
{
string Name { get; }
IOpticModel Optics { get; }
}
[NotifyPropertyChanged]
public interface IOpticModel : INotifyPropertyChanged
{
string Lens { get; }
void UpdateOptics(string lens);
}
Now I want PostSharp to Notify me that the Specimen Property Optics got changed (or more precise modified) when I Update the Lens of the IOpticModel. How can i achieve that? Is there anything already ready to go or do i need to create a custom Aspect for that?
I did expect something like that to be ready to use but may be just to blind to find it in the Docs.
This behavior is currently not supported. This means that you'd need to raise the event manually.
You can submit a feature request at https://postsharp.uservoice.com/.
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.
I'm trying to use interfaces more, and often I find myself putting events into the interface. It feels strange, because I'm leaving up to the implementer to know when to raise these events within their implementation.
For example, I have an ILevellable interface, which simply states an object should have xp, a level and a max level, and some methods which modify these values. Classes referencing ILevellable would probably want to know when it levelled up, so I add an Action for OnLevelUp... but the implementation might not put that in the correct place.
public interface ILevellable
{
int Level { get; }
int MaxLevel { get; }
int XP { get; }
event Action<bool> OnXPChanged;
event Action<ILevellable> OnLevelUp;
event Action OnMaxLevelReached;
void LevelUp(int newLevel);
}
Am I supposed to trust that users implementing this interface will know where to implement this? I would assume not. Especially because some events might have arguments which, again, the user might have no idea what it is supposed to represent.
I appreciate any guidance! I am new to working like this.
Thanks!
It's fine to define events in your interfaces. However it is consistent with convention to have both a 'sender' and 'event args' passed into the event. TypedEventHandler is an easy way to do it. For example:
using Windows.Foundation;
public struct LevellableChange {
public LevellableChange( int dl, int dxp)
{
this.ChangeInLevel = dl;
this.ChangeInXP = dxp;
}
int ChangeInLevel { get; }
int ChangeInXP {get;}
}
public interface ILevellable
{
int Level { get; }
int MaxLevel { get; }
int XP { get; }
event TypedEventHandler< ILevellable, LevellableChange> Changed;
}
Then the standard approach to implementing them would be like this:
public class Levellable: ILevellable
{
public event TypedEventHandler<ILevellable, LevellableChange> Changed;
public int Level {
get {
return this._level;
}
private set {
if (value != this._level) {
int oldLevel = this._level;
this._level = value;
this.Changed?.Invoke(this, new LevellableChange(value - oldLevel, 0));
}
}
}
private int _level;
// similar for XP.
}
One other comment, it is useful to have a class implementing INotifyPropertyChanged, and then get in the habit of deriving from it. Together with a snippet in Visual Studio it makes things a lot easier. It's true that for the example above, INotifyPropertyChanged would not be enough since it doesn't give the change to the previous value.
First of all you are not having delegate types in your interface definition but events. Check this answer by Jon Skeet to see the difference between delegates and events. After saying that it is totally fine to have events in your interface, even .Net does it, consider for instance the famous INotifyPropertyChanged interface. It just defines an event...
Secondly:
Am I supposed to trust that users implementing this interface will know where to implement this?
When you define an interface you really do not care about implementers as much as about consumers, and so, for a consumer that sees your contract definition it is useful to know that he/she can susbscribe to an event named OnLevelUp. Let's say I am indeed consuming your interface, I do not know how or when you trigger OnLevelUp event, and I should not really care, because you have given to that event a very semantic and meaningful name, so I can pretty much be sure that it will be triggered when the ILevellable's Level property is increased.
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.
I have a library (no source), to an certain object of which, I need to add some properties.
What would be the a way to do it ? I'm aware I could extend the class and add properties to the child. As well I know there are NO Extension Properties in C# yet. What would you suggest ? Thank you !
The metadata of class could be something like :
public class ResultClass
{
public IList<Item> Results { get; set; }
public int TotalResults { get; set; }
}
and I want to add a :
String description;
to it. Thanks.
There are a couple strategies you could take. Inheritance is the most obvious one. You might also consider composition. Can you give us some more details about what the object is and what properties you need to add to it and why?
After seeing the expanded question:
Either strategy outlined above (composition or inheritance) will probably work for you. Personally, I prefer composition. I think it better insulates you from changes that might be made to the third party library. It also forces you to work through the public interface of the library class, which is preferable when you have no knowledge or control of the internals of a class.
Here is the most basic example of composition.
public CompositeClass
{
private ResultClass _resultClass = new ResultClass();
public IList<Item> Results
{
get { return _resultClass.Results; }
set { _resultClass.Results = value; }
}
public int TotalResults
{
get { return _resultClass.TotalResults; }
set { _resultClass.TotalResults = value; }
}
//
// New Property
//
public string Description { get; set; }
}
Why do you need to add properties? If this is for binding purposes then I would suggest creating a wrapper class or creating your own inherited type that can raise PropertyChanged events in response to various state changes in your third party types. Instead of telling us your proposed solution you should tell us the actual problem you are trying to solve. Also (as I can't vote to close/migrate), this is not really a valid discussion for this site.
I think you are mixing up Extension Methods with Extension Properties.
And the last ones do not exist in C#.
So you should extend the class or create an inheriting class.