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

问题描述 投票:0回答:1

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

这里是一些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)));
}

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

另外几个要点:

  • 我希望这适用于任何UIElement,而不仅仅是StackPanel
  • Identifier不是视觉树的一部分。它们没有并且不应该具有视觉元素
  • 因为这是一个内部库,所以我希望避免对绑定要求SourceRelativeSource,因为这样做不直观,需要这样做

是否有可能在标记的这一层中绑定到继承的DataContext?我需要手动将它们添加到逻辑树吗?如果是这样,如何?

谢谢!

c# wpf xaml data-binding attached-properties
1个回答
1
投票

除了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>

Sample demo

© www.soinside.com 2019 - 2024. All rights reserved.