我有一个以编程方式创建的绑定到某个标准 WPF 控件的 DependencyProperty。我希望在此绑定与属性分离时调用我的函数。
可以将值更改通知附加到属性,但这意味着我需要在实际值更新时重新检查绑定是否仍然存在,如果它确实如此,它可能会影响性能。
相关:BindingExpression 的源更改时的通知?
.
以下是有关我要完成的工作的详细信息:
我正在编写一个类似 WPF Canvas 的控件,该控件具有自定义规则来放置、调整大小和旋转它的子项。
<my:MyCustomCanvas>
<Button my:Name="child control #1" Canvas.Left="10" Canvas.Top="20"/>
<my:SomeOtherControl my:Name="child control #2" Canvas.Left="70" Canvas.Top="80"/>
</my:MyCustomCanvas>
要旋转子控件,我需要像这样设置它们的 RenderTransformProperty:
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
foreach (UIElement child in InternalChildren)
{
var group = new TransformGroup();
var rt = new RotateTransform();
var b = new Binding() { Source = ..., Convertor = ... };
BindingOperations.SetBinding(rt, RotateTransform.AngleProperty, b);
group.Children.Add(rt);
child.RenderTransform = group;
}
}
如果有人在设计器中调整子控件的大小,并且不小心将其宽度或高度设为负值,即使是一瞬间,设计器也会完全删除我的 RenderTransform 以将其替换为 ScaleTransform{ ScaleX = -1 }。如果任何人或任何事物出于任何原因设置或重置子控件的 RenderTransform,也会发生同样的情况。恢复的唯一方法是关闭并重新打开设计器选项卡,这很烦人。
我唯一能做的就是订阅值更改事件并在绑定消失时重新应用绑定:
var dpd = DependencyPropertyDescriptor.FromProperty(RenderTransformProperty, typeof(CustomCanvas));
dpd.AddValueChanged(child, MyValueChangedHandler);
static void MyValueChangedHandler(object sender, EventArgs e)
{
if ( ! /*do checks to see if my bindings are still untouched*/ )
BindEverythingAgain();
}
这可行,但在性能方面可能不是一个好主意,考虑到我在屏幕上最多有 1000 个控件,每个控件每秒更新大约 60 次 - 我必须为所有控件请求绑定路径以正确检查它们是否没有被覆盖.
属性未绑定时是否没有要监听的事件?
查看参考源,我发现当依赖属性数据源发生更改时,会调用Expression.OnDetach() 。但是,由于 BindingExpression 和 MultiBindingExpression 都是密封的,所以我需要将 BindingExpressionBase 子类化,这太麻烦了。
我还发现BindingExpression.Status属性是在绑定分离时设置的。因此,应该可以维护一个 BindingExpressions 列表并定期检查,这在性能方面可能会更好。这并不是一个真正干净的解决方案,因为检查需要在计时器上运行,并且不能在值更改事件中完成,因为这需要在已知的列表中查找绑定表达式,这并不是一个真正的快速操作,即使这样的列表是排序的。