C# 绑定到WPF中附加属性中的嵌套元素

C# 绑定到WPF中附加属性中的嵌套元素,c#,wpf,xaml,data-binding,attached-properties,C#,Wpf,Xaml,Data Binding,Attached Properties,我试图将嵌套在附加属性内的元素绑定到我的DataContext,但问题是附加属性不是逻辑树的一部分,因此无法正确设置或绑定到父对象的数据上下文。在本例中,值的依赖项属性始终为空 下面是一些XAML示例 <StackPanel> <!-- attached property of static class DataManager --> <local:DataManager.Identifiers> <local:TextI

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

下面是一些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>
我已经尝试过使基类
标识符
实现
可自由化
,希望它能用于数据继承和绑定上下文,但没有任何效果(可能是因为它嵌套在另一层中-附加属性)

还有几个要点:

  • 我希望它能在任何
    UIElement
    上工作,而不仅仅是
    StackPanel
    s
  • 标识符
    不是可视树的一部分。它们没有也不应该有视觉元素
  • 由于这是一个内部库,我希望避免要求绑定使用
    源代码
    相对源代码
    ,因为这样做并不直观
是否可以绑定到此标记层中继承的
DataContext
?我是否需要手动将它们添加到逻辑树中?如果是,怎么做


谢谢

除了让
标识符
继承自
Freezable
,您还需要使用
FreezableCollection
而不是
Collection
作为附加属性类型。这将确保继承链不会断开

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

    protected override Freezable CreateInstanceCore()
    {
        return new Identifier();
    }
}
创建自定义集合:

public class IdentifierCollection : FreezableCollection<Identifier> { }
示例用法:


除了从
Freezable
继承
标识符
之外,还需要使用
FreezableCollection
而不是
集合
作为附加属性类型。这将确保继承链不会断开

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

    protected override Freezable CreateInstanceCore()
    {
        return new Identifier();
    }
}
创建自定义集合:

public class IdentifierCollection : FreezableCollection<Identifier> { }
示例用法:


您是否考虑过对DataManager.Identifiers属性使用多重绑定?它将使用一个IMultiValueConverter返回一个
集合
,这也将解决您如何使用集合实例初始化
标识符
属性的问题。这可能只需要使用一个附加属性就行了,但它还为混合、转换器甚至更多标记添加了另一个要求。我可以试一试,但希望有一个更干净的解决方案。您是否考虑过对DataManager.Identifiers属性使用多重绑定?它将使用一个IMultiValueConverter返回一个
集合
,这也将解决您如何使用集合实例初始化
标识符
属性的问题。这可能只需要使用一个附加属性就行了,但它还为混合、转换器甚至更多标记添加了另一个要求。我可以试一试,但希望有一个更干净的解决方案。
FreezableCollection
是我缺少的链接。这非常有效-谢谢<代码>FreezableCollection是我丢失的链接。这非常有效-谢谢!
[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>