【问题标题】:Change style of last item in ListBox更改列表框中最后一项的样式
【发布时间】:2020-02-21 13:16:40
【问题描述】:

我有一个包含颜色列表的列表框控件。这是代码和图片:

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

图片:

我怎样才能改变最后一项的样式,就像这样:

【问题讨论】:

  • 您也许可以使用基于RelativeSource PreviousData 的触发器,例如this answer
  • 如果这么简单,我就不会在这里问了。他/她需要先验证,而不是给予减分票(给我的人 - 点)。我的列表框项目不是静态的。这取决于我的应用程序的肤色,并显示该肤色的所有差异阴影。
  • 而不是 Binding="{Binding RelativeSource={RelativeSource PreviousData }}",有什么方法可以与当前数据进行比较。我尝试了自我但没有工作。

标签: wpf listbox itemtemplate


【解决方案1】:

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

转换器

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>

【讨论】:

  • 如何在 ItemsControl 更改时更新绑定? (添加/删除项目)
  • 我无法让它工作。当转换器由于某种原因被调用时,列表总是空的。
  • 如果每次将项目添加到项目源中,这将不起作用
  • Self 在这种情况下不是依赖对象。您应该将TemplatedParent 绑定为相对源。
【解决方案2】:

为了让它与随时间变化的 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();
    }
}

【讨论】:

  • 我不确定您是否需要使用多重绑定,因为转换器仅使用两个值中的第一个。我假设您可能从两者开始,后来想出了如何只使用第一个值...
  • @TonyT 答案是:“注意:第二个绑定仅用于在列表更改时获得通知”
【解决方案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 用于纯 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;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-07
    • 2012-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    相关资源
    最近更新 更多