Avoiding Duplicate RaisePropertyChanged for Cascading Read-Only Properties
Asked Answered
P

1

1

I have several Read-Only Properties in my View Model that are referenced by the View and some of them are dependent on one/more other Read-Only Properties (in the same View Model) which are ultimately dependent on one/more Read/Write Properties (in the same View Model). I've only seen the following pattern in samples to make sure PropertyChanged Event is Raised for all potentially affected Properties, but I don't like duplicating all those RaisePropertyChanged Calls.

I suspect I should instead be adding a Handler to the PropertyChanged Event of my View Model and in that Handler, for each Property which has dependent Properties, call RaisePropertyChanged on only the Properties that directly (vs. also the ones that indirectly) depend on that Property. How can I avoid duplicating all those RaisePropertyChanged Calls?

public bool MyReadOnlyPropertyAA1
{
    get
    {
        return MyReadOnlyPropertyA1 [&& MyReadOnlyPropertyB1 ...]
    }
}


public bool MyReadOnlyPropertyA1
{
    get
    {
        return (MyPropertyA == (Some Value)) [&& MyPropertyB == (Some Other Value)) ... ]
    }
}

public MyPropertyAType MyPropertyA
{
    get
    {
        return myPropertyA
    }
    set
    {
        myPropertyA = value;
        RaisePropertyChanged(nameof(MyPropertyA));  
        RaisePropertyChanged(nameof(MyReadOnlyPropertyA));
        RaisePropertyChanged(nameof(MyReadOnlyPropertyA1));
        RaisePropertyChanged(nameof(MyReadOnlyPropertyAA1));
        ...
        RaisePropertyChanged(nameof(MyReadOnlyPropertyAA...1));
    }
}


public MyPropertyBType MyPropertyB
{
    get
    {
        return myPropertyB
    }
    set
    {
        myPropertyB = value;
        RaisePropertyChanged(nameof(MyPropertyB));  
        RaisePropertyChanged(nameof(MyReadOnlyPropertyA));
        RaisePropertyChanged(nameof(MyReadOnlyPropertyA1));
        RaisePropertyChanged(nameof(MyReadOnlyPropertyAA1));
        ...
        RaisePropertyChanged(nameof(MyReadOnlyPropertyAA...1));
    }
 }
Protractor answered 20/9, 2017 at 6:23 Comment(15)
Firing property change events for read-only properties is the height of sillyness. If you give users of your code the impression the property is read-only, let it stay that way. The design should not be such that changing some other property has an effect on itKravits
@MickyD Just because a property is read-only, does not mean the property's value is constant. It just means that you cannot modify the property through their setter.Libel
There are multiple ways to achieve what you want, but almost all of them as ugly as the one you are trying to avoid. It's all because of creating readonly properties, instead of using bindings/multiple bindings and converters.Tillage
@Libel Then it should be a method to imply some operational change not a setter on some other confusing disjointed propertyKravits
@MickyD Why, just to derive some information (e.g. concat two strings, or a simple calculation) a method wouldn't help much. Having a property allows you to easily bind your derived information.Libel
@CrudaLilium: Re. "using bindings/multiple bindings and converters": That can easily result in: a) duplicate code in the XAML, and/or b) placing business logic in the XAML that should be in the View Model.Protractor
@Protractor Ok, let's assume that value of your properties have complex logic behind it and so changes to view are also off limits. In that case you can either keep the logic as it is and just extract the notifications to one method, but the code itself hides dependencies. I would rather create backing field for your properties and do notification in setter while calculating and setting the value elsewhere. I would trigger the reevaluation from setter. You can also do it by subscribing to propertychanged and checking which property changed, though this approach once again hides dependencies.Tillage
@CrudaLilium: Re. "create backing field....": How does that avoid dup code? Note the RO Props at any level could call RW Props, so may need N Methods for all combos of notifications which again, dups code. Re. "though this approach once again hides dependencies": That brings up the other issue I have with this Pattern. The Props whose PropertyChanged Events are being Raised are "dependents" not "dependencies" of the Props who're Raising them. Those Props shouldn't know about them just like ViewModel Props shouldn't know about what XAML Elements use them.Protractor
4 Downvotes already?!? On a Question?!? This is why good people quit SO. a) If it "does not show any research effort", Link to a dup SO Question or docs that already answers it (although I doubt doing either automatically implies it "does not" per SO rules), b) if it "is unclear", ask a Question in the Comments for clarification, c) if it "is not useful", then state why you think so (although IMHO, most wouldn't think avoiding violations of DRY (prolly one of/the most basic principles in CS) "is not useful"). No one so far has done any of those 3, IMHO, common courtesy things.Protractor
@user1892538: Re. "Possible duplicate of Does this violate the DRY principle?": I don't think that applies here. I'm not trying to avoid violating DRY by avoiding a Layer. Even if did what I suggested, I still have Model. View and View Model Layers. What I'm suggesting is merely an implementation detail change (like Compiler-checked vs. literal Property names) with the bonus that it helps to reduce DRY violations.Protractor
@user1892538: Re. "write method in the derived VM class, don't create a mess in the VM base class": What?!? I never suggested modifying the "VM base class"!?! I suggested adding, in the View-specific Derived VM Class, a Handler for the PropertyChanged Even "in the VM base class" which would execute in addition to the one the View added for it. Note: I'm not suggesting moving the RaisePropertyChange's for the Propertyies themselves from their Setters, just the ones for the Properties that are "dependents" of them.Protractor
@user1892538: Re. "Possible duplicate of Does this violate the DRY principle?": I edited the question to remove DRY refs and therefore any debate about whether this is a "'DRY'" violation. I'm just asking for ways to avoid dupe RaisePropertyChange's, and the answer of moving them to a Method may avoid it for this specific example, but does not for more complex ones (where RO Props at any level could ref RW Props) which my suggestion would. Just answer the question of how I can do what I'm asking please (vs. just responding with the ole' "You "'should'" do something else instead.")Protractor
Not to sure why you got a bunch of downvotes, but my best guess is it's the line "What are some / is the best way...". People here sometimes take that way too literally and see that as "asking for a list of things" so downvote for being too broad. I'd recommend making your core question more specific, such as "How can I avoid duplicating RaisePropertyChanged calls in property setters?". Personally I don't think it's a bad question, and is one I might have asked in my early years on SO.Nellanellda
@Rachel: Thanks! I made the edit. Apparently, on SO, "It (does) hurt to ask!". Sheesh!Protractor
Ha! To you haters who down-voted my Q: Although, I might have worded it poorly, this is a real issue with a real name ("calculated property dependency problem") and real non-trivial solutions according to the writer (and several commenters) of a Code Project article (over 5 yrs prior to my Q) I didn't know existed at " codeproject.com/Articles/375192/…". Although, I prefer the inverse solution offered by "thesly"'s ("Doing the Reverse Attribute" 2-Apr-17 6:25) comment.Protractor
N
4

