【问题标题】:Use different template for last item in a WPF itemscontrol对 WPF 项目控件中的最后一项使用不同的模板
【发布时间】:2026-02-22 12:50:01
【问题描述】:

我在我的 itemscontrol 中使用自定义模板来显示以下结果:

item 1, item 2, item3,

我想更改最后一项的模板,结果变成:

item 1, item2, item3

ItemsControl:

<ItemsControl ItemsSource="{Binding Path=MyCollection}">

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>

            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Name}"/>
                <TextBlock Text=", "/>
            </StackPanel>

        </DataTemplate>
    </ItemsControl.ItemTemplate>

</ItemsControl>

有没有人可以为我的问题提供解决方案?谢谢!

【问题讨论】:

    标签: wpf templates itemscontrol


    【解决方案1】:

    我找到了仅使用 XAML 的问题的解决方案。如果有人需要这样做,请使用:

    <ItemsControl ItemsSource="{Binding Path=MyCollection}">
    
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    
        <ItemsControl.ItemTemplate>
            <DataTemplate>
    
                <StackPanel Orientation="Horizontal">
                    <TextBlock x:Name="comma" Text=", "/>
                    <TextBlock Text="{Binding}"/>
                </StackPanel>
    
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
                        <Setter TargetName="comma" Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
    
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    
    </ItemsControl>
    

    【讨论】:

    • +1 不错的简单解决方案。但是需要注意的是,如果对数据绑定集合进行任何更改(排序、过滤、添加或删除),“DataTrigger”将不会再次触发,DataTemplate 也不会相应更新。
    • 对于那些来到这里根据初始问题(以及错误链接到此处的其他类似问题)寻找答案的人的说明,这不是您要寻找的。此解决方案更改了第一个项目的模板,而不是最后一个项目的模板。
    • jpwkeeper 是完全正确的,但这是我的问题的一个该死的性感解决方案!
    • 但请注意,PreviousData 对于具有大量项目的控件结果非常慢,将其用于 1000 个项目是不可用的。
    • 此处显示了使用 PreviousData 的替代方法:*.com/a/34138980/3792603 - 此答案使用 AlternationIndex
    【解决方案2】:

    你可以使用DataTemplateSelector,在SelectTemplate()方法中你可以检查item是否是最后一个,然后返回另一个模板。

    在 XAML 中:

    <ItemsControl.ItemTemplate>     
      <DataTemplate>
          <ContentPresenter 
                 ContentTemplateSelector = "{StaticResource MyTemplateSelector}">
    

    在后面的代码中:

     private sealed class MyTemplateSelector: DataTemplateSelector
     { 
    
        public override DataTemplate SelectTemplate(
                                          object item, 
                                          DependencyObject container)
        {
            // ...
        }
      }
    

    【讨论】:

    • 使用提供的参数(项目和容器),我无法确定该项目是否是集合中的最后一个。
    • 您可以找到父 ItemsControl 容器使用 FindParentOfType&lt;TParent&gt; 方法(google intrawebs),然后使用 AlternationIndex,参见 * 示例
    【解决方案3】:

    此解决方案会影响最后一行并随着基础集合的更改而更新:


    代码隐藏

    转换器需要 3 个参数才能正常工作 - 当前项、itemscontrol、itemscount,如果当前项也是最后一项,则返回 true:

      class LastItemConverter : IMultiValueConverter
        {
    
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                int count = (int)values[2];
    
                if (values != null && values.Length == 3 && count>0)
                {
                    System.Windows.Controls.ItemsControl itemsControl = values[0] as System.Windows.Controls.ItemsControl;
                    var itemContext = (values[1] as System.Windows.Controls.ContentPresenter).DataContext;
                
                    var lastItem = itemsControl.Items[count-1];
    
                    return Equals(lastItem, itemContext);
                }
    
                return DependencyProperty.UnsetValue;
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    

    XAML

    DataTemplate 的数据触发器,包括一个名为“PART_TextBox”的文本框:

      <DataTemplate.Triggers>
                <DataTrigger Value="True" >
                    <DataTrigger.Binding>
                        <MultiBinding Converter="{StaticResource LastItemConverter}">
                            <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}" />
                            <Binding RelativeSource="{RelativeSource Self}"/>
                            <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}" Path="Items.Count"/>
                        </MultiBinding>
                    </DataTrigger.Binding>
                    <Setter Property="Foreground" TargetName="PART_TextBox" Value="Red" />
                </DataTrigger>
     </DataTemplate.Triggers>      
    

    转换器作为 Xaml 中的静态资源

    <Window.Resources>
         <local:LastItemConverter x:Key="LastItemConverter" />
    </Window.Resources>
    

    快照

    以及它在行动中的快照

    代码已经从这个'codeproject'添加到itemscontrol https://www.codeproject.com/Articles/242628/A-Simple-Cross-Button-for-WPF

    注意最后一项的红色文字

    【讨论】:

      【解决方案4】:

      一个问题...我看到您使用的是 ItemsControl 而不是 ListBox 并且它似乎绑定到字符串集合,并且您只是想显示结果没有格式化各个部分的文本,这让我想知道您想要的输出是否实际上是问题中提到的字符串本身,而不是实际的 ItemsControl 本身。

      如果我是正确的,您是否考虑过只使用绑定到项目集合的简单TextBlock,但通过转换器馈送?然后在转换器内部,您将value 转换为字符串数组,然后在Convert 方法中,只需Join 它们使用逗号作为分隔符,这将自动,仅在元素之间添加它们,就像这样.. .

      var strings = (IEnumerable<String>)value;
      
      return String.Join(", ", strings);
      

      【讨论】:

        最近更新 更多