部分模板化的ListBox.ItemTemplate

时间:2013-02-11 15:59:30

标签: silverlight silverlight-5.0

我正在创建一个自定义控件,我正在尝试为列表框项创建部分指定的模板。模板有一些预定义的部分,并且在使用控件时应该有另一个可以模板化的部分。

为此,我创建了一个名为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}"

列表框,然后在我使用此控件的任何地方复制/粘贴通用模板。但这是我希望首先避免的行为。

谢谢!

编辑:我为所有代码块使用了代码标记,但由于某些原因它们没有突出显示。 :/

2 个答案:

答案 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>