在WPF的集合控件中常常需要在每一个集合项之间插入一个分隔符样式,但是WPF的ItemsControl没有相关功能的直接实现,所以只能考虑曲线救国,经过研究,大概想到了以下两种实现方式。

先写出ItemsControl的数据模板,如下:

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

其中名为BdBorder就是分隔符,此时每一项的头部都可以看见分隔符,现在我们的目标是要隐藏掉第一项的分隔符,这就达到了项与项之间才有分隔符的目的。

第一种实现方式最简单,使用集合项前向绑定PreviousData,这是四种绑定方式中的一种,估计也是平时用得最少的一种,不过此时就派上用场了,代码如下:

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

当某一项的前项为空时就隐藏分隔符,简单的一行代码搞定。不过这种实现方式有个缺点就是如果使用的是Insert方式向绑定的数据源的最前面添加数据则就会出现不止一个没有分隔符的项,如果是往队尾或者队中添加则不会出现这个问题。

第二种实现方式是借助ItemsControlAlternationCountAlternationIndex属性来为集合项标记索引号,再隐藏索引号为0的项的分隔符,代码如下:

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

首先在ItemsControl上绑定AlternationCount到数据源的Count属性上,然后此时ItemsControlAlternationIndex属性就变成的该集合数据源的索引号了,在触发器中写上逻辑即可:

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

触发器判定当索引号为0时就隐藏Border,这种方式代码量也不大,优点是能绝对实现这个功能,无论向队首插入还是队尾插入,但是AlternationCountAlternationIndex属性本来的含义是用来实现比如隔行变色等功能,此时这种功能被占用,所以如果你的集合要同时实现分隔符和隔行样式的功能可能需要额外加转换器,不过转换器内容也很简单,求个余数就能还原之前的功能了。


(2017年4月15日补充)

经过网友vbfool提示,补充第三种方式,按照第二种思路自定义附加属性,这样就不用占用原生ItemsControl的属性了。并且可以用附加属性标记出所有的索引号,供其他场景使用。

先自定义一个MarkIndex属性用于标记ItemsControl,如果这个属性被设为True再在代码逻辑中去订阅数据项的变更,然后向ItemContainer中设置一个ItemIndex附加属性,标记出索引号。

定义的依赖属性如下:

#region MarkIndex

public static readonly DependencyProperty MarkIndexProperty = DependencyProperty.RegisterAttached(
"MarkIndex", typeof(bool), typeof(ItemsControlHelper), new PropertyMetadata(default(bool), OnMarkIndexPropertyChanged)); public static bool GetMarkIndex(DependencyObject obj)
{
return (bool)obj.GetValue(MarkIndexProperty);
} public static void SetMarkIndex(DependencyObject obj, bool value)
{
obj.SetValue(MarkIndexProperty, value);
} private static void OnMarkIndexPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs args)
{
if ((bool)args.NewValue)
{
var itemsControl = dependencyObject as ItemsControl;
if (itemsControl != null)
{
itemsControl.ItemContainerGenerator.StatusChanged -= ItemContainerGeneratorOnStatusChanged;
itemsControl.ItemContainerGenerator.ItemsChanged -= ItemContainerGeneratorOnItemsChanged; itemsControl.ItemContainerGenerator.StatusChanged += ItemContainerGeneratorOnStatusChanged;
itemsControl.ItemContainerGenerator.ItemsChanged += ItemContainerGeneratorOnItemsChanged;
}
}
else
{
var itemsControl = dependencyObject as ItemsControl;
if (itemsControl != null)
{
itemsControl.ItemContainerGenerator.StatusChanged -= ItemContainerGeneratorOnStatusChanged;
itemsControl.ItemContainerGenerator.ItemsChanged -= ItemContainerGeneratorOnItemsChanged;
}
}
} private static void ItemContainerGeneratorOnItemsChanged(object sender, ItemsChangedEventArgs itemsChangedEventArgs)
{
var itemContainerGenerator = (ItemContainerGenerator)sender; if (itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
for (int i = 0; i < itemContainerGenerator.Items.Count; i++)
{
var dp = itemContainerGenerator.ContainerFromIndex(i); if (dp != null)
{
var oldIndex = (int)dp.GetValue(ItemIndexProperty);
if (oldIndex != i)
{
dp.SetValue(ItemIndexProperty, i);
}
}
}
}
} private static void ItemContainerGeneratorOnStatusChanged(object sender, EventArgs eventArgs)
{
var itemContainerGenerator = (ItemContainerGenerator)sender; if (itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
for (int i = 0; i < itemContainerGenerator.Items.Count; i++)
{
var dp = itemContainerGenerator.ContainerFromIndex(i); if (dp != null)
{
var oldIndex = (int)dp.GetValue(ItemIndexProperty);
if (oldIndex != i)
{
dp.SetValue(ItemIndexProperty, i);
}
}
}
}
} #endregion #region ItemIndex public static readonly DependencyProperty ItemIndexProperty = DependencyProperty.RegisterAttached(
"ItemIndex", typeof(int), typeof(ItemsControlHelper), new PropertyMetadata(default(int))); public static int GetItemIndex(DependencyObject obj)
{
return (int)obj.GetValue(ItemIndexProperty);
} public static void SetItemIndex(DependencyObject obj, bool value)
{
obj.SetValue(ItemIndexProperty, value);
} #endregion

使用方式如下:

