C# 为什么到祖先的绑定比按名称绑定到元素或绑定到DataContext的绑定更晚激活?
我在尝试在代码中短期设置绑定时注意到了这一点。实际上,我只想获得绑定提供的值。所以我设置绑定,获取目标属性的值,并立即清除绑定。在为绑定设置带有模式FindAncestor的RelativeSource之前,一切都很好。在这种情况下,目标属性返回其默认值 经过一些调试后,我发现FindAncestor绑定的BindingExpression的属性状态设置为Unattached。对于其他类型的绑定BindingExpression.Status设置为Active 我已经写了一些代码来说明这一点 Window1.xamlC# 为什么到祖先的绑定比按名称绑定到元素或绑定到DataContext的绑定更晚激活?,c#,wpf,binding,relativesource,C#,Wpf,Binding,Relativesource,我在尝试在代码中短期设置绑定时注意到了这一点。实际上,我只想获得绑定提供的值。所以我设置绑定,获取目标属性的值,并立即清除绑定。在为绑定设置带有模式FindAncestor的RelativeSource之前,一切都很好。在这种情况下,目标属性返回其默认值 经过一些调试后,我发现FindAncestor绑定的BindingExpression的属性状态设置为Unattached。对于其他类型的绑定BindingExpression.Status设置为Active 我已经写了一些代码来说明这一点 W
<Window x:Class="Wpf_SetBindingInCode.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="Window1"
Height="300" Width="300"
DataContext="DataContext content">
<StackPanel>
<Button Content="Set binding" Click="SetBindingButtonClick"/>
<TextBlock x:Name="TextBlock1"/>
<TextBlock x:Name="TextBlock2"/>
<TextBlock x:Name="TextBlock3"/>
</StackPanel>
</Window>
上面的代码生成以下输出:
TextBlock1.Text = ""
TextBlock2.Text = "DataContext content"
TextBlock3.Text = "DataContext content"
bindingExpressionBase1.Status = Unattached
bindingExpressionBase2.Status = Active
bindingExpressionBase3.Status = Active
但尽管如此,表单上的所有三个文本块都有预期值——“DataContext内容”
因此,我的问题是:
这是设计的。为了理解原因,让我们看看代码 当
表达式
被设置为dependencProperty
的值时,调用表达式.OnAttach
。此方法在BindingExpressionBase
类()中被重写:
AttachOverride
方法也是虚拟的,它在BindingExpression
()中被重写
在列出的代码中,我们可以看到,在所有操作之后,BindingExpression
仍然可以Unattached
。让我们看看为什么在我们的情况下是这样。为此,我们需要确定状态更改的位置。这可以通过IL Spy完成,它显示状态在AttachToContext
()中更改
评论中说,某些功能至少需要一个布局过程,其中一个是RelativeSource
和祖先查找()
需要内部布尔树文本(DependencyObject目标)
{
返回ProtectedTreeContextIsRequired(目标);
}
///如果ObjectRef确实需要树上下文,则为true
受保护覆盖布尔ProtectedTreeContextIsRequired(DependencyObject目标)
{
返回((_relativeSource.Mode==RelativeSourceMode.FindAncestor
||(_relativeSource.Mode==RelativeSourceMode.PreviousData));
}
由于相对资源
需要树上下文,因此绑定表达式
是未附加的
。因此属性值不会立即更新
在任何
UIElement
上调用UpdateLayout
,强制布局更新和附加BindingExpression
。这是设计的。为了理解原因,让我们看看代码
当表达式
被设置为dependencProperty
的值时,调用表达式.OnAttach
。此方法在BindingExpressionBase
类()中被重写:
AttachOverride
方法也是虚拟的,它在BindingExpression
()中被重写
在列出的代码中,我们可以看到,在所有操作之后,BindingExpression
仍然可以Unattached
。让我们看看为什么在我们的情况下是这样。为此,我们需要确定状态更改的位置。这可以通过IL Spy完成,它显示状态在AttachToContext
()中更改
评论中说,某些功能至少需要一个布局过程,其中一个是RelativeSource
和祖先查找()
需要内部布尔树文本(DependencyObject目标)
{
返回ProtectedTreeContextIsRequired(目标);
}
///如果ObjectRef确实需要树上下文,则为true
受保护覆盖布尔ProtectedTreeContextIsRequired(DependencyObject目标)
{
返回((_relativeSource.Mode==RelativeSourceMode.FindAncestor
||(_relativeSource.Mode==RelativeSourceMode.PreviousData));
}
由于相对资源
需要树上下文,因此绑定表达式
是未附加的
。因此属性值不会立即更新
在任何
UIElement
上调用UpdateLayout
,强制布局更新和附加BindingExpression
。这是设计的。为了理解原因,让我们看看代码
当表达式
被设置为dependencProperty
的值时,调用表达式.OnAttach
。此方法在BindingExpressionBase
类()中被重写:
AttachOverride
方法也是虚拟的,它在BindingExpression
()中被重写
在列出的代码中,我们可以看到,在所有操作之后,BindingExpression
仍然可以Unattached
。让我们看看为什么在我们的情况下是这样。为此,我们需要确定状态更改的位置。这可以通过IL Spy完成,它显示状态在AttachToContext
()中更改
评论中说,某些功能至少需要一个布局过程,其中一个是RelativeSource
和祖先查找()
需要内部布尔树文本(DependencyObject目标)
{
返回ProtectedTreeContextIsRequired(目标);
}
///如果ObjectRef确实需要树上下文,则为true
受保护覆盖布尔ProtectedTreeContextIsRequired(DependencyObject目标)
{
返回((_relativeSource.Mode==RelativeSourceMode.FindAncestor
||(_relativeSource.Mode==RelativeSourceMode.PreviousData));
}
因为树上下文是
TextBlock1.Text = ""
TextBlock2.Text = "DataContext content"
TextBlock3.Text = "DataContext content"
bindingExpressionBase1.Status = Unattached
bindingExpressionBase2.Status = Active
bindingExpressionBase3.Status = Active
internal sealed override void OnAttach(DependencyObject d, DependencyProperty dp)
{
if (d == null)
throw new ArgumentNullException("d");
if (dp == null)
throw new ArgumentNullException("dp");
Attach(d, dp);
}
internal void Attach(DependencyObject target, DependencyProperty dp)
{
// make sure we're on the right thread to access the target
if (target != null)
{
target.VerifyAccess();
}
IsAttaching = true;
AttachOverride(target, dp);
IsAttaching = false;
}
internal override bool AttachOverride(DependencyObject target, DependencyProperty dp)
{
if (!base.AttachOverride(target, dp))
return false;
// listen for InheritanceContext change (if target is mentored)
if (ParentBinding.SourceReference == null || ParentBinding.SourceReference.UsesMentor)
{
DependencyObject mentor = Helper.FindMentor(target);
if (mentor != target)
{
InheritanceContextChangedEventManager.AddHandler(target, OnInheritanceContextChanged);
UsingMentor = true;
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.UseMentor(
TraceData.Identify(this),
TraceData.Identify(mentor)));
}
}
}
// listen for lost focus
if (IsUpdateOnLostFocus)
{
Invariant.Assert(!IsInMultiBindingExpression, "Source BindingExpressions of a MultiBindingExpression should never be UpdateOnLostFocus.");
LostFocusEventManager.AddHandler(target, OnLostFocus);
}
// attach to things that need tree context. Do it synchronously
// if possible, otherwise post a task. This gives the parser et al.
// a chance to assemble the tree before we start walking it.
AttachToContext(AttachAttempt.First);
if (StatusInternal == BindingStatusInternal.Unattached)
{
Engine.AddTask(this, TaskOps.AttachToContext);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.AttachToContext))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.DeferAttachToContext(
TraceData.Identify(this)));
}
}
GC.KeepAlive(target); // keep target alive during activation (bug 956831)
return true;
}
// try to get information from the tree context (parent, root, etc.)
// If everything succeeds, activate the binding.
// If anything fails in a way that might succeed after further layout,
// just return (with status == Unattached). The binding engine will try
// again later. For hard failures, set an error status; no more chances.
// During the "last chance" attempt, treat all failures as "hard".
void AttachToContext(AttachAttempt attempt)
{
// if the target has been GC'd, just give up
DependencyObject target = TargetElement;
if (target == null)
return; // status will be Detached
bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.AttachToContext);
bool traceObjectRef = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.SourceLookup);
// certain features should never be tried on the first attempt, as
// they certainly require at least one layout pass
if (attempt == AttachAttempt.First)
{
// relative source with ancestor lookup
ObjectRef or = ParentBinding.SourceReference;
if (or != null && or.TreeContextIsRequired(target))
{
if (isExtendedTraceEnabled)
{
TraceData.Trace(TraceEventType.Warning,
TraceData.SourceRequiresTreeContext(
TraceData.Identify(this),
or.Identify()));
}
return;
}
}
internal bool TreeContextIsRequired(DependencyObject target)
{
return ProtectedTreeContextIsRequired(target);
}
/// <summary> true if the ObjectRef really needs the tree context </summary>
protected override bool ProtectedTreeContextIsRequired(DependencyObject target)
{
return ( (_relativeSource.Mode == RelativeSourceMode.FindAncestor
|| (_relativeSource.Mode == RelativeSourceMode.PreviousData)));
}