绑定到 WPF 中 Attached 属性 中的嵌套元素

Binding to Nested Element in Attached Property in WPF

我正在尝试将嵌套在附加 属性 内的元素绑定到我的 DataContext,但问题是附加的 属性 不是逻辑树的一部分,并且因此没有正确设置或绑定到父对象的数据上下文。依赖项 属性,在本例中 Value,始终为空。

这是一些例子XAML

<StackPanel>
    <!-- attached property of static class DataManager -->
    <local:DataManager.Identifiers>
        <local:TextIdentifier Value="{Binding Path=MyViewModelString}" />
        <local:NumericIdentifier Value="{Binding Path=MyViewModelInt}" />      
        <local:NumericIdentifier Value="{Binding Path=SomeOtherInt}" />
    </local:DataIdentifiers>
    <!-- normal StackPanel items -->
    <Button />
    <Button />
</StackPanel>

由于实施原因,这不能是单个附加的 属性 - 它需要是允许 n 个实体的集合。另一个可接受的解决方案是将标识符直接放在节点中,但我认为如果不在逻辑树中明确包含这些元素,这种语法是不可能的。即...

<Button>
    <local:NumericIdentifier Value="{Binding}" />
    <local:TextIdentifier Value="{Binding}" />
    <TextBlock>Actual button content</TextBlock>
</Button>

DataManager.

的执行开始
[ContentProperty("IdentifiersProperty")]
public static class DataManager
{
    public static Collection<Identifier> GetIdentifiers(DependencyObject obj)
    {
        return (Collection<Identifier>)obj.GetValue(IdentifiersProperty);
    }

    public static void SetIdentifiers(DependencyObject obj, Collection<Identifier> value)
    {
        obj.SetValue(IdentifiersProperty, value);
    }

    public static readonly DependencyProperty IdentifiersProperty =
        DependencyProperty.RegisterAttached("Identifiers", typeof(Collection<Identifier>), typeof(DataManager), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIdentifiersChanged)));
}

我试过使基础 class Identifiers 实现 Freezable 希望它能用于数据和绑定上下文的继承,但没有任何效果(可能是因为它嵌套在另一层内 - 附加的 属性)。

还有几个要点:

是否可以在标记的这一层绑定到继承的DataContext?我需要手动将它们添加到逻辑树吗?如果是,怎么做?

谢谢!

除了让 Identifier 继承自 Freezable 之外,您还需要使用 FreezableCollection 而不是 Collection<Identifier> 作为附加的 属性 类型。这将确保继承链不被破坏。

public class Identifier : Freezable
{
    ... // dependency properties 

    protected override Freezable CreateInstanceCore()
    {
        return new Identifier();
    }
}

创建自定义集合:

public class IdentifierCollection : FreezableCollection<Identifier> { }

并且,修改附件 属性 以使用此集合:

[ContentProperty("IdentifiersProperty")]
public static class DataManager
{
    public static readonly DependencyProperty IdentifiersProperty =
                    DependencyProperty.RegisterAttached(
                            "Identifiers",
                            typeof(IdentifierCollection),
                            typeof(DataManager),
                            new FrameworkPropertyMetadata(OnIdentifiersChanged));

    ...

    public static void SetIdentifiers(UIElement element, IdentifierCollection value)
    {
        element.SetValue(IdentifiersProperty, value);
    }
    public static IdentifierCollection GetIdentifiers(UIElement element)
    {
        return element.GetValue(IdentifiersProperty) as IdentifierCollection;
    }
}

示例用法:

<Window.DataContext>
    <local:TestViewModel 
        MyViewModelInt="123"
        MyViewModelString="Test string"
        SomeOtherInt="345" />
</Window.DataContext>

<StackPanel x:Name="ParentPanel" ... >
    <!-- attached property of static class DataManager -->
    <local:DataManager.Identifiers>
        <local:IdentifierCollection>
            <local:TextIdentifier Value="{Binding Path=MyViewModelString}" />
            <local:NumericIdentifier Value="{Binding Path=MyViewModelInt}" />
            <local:NumericIdentifier Value="{Binding Path=SomeOtherInt}" />
        </local:IdentifierCollection>
    </local:DataManager.Identifiers>
    <!-- normal StackPanel items -->

    <TextBlock Text="{Binding Path=(local:DataManager.Identifiers)[0].Value, 
        ElementName=ParentPanel, StringFormat=Identifer [0]: {0}}" />
    <TextBlock Text="{Binding Path=(local:DataManager.Identifiers)[1].Value, 
        ElementName=ParentPanel, StringFormat=Identifer [1]: {0}}" />
    <TextBlock Text="{Binding Path=(local:DataManager.Identifiers)[2].Value, 
        ElementName=ParentPanel, StringFormat=Identifer [2]: {0}}" />

</StackPanel>