The WPF collection control implements the separator of ItemsControl Separator

  • 2021-09-20 19:49:34
  • OfStack

In the collection control of WPF, it is often necessary to insert a separator style between every collection item, but ItemsControl of WPF has no direct implementation of related functions, so we can only consider saving the nation by curves. After research, we probably think of the following two implementation methods.

Write out the data template for ItemsControl first, as follows:


<ItemsControl ItemsSource="{Binding Source}" BorderThickness="1" BorderBrush="Blue" VerticalAlignment="Stretch">
 <ItemsControl.ItemTemplate>
  <DataTemplate>
   <Grid>
    <Grid.RowDefinitions>
     <RowDefinition Height="Auto" />
     <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Border Name="Bd" Grid.Row="0" Height="1" Background="Red" />
    <TextBlock Grid.Row="1" Text="{Binding}" />
   </Grid>
  </DataTemplate>
 </ItemsControl.ItemTemplate>
</ItemsControl>

Among them, Border named Bd is the separator. At this time, the separator can be seen in the head of every item. Now our goal is to hide the separator of the first item, which achieves the purpose of separator between items.

The first implementation is the simplest, using the collection item forward binding PreviousData, which is one of the four binding methods, and it is estimated that it is also the least used one at ordinary times, but it comes in handy at this time. The code is as follows:


<DataTemplate.Triggers>
 <DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}"
     Value="{x:Null}">
  <Setter TargetName="Bd" Property="Visibility" Value="Collapsed" />
 </DataTrigger>
</DataTemplate.Triggers>

When the preceding item of a certain item is empty, the separator is hidden, and a simple line of code is done. However, this implementation has a disadvantage that if Insert is used to add data to the front of the bound data source, there will be more than one item without delimiters, and this problem will not occur if it is added to the end of the queue or in the queue.

The second implementation is to mark the index number of the collection items with the AlternationCount and AlternationIndex attributes of ItemsControl, and then hide the separator of the items with index number 0. The code is as follows:

<ItemsControl ItemsSource="{Binding Source}" BorderThickness="1" BorderBrush="Blue"
              VerticalAlignment="Stretch" AlternationCount="{Binding Source.Count}">

First, bind AlternationCount to the Count attribute of the data source on ItemsControl, and then the AlternationIndex attribute of ItemsControl becomes the index number of the collection data source. Write logic in the trigger:


<Border Name="Bd" Grid.Row="0" Height="1" Background="Red">
 <Border.Style>
  <Style TargetType="{x:Type Border}">
   <Style.Triggers>
    <DataTrigger
     Binding="{Binding Path=(ItemsControl.AlternationIndex), 
   RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
     Value="0">
     <Setter Property="Visibility" Value="Collapsed" />
    </DataTrigger>
   </Style.Triggers>
  </Style>
 </Border.Style>
</Border>

The trigger determines that Border is hidden when the index number is 0, The amount of code in this way is not large, The advantage is that this function can be absolutely realized, Whether you insert it at the head or the end of the team, However, the original meaning of AlternationCount and AlternationIndex attributes is to realize functions such as interlaced discoloration, which is occupied at this time. Therefore, if your collection wants to realize the functions of separator and interlaced style at the same time, it may need to add an extra converter, but the content of the converter is also very simple, and the previous functions can be restored by finding a remainder.

For the code for this small feature, see: https://github.com/fengrui358/WPFLabs/tree/master/WpfItemsControlSplitter


Related articles: