WPF集合控件实现分隔符(ItemsControl Separator)
在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>
其中名为Bd的Border就是分隔符,此时每一项的头部都可以看见分隔符,现在我们的目标是要隐藏掉第一项的分隔符,这就达到了项与项之间才有分隔符的目的。
第一种实现方式最简单,使用集合项前向绑定PreviousData,这是四种绑定方式中的一种,估计也是平时用得最少的一种,不过此时就派上用场了,代码如下:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}"
Value="{x:Null}">
<Setter TargetName="Bd" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
当某一项的前项为空时就隐藏分隔符,简单的一行代码搞定。不过这种实现方式有个缺点就是如果使用的是Insert方式向绑定的数据源的最前面添加数据则就会出现不止一个没有分隔符的项,如果是往队尾或者队中添加则不会出现这个问题。
第二种实现方式是借助ItemsControl的AlternationCount和AlternationIndex属性来为集合项标记索引号,再隐藏索引号为0的项的分隔符,代码如下:
<ItemsControl ItemsSource="{Binding Source}" BorderThickness="1" BorderBrush="Blue"
VerticalAlignment="Stretch" AlternationCount="{Binding Source.Count}">
首先在ItemsControl上绑定AlternationCount到数据源的Count属性上,然后此时ItemsControl的AlternationIndex属性就变成的该集合数据源的索引号了,在触发器中写上逻辑即可:
<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,这种方式代码量也不大,优点是能绝对实现这个功能,无论向队首插入还是队尾插入,但是AlternationCount和AlternationIndex属性本来的含义是用来实现比如隔行变色等功能,此时这种功能被占用,所以如果你的集合要同时实现分隔符和隔行样式的功能可能需要额外加转换器,不过转换器内容也很简单,求个余数就能还原之前的功能了。
(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)的更多相关文章
- WPF集合控件实现分隔符(ItemsControl Splitter)
在WPF的集合控件中常常需要在每一个集合项之间插入一个分隔符样式,但是WPF的ItemsControl没有相关功能的直接实现,所以只能考虑曲线救国,经过研究,大概想到了以下两种实现方式. 先写出Ite ...
- Windows Phone中的几种集合控件
前言 Windows Phone开发过程中不可避免的就是和集合数据打交道,如果之前做过WP App的开发的话,相信你已经看过了各种集合控件的使用.扩展和自定义.这些个内容在这篇博客里都没有,那么我们今 ...
- 重新想象 Windows 8 Store Apps (5) - 控件之集合控件: ComboBox, ListBox, FlipView, ItemsControl, ItemsPresenter
原文:重新想象 Windows 8 Store Apps (5) - 控件之集合控件: ComboBox, ListBox, FlipView, ItemsControl, ItemsPresente ...
- WPF布局控件常用属性介绍
WPF布局控件常用属性介绍 其它 | 作者:慧都控件网 | 2011-04-06 13:41:57| 阅读 0次 有用(0) 评论(0) 概述:WPF布局控件都是派生自System.Windows ...
- WPF ListView控件设置奇偶行背景色交替变换以及ListViewItem鼠标悬停动画
原文:WPF ListView控件设置奇偶行背景色交替变换以及ListViewItem鼠标悬停动画 利用WPF的ListView控件实现类似于Winform中DataGrid行背景色交替变换的效果,同 ...
- WPF Popup 控件导致被遮挡内容不刷新的原因
WPF Popup 控件导致被遮挡内容不刷新的原因 周银辉 今天在写一个WPF控件时用到了Popup控件,很郁闷的情况是:当popup关闭时,原来被popup挡住的界面部分不刷新,非要手动刷新一下(比 ...
- 创建 WPF 工具箱控件
创建 WPF 工具箱控件 WPF (Windows Presentation Framework) 工具箱控件模板允许您创建 WPF 控件,会自动添加到 工具箱 安装扩展的安装. 本主题演示如何使用模 ...
- wpf打印控件 实现分页打印控件功能
因为 要实现打印 wpf listbox控件 数据特别多 要打印在 几张纸上 找了几天 都没有找到相关的例子 现在 解决了 在这里和大家分享一下 public void print(Fram ...
- WPF常用控件应用demo
WPF常用控件应用demo 一.Demo 1.Demo截图如下: 2.demo实现过程 总体布局:因放大缩小窗体,控件很根据空间是否足够改变布局,故用WrapPanel布局. <ScrollVi ...
随机推荐
- Web站点性能-宏观手段
1,增加服务器配置,购买性能更强的服务器,cpu.增加内存.增加硬盘(换更大更好的硬盘): 2,修改优化程序: 1)增加缓存: 2)优化代码,优化sql: 3)分离静态资源和动态页面: 3,对服务承担 ...
- Microsoft Orleans 之 开发人员指南
开发一个Grain 在开发Grain之前请先阅读Grains 这篇文章 Grain 接口 Grains通过调用各自定义在接口中的方法相互作用,一个grain实现了事先定义好的一个或多个接口,grain ...
- java——线性表接口实现
线性表是存储顺序牌类的数据时最常用的数据结构. 实现线性表有两种方式.第一种是使用数组存储线性表的元素.数组是动态创建的.超过数组的容量时,创建一个 新的更大的数组,并且将当前数组中的元素复制到新建的 ...
- PreparedStatement的execute误解
boolean execute() throws SQLException在此 PreparedStatement 对象中执行 SQL 语句,该语句可以是任何种类的 SQL 语句.一些特别处理过的语 ...
- node+express搭建个人网站(1)
我的个人网站 http://yangchaojie.top/ 首先了解一下node Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个 ...
- ie浏览器升级的正确姿势
一.版本说明 1.当前IE浏览器分为一下几个版本:IE 6,IE 7,IE 8,IE 9,IE 10,IE 11 2.windows最高支持IE版本win xp:IE 8win 7 :IE 11win ...
- SPOJ3899——Finding Fractions
SPOJ上的每个题目都做得我泪牛满面. 这个题目也是的.题目意思是给你两个分数a/b和c/d,要你求出一个分数p/q,使得a/b<p/q<c/d,且p最小. 看完题目后半天都没有任何思路哦 ...
- 【bzoj3774】最优选择 网络流最小割
题目描述 小N手上有一个N*M的方格图,控制某一个点要付出Aij的代价,然后某个点如果被控制了,或者他周围的所有点(上下左右)都被控制了,那么他就算是被选择了的.一个点如果被选择了,那么可以得到Bij ...
- P1107 [BJWC2008]雷涛的小猫
题目描述 雷涛同学非常的有爱心,在他的宿舍里,养着一只因为受伤被救助的小猫(当然,这样的行为是违反学生宿舍管理条例的).在他的照顾下,小猫很快恢复了健康,并且愈发的活泼可爱了. 可是有一天,雷涛下课回 ...
- 2月4日 考试——迟到的 ACX
迟到的 ACX 时限:1s 内存限制:128MB题目描述: 今天长沙下雪了,小 ACX 在上学路上欣赏雪景,导致上学迟到,愤怒的佘总给 ACX 巨佬出了一个题目想考考他,现在他找到你,希望你能帮帮他. ...