【问题标题】:What's the difference between Dependency Property SetValue() & SetCurrentValue()依赖属性 SetValue() 和 SetCurrentValue() 有什么区别
【发布时间】:2011-05-12 22:56:08
【问题描述】:

我之所以问这个问题是因为@Greg D(来自this question)建议我改用SetCurrentValue(),但是查看文档并没有看到有什么区别。或者“不改变其价值来源”是什么意思?

SetValue()

设置依赖属性的本地值,由其指定 依赖属性标识符。

SetCurrentValue()

设置依赖属性的值而不改变其值 来源。

【问题讨论】:

    标签: wpf dependency-properties setvalue setcurrentvalue


    【解决方案1】:

    你提供的 MSDN 链接说得很好:

    这个方法被一个组件使用 以编程方式设置值 没有它自己的财产之一 禁用应用程序的声明 财产的使用。这 SetCurrentValue 方法更改 财产的有效价值,但 现有的触发器、数据绑定和 样式将继续有效。

    假设您正在编写TextBox 控件,并且您公开了人们经常使用的Text 属性,如下所示:

    <TextBox Text="{Binding SomeProperty}"/>
    

    在您的控件代码中,如果您调用SetValue,您将使用您提供的任何内容覆盖绑定。但是,如果您调用 SetCurrentValue,将确保该属性采用给定值,但不会破坏任何绑定。

    据我所知,Greg 的建议是不正确的。您应该始终使用 CLR 包装器属性中的 GetValue/SetValueSetCurrentValue 在您需要属性来获取给定值但又不想覆盖针对您的属性配置的任何绑定、触发器或样式的情况下更有用。

    【讨论】:

    • @Meleak:说实话,我觉得那个帖子有点不清楚。我认为他的意思是说 SetCurrentValue 应该在您想要内部修改依赖属性值的控件中的任何地方使用。事实上,如果你打开他的示例项目,你会看到他仍在 CLR 属性包装器中使用GetValueSetValue。事实上,如果您针对 .NET 4.0 破解开放式反射器,您会发现情况仍然如此。
    • 嗯,到目前为止,我一直使用 SetValue 作为它由 Visual Studio 生成的,它工作正常。我真的不明白你说的销毁绑定是什么意思。第一次值更改后,我的绑定不会被破坏?因为我仍然可以进行更改并且绑定仍然有效?
    • 在 .Net 4 之前缺少 SetCurrentValue() 是导致实现一个适当的、独立的、功能齐全的 NumericUpDownControl 异常困难的原因之一。单击此类控件上的经典向上/向下箭头通常会以编程方式修改控件的值,但使用SetValue(),这会消除用户可能针对该字段实现的任何绑定。 SetCurrentValue() 允许您通过向上和向下按钮以编程方式修改属性的值,而不会破坏与支持数据的绑定。
    • 当我在这个Screenr Video 中使用SetValue 时,我尝试查看是否有任何绑定被破坏。一切正常吗?也许我没有做破坏绑定的事情?但是我该怎么做呢?
    • 我相信绑定主要是在绑定模式不是双向的情况下才被破坏的。对于 TwoWay 绑定,从我的观察中可以找到关于使用 SetValue 的一切。
    【解决方案2】:

    演示线束(完整):

    class test : DependencyObject
    {
        static DependencyProperty XyzProperty =
            DependencyProperty.Register("Xyz", typeof(int), typeof(test), new PropertyMetadata(42));
    
        public test()
        {
            /* ... see code shown below ... */
        }
    
        void inf()
        {
            var info = DependencyPropertyHelper.GetValueSource(this, XyzProperty);
            var msg = $@"{"//"
                       } {(int)GetValue(XyzProperty),2
                       } {(ReadLocalValue(XyzProperty) is int x ? "(Object)" + x : "UnsetValue"),12
                       } {info.BaseValueSource,9
                       } {(info.IsCurrent ? "" : "Not") + "Current",12
                       } {(info.IsCoerced ? "" : "Not") + "Coerced",12
                       }";
            Trace.WriteLine(msg);
        }
    };
    

    讨论示例:

                                          // v̲a̲l̲u̲e̲  s̲t̲o̲r̲e̲d̲-o̲b̲j̲      B̲V̲S̲    C̲u̲r̲r̲e̲n̲t̲?    C̲o̲e̲r̲c̲e̲d̲?
    /*1*/                                    // 42  UnsetValue  Default  NotCurrent  NotCoerced
    
    /*2*/ SetValue(XyzProperty, 5);          //  5   (Object)5    Local  NotCurrent  NotCoerced
    
    /*3*/ SetValue(XyzProperty, 42);         // 42  (Object)42    Local  NotCurrent  NotCoerced
    
    /*4*/ ClearValue(XyzProperty);           // 42  UnsetValue  Default  NotCurrent  NotCoerced
    
    /*5*/ SetCurrentValue(XyzProperty, 5);   //  5   (Object)5  Default     Current     Coerced
    
    /*6*/ SetCurrentValue(XyzProperty, 42);  // 42  UnsetValue  Default  NotCurrent  NotCoerced
    
    /*7*/ SetValue(XyzProperty, 5);          //  5   (Object)5    Local  NotCurrent  NotCoerced
          SetCurrentValue(XyzProperty, 42);  // 42  (Object)42    Local     Current     Coerced
    

    讨论:

    1. DefaultValue 为 '42' 的缺席 DependencyProperty 的初始状态已断言 BaseValueSource.Default 标志。 ReadLocalValue() 返回全局单例实例DependencyProperty.UnsetValue

    2. SetValue() 在内部按预期存储BaseValueSource.Local 值。

    3. 使用SetValue 存储恰好等于DefaultValue 的值不会恢复BaseValueSource.Default 状态(与下面的#6 相比)。

    4. 相反,如果您想删除任何/所有存储值或绑定并将 DP 恢复到原始状态,请致电 ClearValue()。 (见下文注释)

    5. 使用SetCurrentValue(),属性值是通过强制生成的,而不是断言BaseValueSource.Local 模式。请注意,之前的 BaseValueSource Default 仍然有效,尽管该属性现在报告的值是 not,实际上等于其 @987654337 @。

    重要提示:
    这意味着检查GetValueSource() 状态返回的BaseValueSourceBaseValueSource.Default不是主要属性值是否等于 DP 元数据中的默认值的可靠指标。

    1. 另一方面——与上面的 #3 不同——SetCurrentValue 确实检查 DP 元数据的 DefaultValue 是否相等,以便修剪值因此,它也将多余的视为“不必要的”。这种急切的清理可能旨在缓解 DP 存储膨胀,但它也使 DP 状态透明性与特殊情况的“取消屏蔽”行为复杂化,如果不彻底理解,可能会导致模糊的错误。例如,#6 将 DP 清除回与 ClearValue() 无法区分的原始状态...

    2. ...但仅当之前存储的 BaseValueSourceCurrent 而不是 Local;比较 #5/#6 和 #7 对,尽管报告的属性值相同,但内部状态标志差异很大。

    关于ClearValue()

    很明显,PropertyChangedCallback 不会被任何SetValue() 操作调用,这最终不会导致属性值发生变化。这是基本的,因为SetValue 带有一个隐含的假设,即持续的变化与工作中的活动财产的价值有关。不太直观的是,同样的逻辑也适用于ClearValue()

    例如,在 #4 中,ClearValue 导致本地值 42 从内部 DP 存储中删除,以及其他内部状态更改,所有这些都符合预期。问题是OnPropertyChanged 是否被调用在当前的ClearValue 调用中取决于之前的值是否(或不)恰好等于元数据默认值。

    由于“清除”操作的语义似乎意味着对先前状态的摘要丢弃——因此通常假定为上下文任意——人们可能不会想到ClearValue() 的行为取决于某些/任何先前状态的这种不一致。尤其是对于同时涉及(并混合)状态的重要行为,例如是否触发“更改”通知。

    【讨论】:

      【解决方案3】:

      进一步接受的答案:

      我发现this post 很好地解释了 SetCurrentValue()。请注意依赖属性值优先级系统如何将本地值置于绑定值之上。这解释了评论者的意外行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-09
        • 2011-01-20
        • 2015-11-13
        • 1970-01-01
        • 2011-11-14
        • 2012-04-19
        相关资源
        最近更新 更多