WPF INotifyPropertyChanged for linked read-only properties

后端 未结 7 1323
小鲜肉
小鲜肉 2020-12-30 09:22

I am trying to understand how to update the UI if I have a read-only property that is dependent on another property, so that changes to one property update both UI elements

相关标签:
7条回答
  • 2020-12-30 09:50

    I pretty sure it must be possible in a declarative way, but my first attempt to solve this issue have been a failure. The solution i want to accomplish is using Lambda Expressions to define this. Since Expressions can be parsed (??) it should be possible to parse the expressions and attach to all NotifyPropertyChanged events to get notified about depended data changes.

    in ContinousLinq, this works excellent for collections.

    private SelfUpdatingExpression<int> m_fooExp = new SelfUpdatingExpression<int>(this, ()=> Foo * Foo);
    
    public int Foo
    {
        get
        { 
            return  m_fooExp.Value;   
        }
    }
    

    but unfortionatly i lack on fundamentional know how in Expressions and Linq :(

    0 讨论(0)
  • 2020-12-30 09:57

    One way to indicate that bar has changed is to add a call to onPropertyChanged(this, "bar") in the foo setter. Ugly as hell, I know, but there you have it.

    If foo is defined in an ancestor class or you otherwise don't have access to the implementation of the setter, I suppose you could subscribe to the PropertyChanged event so that when you see a "foo" change, you can also fire a "bar" change notification. Subscribing to events on your own object instance is equally ugly, but will get the job done.

    0 讨论(0)
  • 2020-12-30 09:57

    If you are only using bar for UI purposes, you could remove it from your model completely. You could bind the UI element to the foo property and use a custom value converter to change the result from foo into foo*foo.

    In WPF there are often a lot of ways to accomplish the same thing. Often times, there isn't a correct way, just a personal preference.

    0 讨论(0)
  • 2020-12-30 09:58

    Depending on the expense of the calculation and how frequently you expect it to be used, it may be beneficial to make it a private set property and calculate the value when foo is set, rather than calculating on the fly when the bar get is called. This is basically a caching solution and then you can do the property change notification as part of the bar private setter. I generally prefer this approach, mainly because I use AOP (via Postsharp) to implement the actual INotifyPropertyChanged boilerplate.

    -Dan

    0 讨论(0)
  • 2020-12-30 10:05

    You could simply call

    OnPropertyChanged(this, "bar");
    

    from anywhere in this class...You cold even go like this:

        public raz()
        {
            this.PropertyChanged += new PropertyChangedEventHandler(raz_PropertyChanged);
        }
    
        void raz_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "foo")
            {
                 onPropertyChanged(this, "bar");
            }
        }
    
    0 讨论(0)
  • 2020-12-30 10:08

    If this is a serious issue (by "serious", I mean you have a non-trivial number of dependent read-only properties), you can make a property dependency map, e.g.:

    private static Dictionary<string, string[]> _DependencyMap = 
        new Dictionary<string, string[]>
    {
       {"Foo", new[] { "Bar", "Baz" } },
    };
    

    and then reference it in OnPropertyChanged:

    PropertyChanged(this, new PropertyChangedEventArgs(propertyName))
    if (_DependencyMap.ContainsKey(propertyName))
    {
       foreach (string p in _DependencyMap[propertyName])
       {
          PropertyChanged(this, new PropertyChangedEventArgs(p))
       }
    }
    

    This isn't inherently a lot different from just putting multiple OnPropertyChanged calls in the Foo setter, since you have to update the dependency map for every new dependent property you add.

    But it does make it possible to subsequently implement a PropertyChangeDependsOnAttribute and use reflection to scan the type and build the dependency map. That way your property would look something like:

    [PropertyChangeDependsOn("Foo")]
    public int Bar { get { return Foo * Foo; } }
    
    0 讨论(0)
提交回复
热议问题