2010-08-26 61 views
7

我想了解如何更新UI,如果我有一个只读属性是依赖于另一个属性,以便更改一个属性更新两个UI元素(在这种情况下一个文本框和一个只读的文本框,例如:WPF INotifyPropertyChanged为链接的只读属性

public class raz : INotifyPropertyChanged 
{ 

    int _foo; 
    public int foo 
    { 
    get 
    { 
     return _foo; 
    } 
    set 
    { 
     _foo = value; 
     onPropertyChanged(this, "foo"); 
    } 
    } 

    public int bar 
    { 
    get 
    { 
     return foo*foo; 
    } 
    } 

    public raz() 
    { 

    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void onPropertyChanged(object sender, string propertyName) 
    { 
    if(this.PropertyChanged != null) 
    { 
     PropertyChanged(sender, new PropertyChangedEventArgs(propertyName)); 
    } 
    } 
} 

我的理解是,栏将不会自动更新UI时富被修改请告诉我正确的方式做到这一点

回答

6

一个办法?表明酒吧已经改变了,在foo setter中加入了一个叫onPropertyChanged(this, "bar")的电话。我知道,地狱丑陋,但是你知道。

如果foo是在祖先类中定义的,或者您无权访问setter的实现,我想您可以订阅PropertyChanged事件,以便当您看到“foo”更改时,您也可以发出“酒吧”变更通知。在自己的对象实例上订阅事件同样很难,但会完成工作。

+2

我不明白你为什么认为这是特别丑陋的火灾onPropertyChanged(这,“酒吧”)?我可以看到,如果.bar依赖于3个属性,并且你在所有三个setter中都有这个调用,但是他可以将计算从.bar的property-getter移到一个调用update的计算方法中方法。 – Goblin 2010-08-26 19:16:59

+4

这是丑陋的,因为您将属性B的属性B的适当行为放置在属性A的设置器中所需的逻辑。可以有任何数量的属性取决于A的值。必须修改A以满足其他属性的需要使用A不明显且不可扩展。 – dthorpe 2010-08-26 19:41:04

+1

更清洁的解决方案应该是有某种方式来指示B依赖于A,并且当A改变B时也应该指示值改变。上面的第二个选项,以听取自己的PropertyChanged事件,是在这种风格,但听你自己的事件也有点纠结。 – dthorpe 2010-08-26 19:45:17

0

根据计算的花费和您期望使用的频率,将它设置为私有属性并在设置foo时计算该值可能是有益的,而不是在运行时计算bar get被调用。这基本上是一个缓存解决方案,然后您可以将属性更改通知作为bar私人设置器的一部分来执行。我通常更喜欢这种方法,主要是因为我使用AOP(通过Postsharp)来实现实际的INotifyPropertyChanged样板。

-Dan

5

你可以简单地调用

OnPropertyChanged(this, "bar"); 
从任何地方

在这个类...你冷,甚至是这样的:

public raz() 
    { 
     this.PropertyChanged += new PropertyChangedEventHandler(raz_PropertyChanged); 
    } 

    void raz_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if(e.PropertyName == "foo") 
     { 
      onPropertyChanged(this, "bar"); 
     } 
    } 
+0

这似乎是一个更好的可扩展解决方案,当我需要重复一堆属性时。 – tbischel 2010-08-26 23:21:39

+1

订阅PropertyChanged事件并触发栏更改通知正是PRISM在其Commands Quickstart中执行的操作。他们的例子更好 - 如果(propertyName ==“Price”|| propertyName ==“Quantity”|| propertyName ==“Shipping”) { this.NotifyPropertyChanged(“Total”); } – RichardOD 2011-07-09 11:42:50

8

如果这是一个严重的问题(通过“严重”,我的意思是你有一个不平凡数量的依赖只读属性),你可以制作属性依赖关系图,例如:

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

,然后引用它在OnPropertyChanged:

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

这不是从本质上只是把多个在Foo二传手OnPropertyChanged调用了很多不同的,因为你必须更新每一个新的依赖的依赖地图您添加的财产。

但它确实能够实现PropertyChangeDependsOnAttribute并使用反射扫描类型并构建依赖关系图。这样你的财产会看起来像这样:

[PropertyChangeDependsOn("Foo")] 
public int Bar { get { return Foo * Foo; } } 
+0

你可以显示你的'PropertyChangeDependsOn'系统的实现吗? – Shimmy 2012-01-13 10:31:02

+0

唉,我只声称可以实现它。实际上实施它不是我需要做的事情。 – 2012-01-13 18:04:36

+0

谢谢。我自己实现它,为避免重新加载与反射的关系,我没有多少麻烦制作高效缓存。 – Shimmy 2012-01-15 02:31:02

2

如果你只用于UI的目的,你可以完全从你的模型中删除它。您可以将UI元素绑定到foo属性,并使用自定义值转换器将结果从foo更改为foo * foo。

在WPF中通常有很多方法来完成同样的事情。很多时候,没有一个正确的方法,只是个人喜好。

+0

在我的情况下,用户需要看到它,程序也需要访问它,但这听起来很有趣。 – tbischel 2010-08-26 23:20:17

0

我非常确定它必须以声明的方式成为可能,但我第一次尝试解决这个问题是失败的。 我想要完成的解决方案是使用Lambda表达式来定义这一点。 由于可以解析表达式(??),应该可以解析表达式并附加到所有NotifyPropertyChanged事件以获取有关依赖数据更改的通知。

在ContinousLinq中,这对收藏非常适用。

private SelfUpdatingExpression<int> m_fooExp = new SelfUpdatingExpression<int>(this,()=> Foo * Foo); 

public int Foo 
{ 
    get 
    { 
     return m_fooExp.Value; 
    } 
} 

但unfortionatly我缺乏对fundamentional知道如何表达和LINQ :(

+0

WPF可以直接绑定自我更新表达式吗? - 如果是这样,为什么不只是返回该类型而不是计算的int? – BrainSlugs83 2014-09-05 21:43:52

9

我意识到这是一个老问题,但它的“链接属性NotifyPropertyChanged”的第一个谷歌的结果,所以我觉得它是适当的,这样有一些具体的代码添加这个答案

我用罗伯特Rossney的建议,并创建一个自定义属性,然后在基本视图模型的PropertyChanged事件用它

属性类。:

在我基本视图模型(所有其他WPF视图模型继承):

public abstract class BaseViewModel : INotifyPropertyChanged 
{ 
    protected Dictionary<string, List<string>> DependencyMap; 

    protected BaseViewModel() 
    { 
     DependencyMap = new Dictionary<string, List<string>>(); 

     foreach (var property in GetType().GetProperties()) 
     { 
      var attributes = property.GetCustomAttributes<DependsOnPropertyAttribute>(); 
      foreach (var dependsAttr in attributes) 
      { 
       if (dependsAttr == null) 
        continue; 

       var dependence = dependsAttr.Dependence; 
       if (!DependencyMap.ContainsKey(dependence)) 
        DependencyMap.Add(dependence, new List<string>()); 
       DependencyMap[dependence].Add(property.Name); 
      } 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     var handler = PropertyChanged; 
     if (handler == null) 
      return; 

     handler(this, new PropertyChangedEventArgs(propertyName)); 

     if (!DependencyMap.ContainsKey(propertyName)) 
      return; 

     foreach (var dependentProperty in DependencyMap[propertyName]) 
     { 
      handler(this, new PropertyChangedEventArgs(dependentProperty)); 
     } 
    } 
} 

现在,这可以让我轻松地标记属性,就像这样:

public int NormalProperty 
{ 
    get {return _model.modelProperty; } 
    set 
    { 
     _model.modelProperty = value; 
     OnPropertyChanged(); 
    } 
} 

[DependsOnProperty(nameof(NormalProperty))] 
public int CalculatedProperty 
{ 
    get { return _model.modelProperty + 1; } 
} 
+0

如果我在A类中拥有AnyState属性,那么该怎么办?基本上来自两个其他属性,每个属性都在一个单独的类中。 prop A get {return B.IsRunning || C.IsRunning;} B类也有一个IsRunning属性和C。 – Luishg 2018-02-08 16:26:41

+0

@Luishg如果属性位于不同的类中,则需要使用其他解决方案。该属性仅适用于单个类的计算属性。 – 2018-02-08 16:36:10