Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么到祖先的绑定比按名称绑定到元素或绑定到DataContext的绑定更晚激活?_C#_Wpf_Binding_Relativesource - Fatal编程技术网

C# 为什么到祖先的绑定比按名称绑定到元素或绑定到DataContext的绑定更晚激活?

C# 为什么到祖先的绑定比按名称绑定到元素或绑定到DataContext的绑定更晚激活?,c#,wpf,binding,relativesource,C#,Wpf,Binding,Relativesource,我在尝试在代码中短期设置绑定时注意到了这一点。实际上,我只想获得绑定提供的值。所以我设置绑定,获取目标属性的值,并立即清除绑定。在为绑定设置带有模式FindAncestor的RelativeSource之前,一切都很好。在这种情况下,目标属性返回其默认值 经过一些调试后,我发现FindAncestor绑定的BindingExpression的属性状态设置为Unattached。对于其他类型的绑定BindingExpression.Status设置为Active 我已经写了一些代码来说明这一点 W

我在尝试在代码中短期设置绑定时注意到了这一点。实际上,我只想获得绑定提供的值。所以我设置绑定,获取目标属性的值,并立即清除绑定。在为绑定设置带有模式FindAncestor的RelativeSource之前,一切都很好。在这种情况下,目标属性返回其默认值

经过一些调试后,我发现FindAncestor绑定的BindingExpression的属性状态设置为Unattached。对于其他类型的绑定BindingExpression.Status设置为Active

我已经写了一些代码来说明这一点

Window1.xaml

<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内容”

因此,我的问题是:

  • 为什么RelativeSourceMode.FindAncestor绑定不提供 值。是否调用SetBinding(…)

  • 有没有办法强制这种绑定更新目标 财产?我试图调用bindingExpression.UpdateTarget()-它 不像预期的那样工作


  • 这是设计的。为了理解原因,让我们看看代码

    表达式
    被设置为
    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)));
    }