在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. 软工实践第八次作业(课堂实战)- 项目UML设计(第五组)

    本次作业博客 团队信息 队名:起床一起肝活队 原组长: 白晨曦(101) 原组员: 李麒 (123) 陈德斌(104) 何裕捷(214) 黄培鑫(217) 王焕仁(233) 林志华(128) 乐忠豪( ...

  2. 读我是一只IT小小鸟有感

    进入大学半年了,每个人都说软件工程是一个非常有前途的高薪职业,但我这半年来都很迷茫,看不清自己的未来,不知道如何度过接下来的三年半时光,虽然我也认为软件工程是有前途的专业,但是经过一学期的学习,发现不 ...

  3. IHttpModule的那些事

    写在前面 关于IHttpModule的相关内容,在面试的时候也被问到过,当时也是隐隐约约的感觉这个接口有一个Init方法,可以在实现类中的Init方法注册一系列的事件,说句实话,具体哪些事件,忘了差不 ...

  4. AVAudioPlayer播放音乐

    1:首先创建一个新的项目,继承自UIViewController 2:导入框架AVFoundation.framework 右键工程名,在Build Phases的Link Binary With L ...

  5. C++变量内存分配及类型修饰符

    前言 了解C++程序内存分配,有助于深刻理解变量的初始化值以及其生存周期.另外,变量类型修饰符也会影响到变量的初始化值及其生存周期.掌握了不同类型变量的初始化值及其生存周期,能够让我们设计程序时定义变 ...

  6. javascript 常用知识点

    1:浏览器是有缓存的,开发中可以通过快捷键绕过缓存 对于Windows驱动的系统:Ctrl + F5 对于Mac驱动的系统:Command + Shift + R. 2:精度问题 (符点和大数字可能会 ...

  7. Centos 7 环境下,如何使用 Apache 实现 SSL 虚拟主机 双向认证 的详细教程:

    1. testing ! ... 1 1 原文参考链接: http://showerlee.blog.51cto.com/2047005/1266712 很久没有更新LAMP的相关文档了,刚好最近单位 ...

  8. c#对一个类的扩展

    首先定义一个静态类,参数使用this约束并选择需要扩展的类,当然也可以 继续添加扩展是需要添加的参数 public static class StringExrprp { /// <summar ...

  9. canvas - 炫酷的3D星空

    1.国际惯例,先上效果 (⊙o⊙)… 效果图看上去效果并不很炫酷啊,直接戳 这里 看效果吧! 2代码部分 html: <canvas id="canvas" width=&q ...

  10. 跟我学Spring Cloud(Finchley版)-17-Zuul路由配置详解

    但在实际项目中,往往需要自己定义路由规则,Zuul的路由配置非常灵活.简单,本节详细讲解Zuul的路由配置. 一.自定义指定微服务的访问路径 配置zuul.routes.指定微服务的serviceId ...