在ItemsControl中设置特定项类型的样式

时间:2014-04-15 11:53:49

标签: c# wpf

我有一个ItemsControl,用于在画布上绘制两组不同的形状。因此,我有两个ItemsSource包含Edge对象和Node对象。

我对每种类型都有两个不同的DataTemplates。但是,我需要为节点设置画布定位,而不是为边缘设置画布定位。互联网上有大量关于如何使用单个ItemsSource执行此操作的示例,但在我的情况下没有多个示例。

我已经像这样攻击了它,但是这会在输出窗口中引发很多绑定错误(因为只有节点具有Position属性,而不是边缘,因此这“有效”。另外,我想分别为节点和边缘设置ZIndex,这是不可能的。有没有人有任何建议?

<ItemsControl>
    <ItemsControl.ItemsSource>
        <MultiBinding>
            <MultiBinding.Converter>
                <p:CompositeCollectionConverter/>
            </MultiBinding.Converter>
            <Binding Path="Graph.Nodes"/>
            <Binding Path="Graph.Edges"/>
        </MultiBinding>
    </ItemsControl.ItemsSource>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type model:Edge}">
            <Path 
                Stroke="Blue"
                Data="{Binding Path=EdgeSegments, Converter={StaticResource EdgeSegmentsConverter}}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type model:Node}">
            <Ellipse 
                Width="8" 
                Height="8" 
                Stroke="Black"
                Fill="Gray"/>
        </DataTemplate>
    </ItemsControl.Resources>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left">
                <Setter.Value>
                    <Binding Path="Position.X">
                        <Binding.Converter>
                            <p:NodePositionConverter />
                        </Binding.Converter>
                    </Binding>
                </Setter.Value>
            </Setter>
            <Setter Property="Canvas.Top">
                <Setter.Value>
                    <Binding Path="Position.Y">
                        <Binding.Converter>
                            <p:NodePositionConverter />
                        </Binding.Converter>
                    </Binding>
                </Setter.Value>
            </Setter>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

3 个答案:

答案 0 :(得分:1)

为什么不使用ItemContainerStyleSelector?将样式添加到ItemsControl.Resources

<Style TargetType="ContentPresenter" x:Key="{x:Type model:Edge}">
    <Setter Property="ZIndex">
        ...
    </Setter>
</Style>
<Style TargetType="ContentPresenter" x:Key="{x:Type model:Node}">
    <Setter Property="ZIndex">
        ...
    </Setter>
    <Setter Property="Canvas.Top">
        ...
    </Setter>
    <Setter Property="Canvas.Left">
        ...
    </Setter>
</Style>

请注意x:Key设置为类型,以便我们可以在样式选择器中轻松查找item.GetType()

    public override Style SelectStyle(object item, DependencyObject container) {
        var containerElement = (FrameworkElement)container;
        var style = containerElement.TryFindResource(item.GetType()) as Style;
        if (style != null) {
            return style;
        }

        return base.SelectStyle(item, container);
    }

答案 1 :(得分:0)

你在做什么有点奇怪..我不知道你是否真的需要这样做...

你不能把你的两个List合并成一个简单的List吗?像这样:

List<AChild> a;
List<BChild> b;

List<Mother> ab = a.Concat(b).Cast<Mother>();

在您的视图中,您可以使用TemplateSelector来帮助您选择适合该项目的DataTemplate。

<ItemsControl ItemTemplateSelector="{StaticResource YourTemplateSelector}" ItemsSource="{Binding ab}"/>

答案 2 :(得分:0)

Rachel在以下StackOverflow主题中给出了答案:https://stackoverflow.com/a/7931448/970589

所以对她赞不绝口。答案的简短摘要: 使用转换器检查调用绑定的类型,并根据该类型返回一个值。