我问这个的原因是因为@Greg D(来自这个问题)推荐我使用它SetCurrentValue()
,但是看看文档并没有看到有什么区别。或者“不改变其价值来源”是什么意思?
设置依赖属性的本地值,由其依赖属性标识符指定。
设置依赖属性的值而不更改其值源。
我问这个的原因是因为@Greg D(来自这个问题)推荐我使用它SetCurrentValue()
,但是看看文档并没有看到有什么区别。或者“不改变其价值来源”是什么意思?
设置依赖属性的本地值,由其依赖属性标识符指定。
设置依赖属性的值而不更改其值源。
您提供的 MSDN 链接说得很好:
此方法由以编程方式设置其自身属性之一的值而不禁用应用程序声明的属性使用的组件使用。SetCurrentValue 方法更改属性的有效值,但现有触发器、数据绑定和样式将继续工作。
假设您正在编写TextBox
控件,并且已经公开了Text
人们经常使用的属性,如下所示:
<TextBox Text="{Binding SomeProperty}"/>
在您的控件代码中,如果您调用,SetValue
您将使用您提供的任何内容覆盖绑定。但是,如果您调用SetCurrentValue
,将确保该属性采用给定值,但不会破坏任何绑定。
据我所知,格雷格的建议是不正确的。您应该始终使用GetValue
/SetValue
来自您的 CLR 包装器属性。SetCurrentValue
在您需要属性来获取给定值但不想覆盖已针对您的属性配置的任何绑定、触发器或样式的情况下更有用。
演示线束(完整):
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
讨论:
DependencyProperty
具有DefaultValue
“42”的缺席的初始状态具有BaseValueSource.Default
声明的标志。ReadLocalValue()
返回全局单例实例DependencyProperty.UnsetValue
。
SetValue()
BaseValueSource.Local
按预期在内部存储一个值。
用于存储恰好SetValue
等于 的值DefaultValue
不会恢复BaseValueSource.Default
状态(与下面的 #6 相比)。
相反,如果您想删除任何/所有存储值或绑定并将 DP 恢复为原始状态,请调用ClearValue()
. (见下面的注释)
使用SetCurrentValue()
,属性值是通过强制生成的,而不是断言BaseValueSource.Local
模式。请注意,尽管属性现在报告的值实际上不等于它的 ,但之前的BaseValueSource
Default仍然占上风。DefaultValue
重要提示:
这意味着检查stateBaseValueSource
返回的值不是判断当前属性值是否等于 DP 元数据中的默认值的可靠指标。GetValueSource()
BaseValueSource.Default
另一方面——与上面的#3 不同——SetCurrentValue
确实检查了 DP 元数据的相等性DefaultValue
,为了修剪值,它因此也认为冗余是“不必要的”。这种急切的清理可能旨在缓解 DP 存储膨胀,但它也使 DP 状态透明性与特殊情况的“取消屏蔽”行为复杂化,如果不彻底理解,可能会导致模糊的错误。例如,#6 将 DP 清除回原始状态,与ClearValue()
...
...但前提是先前存储的BaseValueSource
isCurrent
和 notLocal
; 将 #5/#6 与 #7 对进行比较,尽管报告的属性值相同,但内部状态标志差异很大。
关于ClearValue()
:
很明显,不会为最终不会导致属性先前值发生变化的PropertyChangedCallback
任何操作调用。SetValue()
它是基本的,因为SetValue
带有一个隐含的假设,即持续的变化与工作中的活动财产的价值有关。不太直观的是,同样的逻辑也适用ClearValue()
。
例如,在 #4 中,ClearValue
导致本地值42
从内部 DP 存储中删除,以及其他内部状态更改,所有这些都符合预期。问题在于,在当前调用期间OnPropertyChanged
是否(或不)被调用取决于(或不)先前的值是否恰好等于元数据默认值。由于“清除”操作的语义似乎暗示了对先前状态的汇总丢弃——因此通常认为这在上下文中是任意的——人们可能不会预料到这种不一致,其中 的行为取决于某些/任何先前状态。特别是对于一个重要的行为,它也暗示(并混合)新的ClearValue
ClearValue()
状态,例如是否触发“更改”通知。
除了接受的答案:
我发现这篇文章很好地解释了 SetCurrentValue()。请注意 Dependency Property Value Precedence 系统如何将本地值置于绑定值之上。这解释了评论者的意外行为。