代码之家  ›  专栏  ›  技术社区  ›  tbischel

链接只读属性的wpf inotifyPropertiesChanged

  •  7
  • tbischel  · 技术社区  · 16 年前

    如果我有一个依赖于另一个属性的只读属性,我将尝试理解如何更新UI,这样对一个属性的更改将同时更新两个UI元素(在本例中是一个文本框和一个只读文本框)。例如:

    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));
        }
      }
    }
    

    我的理解是,当修改foo时,BAR不会自动更新UI。正确的方法是什么?

    7 回复  |  直到 9 年前
        1
  •  7
  •   dthorpe    16 年前

    指示条已更改的一种方法是将调用添加到 onPropertyChanged(this, "bar") 在比赛规则中。我知道,丑陋得要命,但你有它。

    如果foo是在一个祖先类中定义的,或者您没有访问setter实现的权限,我想您可以订阅propertychanged事件,这样当您看到一个“foo”更改时,您还可以触发一个“bar”更改通知。订阅您自己的对象实例上的事件同样丑陋,但这将完成任务。

        2
  •  11
  •   Mage Xy    9 年前

    我意识到这是一个老问题,但这是“链接属性的notifypropertychanged”的第一个谷歌结果,所以我认为添加这个答案是合适的,这样就有了一些具体的代码。

    我使用了RobertRossney的建议并创建了一个自定义属性,然后在基础视图模型的PropertyChanged事件中使用它。

    属性类:

    [AttributeUsage(AttributeTargets.Property,AllowMultiple = true)]
    public class DependsOnPropertyAttribute : Attribute
    {
        public readonly string Dependence;
    
        public DependsOnPropertyAttribute(string otherProperty)
        {
            Dependence = otherProperty;
        }
    }
    

    在我的基础视图模型中(所有其他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; }
    }
    
        3
  •  8
  •   Robert Rossney    16 年前

    如果这是一个严重的问题(通过“严重”,我的意思是您有大量的依赖只读属性),您可以制作一个属性依赖关系映射,例如:

    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))
       }
    }
    

    从本质上来说,这与将多个 OnPropertyChanged 调用 Foo setter,因为必须为添加的每个新依赖属性更新依赖映射。

    但它确实使随后实现 PropertyChangeDependsOnAttribute 并使用反射扫描类型并构建依赖关系图。这样,您的财产看起来就像:

    [PropertyChangeDependsOn("Foo")]
    public int Bar { get { return Foo * Foo; } }
    
        4
  •  6
  •   Tokk    16 年前

    你可以打电话

    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");
            }
        }
    
        5
  •  3
  •   mdm20    16 年前

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

    在WPF中,通常有很多方法可以完成相同的事情。通常情况下,没有正确的方法,只是个人偏好。

        6
  •  0
  •   Dan Bryant    16 年前

    根据计算的费用以及您希望它被使用的频率,将其设置为私有集属性并在 foo 设置,而不是在 bar 已调用get。这基本上是一个缓存解决方案,然后您可以将属性更改通知作为 酒吧 私人设置者。我通常喜欢这种方法,主要是因为我使用AOP(通过Postshap)来实现 INotifyPropertyChanged 样板。

    -丹

        7
  •  0
  •   Ram kiran Pachigolla    13 年前

    我很肯定这是可能的,以声明的方式,但我第一次尝试解决这个问题是失败的。 我要完成的解决方案是使用lambda表达式来定义这个。 因为表达式可以被解析(??)应该可以解析表达式并附加到所有的notifypropertychanged事件,以获得有关依赖数据更改的通知。

    在Continouslinq,这对收藏品来说非常有用。

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

    但不幸的是,我在表达和语言方面缺乏基础知识:(