我正在尝试将嵌套在附加属性内的元素绑定到我的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
不是视觉树的一部分。它们没有并且不应该具有视觉元素Source
或RelativeSource
,因为这样做不直观,需要这样做是否有可能在标记的这一层中绑定到继承的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>