更改ListBox中最后一个项目的样式。

9

我有一个列表框控件,其中列出了一些颜色。以下是代码和图片:

<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>

图片:

image

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

image


1
您可以尝试使用基于“RelativeSource PreviousData”的触发器,例如此答案 - Rachel
1
如果那么容易,我就不会在这里问了。与其给我负分(给我负分的人),他/她需要先进行验证。我的列表框项目不是静态的。它取决于我的应用程序的皮肤颜色,并显示该皮肤颜色的所有不同阴影。 - user1535848
有没有办法与当前数据进行比较,而不是使用 Binding="{Binding RelativeSource={RelativeSource PreviousData }}"?我尝试了 self 但不起作用。 - user1535848
3个回答

22
通过转换器可以实现这一点,它会查找列表框中是否为最后一项。 转换器
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>

4
每当ItemsControl更改(添加/删除项),我该如何使绑定更新? - Clement Hoang
1
我还没能让它正常工作。由于某些原因,每当转换器被调用时,列表总是为空的。 - Goose
如果一次只向项源添加一个项目,则此方法不起作用。 - Matt Becker
在这种情况下,“Self”不是一个依赖对象。你应该使用“TemplatedParent”作为相对源进行绑定。 - Lucas

8
为了让这个与时间变化的ListBox正常工作,我最终使用了MultiBinding:
<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>

注意: 第二个绑定仅用于在列表更改时接收通知

这是相应的多值转换器

public class IsLastItemInContainerConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        DependencyObject item = (DependencyObject)values[0];
        ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);

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

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我不确定你需要使用多重绑定,因为转换器仅使用了其中的第一个值。我猜您可能一开始使用了两个值,后来才发现如何仅使用第一个值... - TonyT_32909023190
@TonyT 这个答案说:“注意:第二个绑定仅用于在列表更改时收到通知”。 - Adam Bartha

3

别再搞乱UI了...它只是数据!!!

个人认为,最简单的方法是使用CompositeCollection(或自定义枚举器)。这种思考方式的优点在于,它将其正确地作为数据分离出来,而不是搞乱自定义UI绑定/相对源等。

我来解释一下。

假设您要显示存储在myColors集合中的“x”个动态生成的颜色,然后是某些表示“无颜色”的东西(带有线条的框)。

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

class NoColorToken{}

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

您甚至可以将其更通用,称之为NoSelectionToken,可用于任何类型的列表。只需确保将DataTemplate限定在特定位置的使用范围内(例如此示例中没有颜色)。

然后,在您的代码中,只需将颜色填充到CompositeCollection中,然后跟随NoColorToken类的实例,如下所示:

var colorsAndToken = new CompositeCollection();
colorsAndToken.Add(new CollectionContainer(myColors));
colorsAndToken.Add(new NoColorToken());

itemsControl.ItemsSource = colorsAndToken;

如果MyColors是可观察的,对它进行的任何更改都会自动更新UI界面。

如果不需要对其进行单独添加或删除(即没有可观察性需求),那么可以通过编写一个枚举器函数来更加简化操作(本质上就是CompositeCollection内部所执行的简化基础操作)。

IEnumerable ColorsWithToken(IEnumerable colors){

    foreach (var color in colors)
        yield return color;

    yield return new NoColorToken();
}

itemsControl.ItemsSource = ColorsWithToken(myColors);

不过,自定义枚举器的方法无法跟踪myColors的更改。 如果myColors发生更改,您必须重新分配ItemsSource。然而,如果您采用CompositeCollection的方法,它会自动处理更新,只是需要一个新对象CompositeCollection,但这正是它存在的目的。

顺便说一下,您还可以将上述内容包装在一个转换器中,该转换器为您处理任何一种方法,返回枚举器或CompositeCollection,从而实现了对应用于它的任何ItemsControl.ItemsSource的纯XAML方法。 实际上,我就是用一个AddNoSelectionPlaceholder转换器做到了这一点。

再次强调,我喜欢这种方法的原因是它将项目(包括“无颜色”项)视为数据(它本来就是),这使得您可以轻松地对它们进行更改。想让“无颜色”的项首先显示? 只需切换添加它们的顺序即可。

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

或者

yield return new NoColorToken();

foreach (var color in colors)
    yield return color;

现在,它只是数据。在数据模板、控件、绑定或任何其他地方都不需要进行任何“聪明”的操作。更好的是,现在这些操作也可以进行完全单元测试,无需使用UI。


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