Wpf 更改列表框中最后一项的样式

Wpf 更改列表框中最后一项的样式,wpf,listbox,itemtemplate,Wpf,Listbox,Itemtemplate,我有一个列表框控件,它有颜色列表。以下是代码和图像: <ListBox Name="FillSelections" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Center" SelectedItem="{Binding SelectedColor}" SelectionMode="Single" Style="{StaticResource HorizontalListBoxStyle}" ItemsSou

我有一个列表框控件,它有颜色列表。以下是代码和图像:

<ListBox Name="FillSelections" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Center" SelectedItem="{Binding SelectedColor}" SelectionMode="Single" Style="{StaticResource HorizontalListBoxStyle}" ItemsSource="{Binding FillColors}" ItemTemplate="{StaticResource ColorsItemTemplate}"></ListBox>

 <DataTemplate x:Key="ColorsItemTemplate">
    <Border BorderBrush="Transparent">
        <Rectangle Width="20" StrokeThickness="1" Stroke="Black">
            <Rectangle.Fill>
                <SolidColorBrush Color="{Binding}" />
            </Rectangle.Fill>
        </Rectangle>
    </Border>

图片:

我将如何更改最后一项的样式,仅如下所示:


这可以通过转换器来实现,转换器的工作是查找列表框中的最后一项-

转换器

public class IsLastItemInContainerConverter : IValueConverter
{
   public object Convert(object value, Type targetType,
                         object parameter, CultureInfo culture)
   {
       DependencyObject item = (DependencyObject)value;
       ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);

       return ic.ItemContainerGenerator.IndexFromContainer(item)
               == ic.Items.Count - 1;
   }

   public object ConvertBack(object value, Type targetType,
                             object parameter, CultureInfo culture)
   {
      throw new NotImplementedException();
   }
}
使用它,您可以像这样在xaml类中设置DataTemplate-

<ListBox ItemContainerStyle="{StaticResource ColorsItemStyle}"/>

<Style x:Key="ColorsItemStyle">
  <Style.Triggers>
     <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
       Converter={StaticResource IsLastItemInContainerConverter}}" Value="False">
          <Setter Property="ContentTemplate">
             <Setter.Value>
                 <DataTemplate></DataTemplate> // Your template goes here
             </Setter.Value>
          </Setter>
      </DataTrigger>

     <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
       Converter={StaticResource IsLastItemInContainerConverter}}" Value="True">
          <Setter Property="ContentTemplate">
             <Setter.Value>
                 <DataTemplate></DataTemplate> // Your lastItem template goes here
             </Setter.Value>
          </Setter>
      </DataTrigger>
  </Style.Triggers>
</Style>

//你的模板在这里
//您的lastItem模板位于此处

要在一个随时间变化的列表框中运行此功能,我最终使用了多重绑定:

<DataTemplate x:Key="myItemTemplate">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding}"/>
        <TextBlock x:Name="dots" Text="..."/>
    </StackPanel>
    <DataTemplate.Triggers>
        <DataTrigger Value="False">
            <DataTrigger.Binding>
                <MultiBinding Converter="{StaticResource isLastItemInContainerConverter}">
                    <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBoxItem}" />
                    <Binding Path="Items.Count" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}" />
                </MultiBinding>
            </DataTrigger.Binding>
            <Setter TargetName="dots" Property="Visibility" Value="Collapsed"/>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>
别再搅乱用户界面了。。。这只是数据!!! 就我个人而言,我认为最简单的方法是使用
复合集合
(或自定义枚举器)。这种思维方式的优点是,它将这些数据正确地分离为数据,而不是与自定义UI无意义/绑定/相对源等混为一谈

我会解释的

假设您正在尝试显示存储在
myColors
集合中的动态生成颜色的“x”个数,后跟表示“无颜色”的内容(您的框中有一行)

首先,在应用程序中的某个地方定义一个“无颜色”标记,如下所示

class NoColorToken{}
<DataTemplate DataType="{x:Type ns:NoColorToken}">
    <TextBlock Text="Replace with template representing 'no color'" />
</DataTemplate>
然后定义一个针对该类的
DataTemplate
,如下所示

class NoColorToken{}
<DataTemplate DataType="{x:Type ns:NoColorToken}">
    <TextBlock Text="Replace with template representing 'no color'" />
</DataTemplate>
对MyColor的更改(如果可以观察到)将自动更新UI

只要编写一个枚举器函数(本质上是
CompositeCollection
内部功能的简化基础),就可以让事情变得更加容易,如果它们不需要被观察(即没有单独的添加或删除)

不过,自定义枚举器方法不会跟踪对
myColors
的更改。如果
myColors
发生变化,您必须重新分配
ItemsSource
。但是,如果您使用
CompositeCollection
,它会自动处理更新,只需牺牲一个新对象
CompositeCollection
,但这就是它的用途

顺便说一句,您还可以将上述内容封装在一个转换器中,该转换器为您处理两种方法中的任何一种,即返回枚举数,或返回纯XAML方法的
CompositeCollection
,而不管您将其应用于哪个
ItemsControl.ItemsSource
。事实上,我已经使用
addnoselection占位符
转换器完全做到了这一点。)

同样,我喜欢这样做的原因是它将项目(包括“无颜色”项目)视为数据,这就是它的本质。更好的是,因为它是数据,所以可以让您轻松地改变周围的事物。要首先显示“无颜色”项吗?只需切换添加顺序即可

colorsAndToken.Add(new NoColorToken());
colorsAndToken.Add(new CollectionContainer(myColors));


再说一遍,现在只是数据。在数据模板或控件、绑定或其他任何地方都不需要做任何“聪明”的事情。更好的是,它现在也完全可以进行单元测试。不需要用户界面。

您可能可以使用基于
相对资源先前数据的触发器,就像如果它是那么简单,那么我就不会在这里问这个问题。他/她需要先核实,而不是投反对票(给我分数的人)。我的ListBox项不是静态的。它取决于我的应用程序的肤色,并显示该肤色的所有差异阴影。是否有任何方法可以与当前数据进行比较,而不是Binding=“{Binding RelativeSource={RelativeSource PreviousData}}”。我尝试了self但没有工作。如何在ItemsControl更改时更新绑定?(添加/删除项目)我无法使其正常工作。当出于某种原因调用转换器时,列表始终为空。如果项目一次添加到项目源中,则此操作无效在此情况下,
Self
不是依赖项对象。您应该使用
TemplatedParent
作为相对源进行绑定。我不确定是否需要使用多重绑定,因为转换器仅使用两个值中的第一个。我想你可能是从这两个方面开始的,后来才知道如何只使用第一个值…@TonyT答案是:“注意:第二个绑定只用于在列表更改时收到通知”
colorsAndToken.Add(new NoColorToken());
colorsAndToken.Add(new CollectionContainer(myColors));
yield return new NoColorToken();

foreach (var color in colors)
    yield return color;