更改ListBox中最后一项的样式

问题描述 投票:7回答:3

我有列表框控件,其中包含颜色列表。这是代码和图片:

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

图片:

我如何才能像这样更改最后一项的样式:

wpf listbox itemtemplate
3个回答
19
投票

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

变流器

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>

7
投票

为了让这个工作与一个随时间变化的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>

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

这是相应的MultivalueConverter

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();
    }
}

1
投票

停止与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用于纯XAML方法,无论你使用哪种ItemsControl.ItemsSource。我实际上是用AddNoSelectionPlaceholder转换器完成的。)

同样,我更喜欢这个的原因是它将项目,包括“无颜色”项目视为数据,这就是它。更好的是,因为它是数据,它可以让你轻松改变周围的事物。想要“无颜色”项目是第一个吗?只需切换添加它们的顺序即可。

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

要么

yield return new NoColorToken();

foreach (var color in colors)
    yield return color;

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

© www.soinside.com 2019 - 2024. All rights reserved.