<ItemsControl ItemsSource="{Binding Source}" BorderThickness="1" BorderBrush="Blue"
VerticalAlignment="Stretch"
wpfItemsControlSeparator:ItemsControlHelper.MarkIndex="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="0,1,0,0" />
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=(wpfItemsControlSeparator:ItemsControlHelper.ItemIndex),
RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
Value="0">
<Setter Property="BorderThickness" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Grid.Row="1" Text="{Binding}"
ToolTip="{Binding Path=(wpfItemsControlSeparator:ItemsControlHelper.ItemIndex),
RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

这个小功能的代码参见:https://github.com/fengrui358/WPFLabs/tree/master/WpfItemsControlSeparator

WPF集合控件实现分隔符(ItemsControl Separator)的更多相关文章

  1. WPF集合控件实现分隔符(ItemsControl Splitter)

    在WPF的集合控件中常常需要在每一个集合项之间插入一个分隔符样式,但是WPF的ItemsControl没有相关功能的直接实现,所以只能考虑曲线救国,经过研究,大概想到了以下两种实现方式. 先写出Ite ...

  2. Windows Phone中的几种集合控件

    前言 Windows Phone开发过程中不可避免的就是和集合数据打交道,如果之前做过WP App的开发的话,相信你已经看过了各种集合控件的使用.扩展和自定义.这些个内容在这篇博客里都没有,那么我们今 ...

  3. 重新想象 Windows 8 Store Apps (5) - 控件之集合控件: ComboBox, ListBox, FlipView, ItemsControl, ItemsPresenter

    原文:重新想象 Windows 8 Store Apps (5) - 控件之集合控件: ComboBox, ListBox, FlipView, ItemsControl, ItemsPresente ...

  4. WPF布局控件常用属性介绍

    WPF布局控件常用属性介绍 其它 | 作者:慧都控件网 | 2011-04-06 13:41:57| 阅读 0次 有用(0) 评论(0)   概述:WPF布局控件都是派生自System.Windows ...

  5. WPF ListView控件设置奇偶行背景色交替变换以及ListViewItem鼠标悬停动画

    原文:WPF ListView控件设置奇偶行背景色交替变换以及ListViewItem鼠标悬停动画 利用WPF的ListView控件实现类似于Winform中DataGrid行背景色交替变换的效果,同 ...

  6. WPF Popup 控件导致被遮挡内容不刷新的原因

    WPF Popup 控件导致被遮挡内容不刷新的原因 周银辉 今天在写一个WPF控件时用到了Popup控件,很郁闷的情况是:当popup关闭时,原来被popup挡住的界面部分不刷新,非要手动刷新一下(比 ...

  7. 创建 WPF 工具箱控件

    创建 WPF 工具箱控件 WPF (Windows Presentation Framework) 工具箱控件模板允许您创建 WPF 控件,会自动添加到 工具箱 安装扩展的安装. 本主题演示如何使用模 ...

  8. wpf打印控件 实现分页打印控件功能

    因为 要实现打印 wpf  listbox控件  数据特别多 要打印在 几张纸上    找了几天 都没有找到相关的例子 现在 解决了 在这里和大家分享一下 public void print(Fram ...

  9. WPF常用控件应用demo

    WPF常用控件应用demo 一.Demo 1.Demo截图如下: 2.demo实现过程 总体布局:因放大缩小窗体,控件很根据空间是否足够改变布局,故用WrapPanel布局. <ScrollVi ...

随机推荐

  1. HDU 5207 Greatest Greatest Common Divisor

    题目链接: hdu:http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=153598 bc(中文):http://bestco ...

  2. Java try catch

    /*   异常处理的捕捉形式: 这是可以对异常进行针对性处理的方式.   具体格式是: try { //需要被检测异常的代码. } catch(异常类 变量)//该变量用于接收发生的异常对象 { // ...

  3. IBM存储降级告警等一些服务器问题/dd/ethtool

    1.IBM存储降级告警 一般两种情况 a.端口降级 例如模块16G->8G(IBM储存端口自适应) b.系统在作raid后,有硬盘损坏,降级 黄灯告警 2. dimm error dimm内存插 ...

  4. Qt动态连接库/静态连接库创建与使用,QLibrary动态加载库

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt动态连接库/静态连接库创建与使用,QLibrary动态加载库     本文地址:https ...

  5. Java实现的词频统计——单元测试

    前言:本次测试过程中发现了几个未知字符,这里将其转化为十六进制码对其加以区分. 1)保存统计结果的Result文件中显示如图: 2)将其复制到eclipse环境下的切分方法StringTokenize ...

  6. 免费各种查询API接口

    快递查询 http://www.kuaidi100.com/query?type=quanfengkuaidi&postid=390011492112 (PS:快递公司编码:申通"s ...

  7. mysql中(存储)函数

    (存储)函数: 函数,也说成“存储函数”,其实就是js或php中所说的函数! 唯一的区别: 这里的函数必须返回一个数据(值): 定义形式: 注意事项: 1, 在函数内部,可以有各种变量和流程控制的使用 ...

  8. saltstack进阶

    查看minion端的文件内容 [root@linux-node2 ~]# cat /etc/resolv.conf # Generated by NetworkManager nameserver 1 ...

  9. bzoj2818 Gcd(欧拉函数)

    Description 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对. Input 一个整数N Output 如题 Sample Input 4 Sam ...

  10. 【loj6038】「雅礼集训 2017 Day5」远行 树的直径+并查集+LCT

    题目描述 给你 $n$ 个点,支持 $m$ 次操作,每次为以下两种:连一条边,保证连完后是一棵树/森林:询问一个点能到达的最远的点与该点的距离.强制在线. $n\le 3\times 10^5$ ,$ ...