I use both approaches depending on the property relationship.

If it would be logical that changing one property would trigger a change in another, I just leave the property change code in the setter.

public string PropertyA
{
    get { return propertyA; }
    set
    {
        myPropertyA = value;
        RaisePropertyChanged(nameof(PropertyA));  
        RaisePropertyChanged(nameof(IsPropertyAValid));
        ...
    }
}

However if the two properties are not logically related, then I will use the PropertyChange handler :

private void MyClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch(e.PropertyName)
    {
        case "PropertyA":
            RaisePropertyChanged(nameof(PropertyB));
            break;
    }
}

The reason for this is I want no special logic in my property setters. To me, they should be fairly generic, stuck in a #region tag, and collapsed to be forgotten about.

If two properties are logically related and you would expect changing one to potentially alter the value of another, then the RaisePropertyChange code can be left in the setter.

But if they are different, and in the future myself or another dev were looking at the code and would potentially not know/understand that there's a dependency between the two, I put the change in the class's PropertyChange event handler so it's easy to find and/or change if necessary.

Nellanellda answered 22/9, 2017 at 13:53 Comment(5)
First of all, sigh, sorry you had to get Downvoted for what, IMHO, is a respectable Answer. I think Downvotes should be reserved for Questions, Comments and Answers that are maliciously bad. Otherwise, let the lack of / less Upvotes speak for themselves (i.e. Positive vs. Negative reinforcement). I'll hold off on marking it as the Answer to see if I get other Answers. FWIW, I Upvoted it, so it'll offset at least one of the (hopefully only past / future) Downvotes.Protractor
On second thought, if the Read-Only Properties are only 1 level removed (as in your example) from the Read/Write Properties, then it's the same # of RaisePropertyChange Calls whether they're done in the Read/Write Properties' Setters or Property Change Event Handlers. It's when the Read-Only Properties are 2+ levels removed, where doing so in the Read/Write Properties Setters introduce duplication. Those are the scenarios I'm asking about.Protractor
As I'd mentioned in an earlier Comment to my Q responding to someone else, besides avoiding duplication, another reason to move the RaisePropertyChange's of Dependent Properties outside of a Property's Setter is to avoid what, IMHO, is an issue (call it SoC or whatever) of where the Properties whose PropertyChanged Events are being Raised are "dependents" not "dependencies" of the Properties who're Raising them. Those Properties shouldn't know what their "dependents" are just like ViewModel Properties shouldn't know what View Elements depend on them. What are your thoughts on that?Protractor
@Protractor In that case, just use the PropertyChanged event handler to listen for changes to the dependent property, and raise the associated change notifications as necessary.Nellanellda
I tried to Edit your Answer to include the use case for avoiding duplicate RaisePropertyChange Calls when Read-Only Properties are 2+ levels removed from the Read/Write Properties being referenced, but the Edit was Rejected. If you would please edit your Answer to include that, I'll Accept it as the Answer. Thx!Protractor

© 2022 - 2024 — McMap. All rights reserved.