我正在创建一个自定义控件,我正在尝试为列表框项创建部分指定的模板。模板有一些预定义的部分,并且在使用控件时应该有另一个可以模板化的部分。
为此,我创建了一个名为SuggestionItemTemplate的依赖项属性,如下所示:
public static readonly DependencyProperty SuggestionItemTemplateProperty =
DependencyProperty.Register("SuggestionItemTemplate",
typeof(DataTemplate),
typeof(AutoSuggestTextBox),
new PropertyMetadata(null));
在我的自定义控件'generic.xaml中,我有:
<Style TargetType="local:AutoSuggestTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AutoSuggestTextBox">
<Grid>
<ListBox x:Name="ItemsControl">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
ContentTemplate="{TemplateBinding SuggestionItemTemplate}"
Content="{Binding}" />
<ToggleButton Grid.Column="1"
x:Name="DetailsHover"
ClickMode="Hover"
Style="{StaticResource DetailsToggleButtonStyle}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
不幸的是,这不起作用,因为无法在嵌套到DataTemplate中的ContentPresenter中使用TemplateBinding。 (成员“SuggestionItemTemplate”无法识别或无法访问。)
我还尝试使用祖先绑定(在Silverlight 5中可用),如:
<ContentPresenter Grid.Column="0"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:AutoSuggestTextBox}, Path=SuggestionItemTemplate}"
Content="{Binding}" />
但这会导致绑定错误:
Error: System.Exception: BindingExpression_CannotFindAncestor
我想这是因为我在自定义控件的ControlTemplate中,并且“local:AutoSuggestTextBox”未在样式中的任何位置定义。
我尝试的第三个选项是在OnApplyTemplate覆盖中应用ContentTemplate,但这也不起作用:
var cp = itemsControlElement.ItemTemplate.LoadContent() as ContentPresenter;
cp.ContentTemplate = SuggestionItemTemplate;
在所有情况下,我的网格都有两列,切换按钮可见,但内容展示者简单打印出视图模型的类型名称。 (我相信如果ContentTemplate为null,这是默认行为。)
这甚至可以吗?是否有其他方法可以指定部分模板,然后只在必要时添加自定义模板部件?
作为现在的解决方法,我可以指定
ItemTemplate="{TemplateBinding SuggestionItemTemplate}"
列表框,然后在我使用此控件的任何地方复制/粘贴通用模板。但这是我希望首先避免的行为。
谢谢!
编辑:我为所有代码块使用了代码标记,但由于某些原因它们没有突出显示。 :/
答案 0 :(得分:0)
可以在OnApplyTemplate方法中遍历Visual Ancestors,找到您的ContentPresenter并在其上设置ItemTemplate。在我看来,这对于单个项目来说很好,但在ItemsControl场景中并没有那么多。
使用自己的自定义控件后,您可以实现自己的目标。只需给它一个Object类型的Content依赖项属性,以及一个DataTemplate类型的模板DP(如果你想要的话,可以是两个的多个),你可以为你的Control设置默认样式的根视觉样式和模板。 p>
在这种特定情况下,我建议最好的方法是将ToggleButton放在ListBoxItem模板中,而不是通过自定义ListBox.ItemContainerStyle。使用Expression Blend很容易修改默认控件模板,而ToggleButton的DataContext也不会改变,因此对您自己的逻辑的更改应该是最小的。
修改 如果您想使用多种不同的数据模板,那么Implicit Data Templates可能会更合适。
答案 1 :(得分:0)
我设法使用不同的方法来解决这个问题。我使用了祖先绑定,但我没有尝试从AutoSuggestTextBox
到达根控件(我的DataTemplate
),而是要求引用我的ListBox
(此处命名为ItemsControl
)
但是,由于ListBox
没有SuggestionItemTemplate
属性,因此我将其分为我自己的CustomListBox
,我在那里实现了该属性。这一切都归结为这段代码:
<Style TargetType="local:AutoSuggestTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AutoSuggestTextBox">
<Grid>
<local:CustomizableListBox x:Name="ItemsControl"
SuggestionItemTemplate="{TemplateBinding SuggestionItemTemplate}">
<local:CustomizableListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:CustomizableListBox}, Path=SuggestionItemTemplate}"
Content="{Binding}" />
<ToggleButton Grid.Column="1"
x:Name="DetailsHover"
ClickMode="Hover"
Style="{StaticResource DetailsToggleButtonStyle}" />
</Grid>
</DataTemplate>
</local:CustomizableListBox.ItemTemplate>
</local:CustomizableListBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>