为ItemsControl.ItemContainerStyle指定ControlTemplate

21

以下类似于我想要实现的内容。然而,我遇到了错误:

Invalid PropertyDescriptor value.

在模板Setter上。我怀疑是因为我没有为Style指定TargetType; 然而,我不知道ItemsControl的容器类型。

<ItemsControl>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <StackPanel>
                            <TextBlock Text="Some Content Here" />
                            <ContentPresenter />
                            <Button Content="Edit" />
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <!-- heterogenous controls -->
    <ItemsControl.Items> 
        <Button Content="Content 1" />
        <TextBox Text="Content 2" />
        <Label Content="Content 3" />
    </ItemsControl.Items>
</ItemsControl>
2个回答

43

您可以使用类型名称来限定属性名:

<Setter Property="Control.Template">
ItemsControl的容器通常是ContentPresenter,但如果子级是UIElement,则不会使用容器。在这种情况下,所有子元素都是控件,因此ItemContainerStyle将直接应用于它们。如果添加了一个不是UIElement的项,那么该setter将在ContentPresenter上设置Control.Template属性,虽然可以成功但没有效果。
实际上,听起来您想要的是即使它们已经是UIElement,也要将每个子级包装在容器中。要做到这一点,您必须使用ItemsControl的子类。您可以使用像ListBox这样的现有控件,或者您可以子类化ItemsControl并重写GetContainerForItemOverrideIsItemItsOwnContainerOverride来包装自己的容器中的项。您可以将它们包装在ContentControl中,然后将其用作StyleTargetType
public class CustomItemsControl
    : ItemsControl
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ContentControl();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        // Even wrap other ContentControls
        return false;
    }
}

您还需要在ControlTemplate上设置TargetType,以便ContentPresenter将绑定到Content属性:

<ControlTemplate TargetType="ContentControl">

运行得非常顺畅!我一直试图用XAML来完成所有的工作,但只需要几行代码来派生一个类就可以让它变得愉快、整洁和清晰。 - Travis Heseman
1
如果您添加的项不是UIElement,则该setter将在ContentPresenter上设置Control.Template属性,这将成功但没有效果。- 我找了很长时间才找到这个提示! - Daniel

3

如果您只想使用XAML完成所有操作,可以使用ListBox代替ItemsControl,并为ListBoxItem定义样式:

        <ListBox ItemsSource="{Binding Elements.ListViewModels}">
        <ListBox.Resources>
            <Style TargetType="ListBoxItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <StackPanel>
                                <TextBlock>Some Content Here</TextBlock>
                                <ContentPresenter Content="{TemplateBinding Content}" />
                                <Button>Edit</Button>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.Resources>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>

请注意,由于我正在使用ListBox,容器为ListBoxItem(通常,WPF的默认列表控件的容器始终命名为Item),因此我们需要为ListBoxItem创建样式:
```

请注意,由于我正在使用ListBox,容器为ListBoxItem(通常,WPF的默认列表控件的容器始终命名为Item),因此我们需要为ListBoxItem创建样式:

```
<Style TargetType="ListBoxItem">

然后我们为ListBoxItem创建一个新的ControlTemplate。请注意,不使用ContentPresenter是因为它经常出现在文章和教程中,需要将其模板绑定到ListBoxItem的Content属性,这样它就会显示该项的内容。
<ContentPresenter Content="{TemplateBinding Content}" />

我刚刚遇到了同样的问题,并通过以下方式解决了它。我不想使用 ListBox 的某些功能(例如项目选择),通过使用这种技术,项目选择将不再起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接