WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探
原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探
最近因为项目需要,开始学习如何使用WPF开发桌面程序。使用WPF一段时间之后,感觉WPF的开发思维和Winform还是有比较大的区别,包括页面布局、数据绑定、自定义模板等等。
整个项目中,有一个业务逻辑的实现方式,需要我在使用Listview控件中插入Combobox控件,效果如下图:
第一次尝试:为了实现这个效果,我在Xaml文件中定义的代码如下:
1.资源模板定义Xaml语句
<Window.Resources>
<namespc:ListViewItemStyleSelector x:Key="mySelector"/>
<DataTemplate x:Key="FirstCell" >
<ComboBox Name="combobox" Width="80" />
</DataTemplate>
</Window.Resources></span></span>
2.Listview定义Xaml语句:
<ListView Name="listview1" Margin="5" ItemContainerStyleSelector="{DynamicResource mySelector}"
SelectionChanged="listview1_SelectionChanged"
PreviewMouseDoubleClick="listview1_PreviewMouseDoubleClick">
<ListView.View>
<GridView>
<GridViewColumn Header="料品编码" DisplayMemberBinding="{Binding Path=II_Code}" ></GridViewColumn>
<GridViewColumn Header="料品名称" DisplayMemberBinding="{Binding Path=II_Name}" ></GridViewColumn>
<GridViewColumn Header="料品规格" DisplayMemberBinding="{Binding Path=II_Spec}" ></GridViewColumn>
<GridViewColumn Header="料品型号" DisplayMemberBinding="{Binding Path=II_Version}" ></GridViewColumn>
<GridViewColumn Header="料品计量单位" DisplayMemberBinding="{Binding Path=II_UnitName}" ></GridViewColumn>
<GridViewColumn Header="工艺路线版本" CellTemplate="{StaticResource FirstCell}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView></span></span>
前台赋值语句这里就省略了,运行程序后发现所有的combobox控件数据源都是空的!
后台检查了数据源,数据源是有数据的,但是并没有成功绑定到相应的Combobox中!在检查了很多遍我的数据源列表之后,确认列表中的数据是存在,而且是符合Combobox的数据源要求的。现在问题很明显了:问题就出在数据绑定到相应Combobox这个过程中!
第二次尝试:数据绑定的问题可能存在两种情况,1.数据绑定语法错误,导致数据无法绑定、2.Combobox控件加载有问题,导致数据无法绑定。
针对第一种情况,我查阅资料,确认我的Xaml语法没有错误。既然Xaml语句无法为我需要的Combobox绑定数据源,那么,我能不能直接利用C#代码来显示地为每一个Combobox绑定数据源呢?
要显示地为Combobox赋值,首先需要利用C#代码获取到Combobox控件列表,代码如下:
private void Window_Loaded(object sender,RoutedEventArgs e)
{
List<Combobox> cbs = FindVisualChild<Combobox>(this);
}
</span><span style="font-size:14px;"> private List<T> FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
try
{
List<T> TList = new List<T> { };
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
{
TList.Add((T)child);
}
else
{
List<T> childOfChildren = FindVisualChild<T>(child);
if (childOfChildren != null)
{
TList.AddRange(childOfChildren);
}
}
}
return TList;
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
return null;
}
}
运行程序之后,发现cbs.Count的值为0!这个发现让我产生了一个让自己觉得有点惊异的想法:难道说在Window_Loaded()事件中,控件并没有加载吗?
为了验证这个想法,我对Xaml代码中的Combobox的Datatemplate部分做出了修改,修改后代码如下:
<Window.Resources>
<namespc:ListViewItemStyleSelector x:Key="mySelector"/>
<DataTemplate x:Key="FirstCell" >
<ComboBox Name="combobox" <em><u>Loaded="combobox_Loaded"</u></em> Width="80" />
</DataTemplate>
</Window.Resources>
如上代码所示,我为每一个Combobox添加了Loaded()事件,只要在窗体加载的时候,监视combobox_Loaded()事件是否发生,就知道Combobox在Window_Loaded()事件中有没有加载了。
运行的结果让我略微感到意外:窗体加载的时候,combobox_Loaded()事件确实没有发生,也就是说Combobox在窗体加载的时候并没有加载;但是当我尝试移动listview的滚动条或者点击listviewitem的时候,程序命中了combobox_Loaded()事件。
对此,我猜测:根据Xaml代码的树形结构来看,本程序中定义的Combobox的直接parent并不是this(窗体)而是listview,所以当this加载的时候,并没有为Combobox加载,所以当Combobox的直接parent加载的时候,Combobox才加载。
而且,在研究combobox_Loaded()事件的过程中,我发现一个现象:
并不是所有在listview中的Combobox都一次性加载完毕,程序会优先加载在listview的显示区域内的listviewitem中的控件,处在显示区域之外的listviewitem只有当其进入显示区域之后才进行加载。
这就会导致这样一个问题:cbs列表中的Combobox很可能因为加载不全,而导致与我提供的数据源列表对应不上。为了解决这个问题,我决定采用“暴力”的笨办法,强制让所有的Combobox都加载。
第三次尝试:
强制加载所有Combobox的函数如下:
/// <summary>
/// 滚动Listview1
/// 因为combobox是放在datatemplate中的,wpf的加载机制就是加载当前listview中显示区域的
/// datatemplate中的控件,显示区域之外的控件不加载。不加载的combobox在系统中并没有生成
/// 变量实例,因为无法为每一个combobox的数据源进行赋值。该方法需要放在window_loaded事件之外!
/// 故此,采用将listview“从头滚动到底”的方式,强制加载所有的combobox。此方法应该有更好的替代方法,暂时没找到。
/// </summary>
private bool ScrollListview( int index)
{
if (listview1.Items.Count >0)
{
int count = listview1.Items.Count;
for (int i = 0; i < count; i ++)
{
listview1.ScrollIntoView(listview1.Items[i]);
}
if (index > -1)
{
listview1.ScrollIntoView(listview1.Items[index]);
}
}
int x = FindVisualChild<ComboBox>(this).Count;
int y = listview1.Items.Count;
return x==y;
}
完成这一步之后,只要ScrollView()函数返回true,就进入以下函数:
/// <summary>
/// 加载所有的combobox
/// </summary>
private void LoadAllCombobox()
{
int count = listview1.Items.Count;
List<ComboBox> cbl = FindVisualChild<ComboBox>(listview1);
for (int i = 0; i < count; i++)
{
//这段代码将combobox和listviewitem内容一一对应起来。
ItemInfoLists item = listview1.Items[i] as ItemInfoLists;
cbl[i].ItemsSource = item.TechVersionList;
cbl[i].DisplayMemberPath = "TRV_Version";
cbl[i].SelectedValuePath = "TR_VersionID";
cbl[i].SelectedIndex = 0;
}
}
完成以上步骤之后,运行程序,所有Combobox都已经正确的加载并且绑定数据源,如下图所示:
总结:因为combobox放在datatamplate中,WPF的控件加载机制决定了在listview显示区域之外的listviewitem中的控件暂不加载,待到所属listviewitem显示的时候再行加载, 其加载顺序和listview的itemssource的遍历顺序存在错位情况,无法控制。 datatemplate中的combobox数据源需要在C# code中显式加载。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
附:解决Combobox数据源加载的问题之后,我又发现了另外一个问题。那就是,当我利用鼠标滚轮滑动listview的时候,Combobox的数据源会存在丢失的情况,需要对Combobox重新进行一次数据绑定。这个问题的原因我暂时没有找到。
WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探的更多相关文章
- MFC中 自定义类访问主对话框控件的方法
之前一直在找有木有好点的方法.现在终于被我找到,收藏之~~~~~~ 在使用mfc的时候经常遇到自定义类访问主对话框控件的问题,例如自定义类中的方法要输出一段字符串到主对话框的EDIT控件.控制对话框的 ...
- XCODE中使用Main.Storyboard拉入控件并实现事件(Swift语言)
如何在XCODE中的Main.Storyboard内拉入控件并实现一个简单的效果呢?本人由于刚接触Swift语言不久,对于IDE的操作还是很生疏,不懂了就在网上参考了网上前辈们的文章.以下我将演示如何 ...
- 陈年佳酿之 - Winform ListView 控件 double click 事件中获取选中的row与column
背景 最近收到了一个关于以前项目的维护请求,那时的楼主还是刚刚工作的小青年~~~ 项目之前使用的是.net/winform.今天重新打开代码,看着之前在FrameWork2.0下面的代码, 满满的回忆 ...
- Cesium中Clock控件及时间序列瓦片动态加载
前言 前面已经写了两篇博客介绍Cesium,一篇整体上简单介绍了Cesium如何上手,还有一篇介绍了如何将Cesium与分布式地理信息处理框架Geotrellis相结合.Cesium的强大之处也在于其 ...
- 在CTreeCtrl控件点击事件中获取点击的项
网上搜了一下,有两种方法: 1.使用GetSelectedItem() HTREEITEM hItem = m_treeCtrl.GetSelectedItem(); CString strText ...
- C#中,用户控件UserControl里面用Panl加载UserControl,并实现利用委托互相传值
用户控件主窗体结构:左侧树形菜单,右侧Panl: 根据点击的菜单节点,panl里面选择性加载某一个子窗体用户控件,并传值给子窗体: 反之,在子窗体进行相应的操作之后,传值给主窗体,触发主窗体的刷新. ...
- XE7 & FMX 那些年我们一起上过的控件:ListView 之 (3) 加载数据时如何显示自定义样式
本文介绍一下ListView下如何加载数据.及使用进度条反馈当前进度给用户. 注意: 原创作品,请尊重作者劳动成果,转载请注明出处!!!原文永久固定地址:http://www.cnblogs.com/ ...
- gePlugin封装成winform控件,一行代码即可加载。
将插件直接封装为控件,大大简化了GEPlugin的使用.多数常用功能也已经封装完毕,其他功能全部开放接口,直接调用即可. 1. GepluginControl控件传送门: 链接:https://pan ...
- C# 遍历所有的子控件和孙控件,包括容器中的,并批量操作和调用
这里要用两个知识,一个是递归,一个是队列. //定义一个Control类型的队列allCtrls private static Queue <Control> allCtrls = new ...
随机推荐
- php实现求最小的k个数(日常出错很容易是分号或者$符号忘记写了)
php实现求最小的k个数(日常出错很容易是分号或者$符号忘记写了) 一.总结 日常出错很容易是分号或者$符号忘记写了 二.php实现求最小的k个数 题目描述 输入n个整数,找出其中最小的K个数.例如输 ...
- C# 使用 RabbitMQ
1. RabbitMQ MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接 ...
- tipc
TIPC SOCKET实现分析 http://ju.outofmemory.cn/entry/158241
- zxing的使用及优化
二维码介绍 zxing项目是谷歌推出的用来识别多种格式条形码的开源项目,项目地址为https://github.com/zxing/zxing,zxing有多个人在维护,覆盖主流编程语言,也是目前还在 ...
- html+css+js实现狼吃羊小游戏
html+css+js实现狼吃羊小游戏 一.总结 一句话总结:给动的元素下标记,这里表现为将要活动的标签动态增加class,这是一种很好的思想. 1.如何实现棋子走动的时候简单精确定位? 用重构坐标系 ...
- 自绘实现半透明水晶按钮(继承CButton,设置BS_OWNERDRAW风格,覆盖DrawItem函数绘制按钮,把父窗口的背景复制到按钮上,实现视觉上的透明,最后通过AlphaBlend实现半透明)
运行效果 实现方法 1.给按钮加上BS_OWNERDRAW样式2.重载DrawItem函数,在这里绘制按钮3.关键之处就是把父窗口的背景复制到按钮上,实现视觉上的透明4.最后通过AlphaBlend实 ...
- Windows安装Jekyll
Run Jekyll on Windows 夹 Jekyll介绍 安装Ruby 安装DevKit 安装Jekyll 安装Python 安装pip 执行Jekyll Introduction Jekyl ...
- 使用readLine()方法遇到的坑
程序很简单,客户段从控制台读取用户输入,然后发送至服务器端,主要代码如下 服务端代码: 客户端代码: 结果运行的时候,当开启服务端和客户端后,在客户端的控制台 键盘输入 内容,服务端却没有显示内容 原 ...
- 安卓手机可以连上wifi但无法上网的解决办法
作者:朱金灿 来源:http://blog.csdn.net/clever101 前晚我的安卓手机还可以连接wifi上网,昨晚显示已经连接,但是死活打不开网页.于是到网上查了下,发现要将原来的DHCP ...
- C# 使用外部别名
原文:C# 使用外部别名 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http://lindexi.gitee.io 访问博客.本文 ...