如何设置子代理相对于 DataContext 的绑定? [英] How do I set the binding of a child proxy relative to the DataContext?

查看:28
本文介绍了如何设置子代理相对于 DataContext 的绑定?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看了很多主题,但它们都以一种或另一种方式与UI元素的DataContext的定义有关.

I looked at a lot of topics, but they all in one way or another are related to the definition of the DataContext of UI elements.

我有一项任务需要采用完全不同的方法.不管我对这个决定有多么困惑,我也想不出什么.

I have a task that requires a completely different approach. And no matter how much I puzzled over the decision, I could not think of anything.

问题描述.
最初,有一个简单的代理:

Description of the problem.
Initially, there is a simple proxy:

using System;
using System.Windows;


namespace Proxy
{
    /// <summary> Provides a <see cref="DependencyObject"/> proxy with
    /// one property and an event notifying about its change. </summary>
    public class Proxy : Freezable
    {
        /// <summary> Property for setting external bindings. </summary>
        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(nameof(Value), typeof(object), typeof(Proxy), new PropertyMetadata(null));

        protected override Freezable CreateInstanceCore()
        {
            throw new NotImplementedException();
        }
    }
}

如果你在任何元素的Resources中设置它,那么它可以通过简单的Binding获取DataContext:

If you set it in the Resources of any element, then it can get the DataContext with a simple Binding:

<FrameworkElement.Resources>
    <proxy:ProxyValue x:Key="proxy"
                      Value="{Binding}"/>
</FrameworkElement.Resources>

同样,任何没有明确指定源的 Bindig 都将使用在其资源中将代理实例声明为源的元素的 DataContext.

Likewise, any Bindig without an explicitly specified source will use the DataContext of the element in whose resources the proxy instance is declared as the source.

子代理注入.
现在,对于某个任务(它的条件与问题无关,所以我不会描述它)我需要一个嵌套(子)代理,它也可以分配一个相对于数据上下文的绑定.
我需要在代码中设置这个绑定.

Child proxy injection.
Now, for a certain task (its conditions are not relevant to the question, so I will not describe it) I needed a nested (child) proxy which can also be assigned a binding relative to the data context.
And I need to set this binding in code.

一个高度简化的演示示例:

A highly simplified example for demonstration:

using System.Windows.Data;

namespace Proxy
{
    public class PregnantProxy : Proxy
    {
        public Proxy Child { get; } = new Proxy();

        public PregnantProxy()
        {
            Binding binding = new Binding();
            BindingOperations.SetBinding(this, ValueProperty, binding);
            BindingOperations.SetBinding(Child, ValueProperty, binding);
        }
    }
}

<StackPanel DataContext="Some data">
    <FrameworkElement.Resources>
        <proxy:PregnantProxy x:Key="proxy"/>
    </FrameworkElement.Resources>
    <TextBlock Text="{Binding}" Margin="10"/>
    <TextBlock Text="{Binding Value, Source={StaticResource proxy}}" Margin="10"/>
    <TextBlock Text="{Binding Child.Value, Source={StaticResource proxy}}" Margin="10"/>
</StackPanel>

父代理绑定按预期工作.
但是链接一个孩子不会返回任何东西.

Parent proxy binding works as expected.
But linking a child will not return anything.

如何为孩子设置正确的绑定?

推荐答案

如果你在任何元素的Resources中设置它,那么它可以通过简单的Binding获取DataContext" - 这是一个严重的错误.资源字典没有 DataContext 继承.你可以很容易地看到它,如果你添加到资源字典中,例如一个 Label 并尝试为其使用绑定(参见下面的示例).

"If you set it in the Resources of any element, then it can get the DataContext with a simple Binding" - this is the crucial mistake. Resource dictionary has not the DataContext inheritance. You can easy see it, if you add to the resource dictionary e.g. a Label and try to use binding for it(see example below).

它适用于 Text={Binding Value, Source={StaticResource proxy}}" 依赖于从 Freezable 类的继承,它找出数据上下文如果我没记错的话,可以使用它 Freezable.ContextList,这是私有的,请参阅 Freezable.此实现不适用于 Child,因为它不在资源字典中.

That it works for Text="{Binding Value, Source={StaticResource proxy}}" lays on inheritance from Freezable class, which finds out data context and use for it if I'm not mistaken Freezable.ContextList, which is private see implementation of Freezable. This implementation doesn't work for Child, since it's not in a resource dictionary.

所以如果你不是从 Freezable 继承,而是从让我们说它是父类 DependencyObject,还有 Text={Binding Value, Source={StaticResource proxy}} 将不起作用.

So if you do inherit not from Freezable, but from let us say it's parent class DependencyObject, also Text="{Binding Value, Source={StaticResource proxy}}" will not work.

我不知道你为什么需要这个结构,它看起来有点奇怪,但是如果你从 FrameworkElement 继承并为它提供一个 DataContext代理及其子元素(在 XAML 中,您可以对其进行硬编码,或者使用 StaticResource 或自定义 MarkupExtension 对其)它可以工作.查看修改后的代码.

I don't know for what you need this construction, it looks for me a kind of weird, but if you inherit from FrameworkElement and provide a DataContext for the proxy and it's child element (in XAML you can hard code it, or use StaticResource or custom MarkupExtension for it) it can work. See modified code.

    public class Proxy : FrameworkElement
    {
        /// <summary> Property for setting external bindings. </summary>
        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(nameof(Value), typeof(object), typeof(Proxy), new PropertyMetadata(null)); 

        //protected override Freezable CreateInstanceCore()
        //{
        //    throw new NotImplementedException();
        //}
    
    }
    public class PregnantProxy : Proxy
    {
        public Proxy Child { get; } = new Proxy();
    
        public PregnantProxy()
        {
            var binding = new Binding() {};
            BindingOperations.SetBinding(this, ValueProperty, binding);
    
            //Child
            this.AddLogicalChild(Child);
            BindingOperations.SetBinding(Child, DataContextProperty, binding);
    
            BindingOperations.SetBinding(Child, ValueProperty, binding);
        }
    }

并在 XAML 中相应地:

and in XAML accordingly:

<StackPanel DataContext="Some data">
    <StackPanel.Resources>
        <local:PregnantProxy x:Key="proxyResBinding"  DataContext="{Binding}"/>
        <local:PregnantProxy x:Key="proxyHardCodedDC"  DataContext="Proxy hardcoded DC"/>

        <Label x:Key="lblResBinding" DataContext="{Binding}"/>
        <Label x:Key="lblHardcoded" DataContext="hard coded DC"/>
    </StackPanel.Resources>

    <Label Content="{Binding}" Background="Yellow" />

    <Label Content="{Binding Child.Value, Source={StaticResource proxyResBinding}}" Background="Red"/>
    <Label Content="{Binding Value, Source={StaticResource proxyResBinding}}" Background="Red"/>
            
    <Label Content="{Binding Child.Value, Source={StaticResource proxyHardCodedDC}}" Background="Green"/>
    <Label Content="{Binding Value, Source={StaticResource proxyHardCodedDC}}" Background="Green"/>

    <Label Content="{Binding DataContext, Source={StaticResource lblResBinding}}" Background="Red"/>
    <Label Content="{Binding DataContext, Source={StaticResource lblHardcoded}}" Background="Green"/>
            
</StackPanel>

这篇关于如何设置子代理相对于 DataContext 的绑定?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