为ItemsControl创建可重用的DataTemplate资源

时间:2014-04-23 10:53:51

标签: c# wpf itemscontrol

我目前拥有什么

我目前有一个ItemsControl,用于显示控件列表。由于每个"项目"包含多个控件我通过指定DataTemplate来设置它。像这样的东西(我删除了一些元素属性,使代码更容易遵循):

<ItemsControl x:Name="Items" ItemsSource="{Binding Path=MyItems}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Path Grid.Column="0"/>
                <StackPanel Grid.Column="1" Orientation="Horizontal">
                    <c:MyControl />
                    <c:MyButton />
                </StackPanel>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我想做什么

上面给出了我在功能方面的确切要求,但我在几个地方都有它,我想最小化重复的代码。关于上面的xaml,重用DataTemplate时唯一需要改变的是&#34; MyButton&#34;和&#34; MyControl&#34;。考虑到这一点,我在上面定义XAML的理想方法是这样的:

<ItemsControl x:Name="Items" ItemsSource="{Binding Path=MyItems}">
    <c:MyControl />
    <c:MyButton />
</ItemsControl>

我很高兴当然会有一些变化,但希望很清楚我想要消除的重复。

我尝试了什么

到目前为止,我已经尝试在我的资源文件中创建一个模板,但这并不是很好用,我甚至不确定我是否会走上正轨。这就是我所拥有的:

<DataTemplate x:Key="MyTemplate">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Path Grid.Column="0" Fill="..." Data="..." />
        <StackPanel Grid.Column="1" Orientation="Horizontal">
            <ItemsPresenter />
        </StackPanel>
    </Grid>
</DataTemplate>

我尝试将其应用于我的XAML:

<ItemsControl x:Name="Items" ItemsSource="{Binding Path=MyItems}" ItemTemplate="{StaticResource MyTemplate}">
    <c:MyControl />
    <c:MyButton />
</ItemsControl>

这一切都很好,但在运行期间我收到一个错误:"Items collection must be empty before using ItemsSource."这显然是我不正确的方法的副作用。

我做错了什么?如何设置我的模板以我想要的方式工作?

2 个答案:

答案 0 :(得分:2)

您可以为项容器类型创建一个使用ContentControl(而不是ContentPresenter)的派生ItemsControl类:

public class MyItemsControl : ItemsControl
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ContentControl();
    }
}

现在,您可以将当前的DataTemplate分成一个“外部”可重用部分,该部分位于ContentControl的Template和由剩余DataTemplate定义的“内部”部分:

<!-- somewhere in Resources -->
<Style x:Key="ReusableItemContainerStyle" TargetType="ContentControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ContentControl">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="10"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Path Grid.Column="0" ... />
                    <ContentPresenter Grid.Column="1"
                        Content="{TemplateBinding Content}"
                        ContentTemplate="{TemplateBinding ContentTemplate}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<local:MyItemsControl
    ItemsSource="{Binding MyItems}"
    ItemContainerStyle="{StaticResource ReusableItemContainerStyle}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <c:MyControl />
                <c:MyButton />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</local:MyItemsControl>

更新:您也可以在Generic.xaml中为派生的ItemsControl设置默认样式的可重用ItemContainerStyle,如下所示:

<Style TargetType="local:MyItemsControl"
       BasedOn="{StaticResource {x:Type ItemsControl}}">
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="ContentControl">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ContentControl">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="10"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Path Grid.Column="0" ... />
                                <ContentPresenter Grid.Column="1"
                                    Content="{TemplateBinding Content}"
                                    ContentTemplate="{TemplateBinding ContentTemplate}"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

然后你还必须为ItemsControl设置默认样式键:

public class MyItemsControl : ItemsControl
{
    static MyItemsControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(MyItemsControl),
            new FrameworkPropertyMetadata(typeof(MyItemsControl)));
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ContentControl();
    }
}

答案 1 :(得分:1)

将模板声明为新控件:

<UserControl x:Class="UI.Views.NewControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" Name="myNewControl">
    <Grid>
        <ItemsControl x:Name="Items" ItemsSource="{Binding Path=MyItems}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="10"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Path Grid.Column="0"/>
                        <ContentControl Grid.Row="1" Content="{Binding MyCustomControl, ElementName=myNewControl}"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</UserControl>

MyCustomControl应该是您控件的依赖属性。

如何使用此功能:

<MyNewControl>
   <MyNewControl.MyCustomControl>
        <StackPanel>
             <MyControl/>
             <MyButton/>
        </StackPanel>
   </MyNewControl.MyCustomControl>
</MyNewControl>

希望这有帮助