原文: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事件中加载机制初探相关推荐

  1. IE8浏览器32位被360篡改为64位(OCX控件在web页面不能加载的问题)

    OCX控件在web页面不能加载的问题 IE8浏览器32位被360篡改为64位 解决办法: 除了在ie浏览器自定义项中设置启用,将C:\Program Files\Internet Explorer下的 ...

  2. Cesium中Clock控件及时间序列瓦片动态加载

    前言 前面已经写了两篇博客介绍Cesium,一篇整体上简单介绍了Cesium如何上手,还有一篇介绍了如何将Cesium与分布式地理信息处理框架Geotrellis相结合.Cesium的强大之处也在于其 ...

  3. 陈年佳酿之 - Winform ListView 控件 double click 事件中获取选中的row与column

    背景 最近收到了一个关于以前项目的维护请求,那时的楼主还是刚刚工作的小青年~~~ 项目之前使用的是.net/winform.今天重新打开代码,看着之前在FrameWork2.0下面的代码, 满满的回忆 ...

  4. C#中,用户控件UserControl里面用Panl加载UserControl,并实现利用委托互相传值

    用户控件主窗体结构:左侧树形菜单,右侧Panl: 根据点击的菜单节点,panl里面选择性加载某一个子窗体用户控件,并传值给子窗体: 反之,在子窗体进行相应的操作之后,传值给主窗体,触发主窗体的刷新. ...

  5. Vue2.x-05 iview的Select控件点击从后台加载数据

    文章目录 概述 知识点 实现 概述 先看下效果 知识点 Vue2 + iviewui http://v2.iviewui.com/components/select 实现 <Select v-m ...

  6. linux下qt不能加载控件,找不到或加载Qt平台插件“xcb”

    我在Ubuntu16.04和Anaconda4.3.17(Python2.7)上遇到了这个问题.这个问题源于安装了Qt 5.6版本的anaconda,而我的系统Qt库是5.5版本. 一个快速的技巧是通 ...

  7. mysql抽屉图标_React Native自定义组件实现抽屉菜单控件效果

    一.需求分析 原生开发中,自定义View可谓是屡见不鲜的事情,往往系统的控件总不能满足现实的需求.五花八门的产品设计需要我们做出不同的View.关于自定义View的内容网上已经有很多的博文,本篇博客要 ...

  8. C# 常用控件及单击事件

    1.窗体  1.常用属性  (1)Name属性:用来获取或设置窗体的名称,在应用程序中可通过Name属性来引用窗体.  (2)WindowState属性: 用来获取或设置窗体的窗口状态. 取值有三种: ...

  9. Open3D-GUI系列教程(二)添加控件与回调事件

    文章目录 Widget和回调函数 1.导入模块 2.添加Widget与事件 2.1创建Button 2.2 回调事件 2.3 界面布局(Layout) 3.运行结果 Widget和回调函数 书接上文, ...

最新文章

  1. 【震惊】漱口水居然增加糖尿病,高血压发病率
  2. 把LabelImg标注的YOLO格式标签转化为VOC格式标签 和 把VOC格式标签转化为YOLO格式标签
  3. Django使用缓存笔记
  4. 【Python】xlwt基础:excel存取读写
  5. android 拖动进度,Android 可拖动的seekbar自定义进度值
  6. [短评] 关于顺丰快递大战阿里菜鸟物流事件的一点吐槽
  7. Python max函数中key的用法
  8. 【代码实现和训练】OCR技术——引入了Attention机制的crnn的印刷体汉字识别
  9. 2021年煤炭生产经营单位(安全生产管理人员)复审考试及煤炭生产经营单位(安全生产管理人员)模拟考试题
  10. IAR_EW_MSP430下载
  11. 如何准备蓝桥杯以及刷题
  12. Mac一些基本常用快捷键的使用
  13. CCNA WAN 帧中继
  14. 基于工业树莓派的AGV机器小车控制系统
  15. iOS 按钮、Cell暴力点击触发多次响应的问题研究
  16. 类似火车头的采集器-免费任意数据采集器
  17. 在Eclipse MarketPlace网页中安装插件显示Eclipse:Drag to Install!
  18. PAT练习 小白鼠排队
  19. 网站忘记密码,如何找回 -- (前提浏览器保存过密码)
  20. 分类算法——K-邻近

热门文章

  1. matlab录音函数怎么保存在哪里,利用matlab 录音
  2. 小甲鱼java视频_b站有哪些好的java视频?
  3. 韵乐x5最佳参数手动_机器学习 4 个常用超参数调试方法!
  4. python具有可嵌入性_如何构建可嵌入Python
  5. linux过滤某个mac的包,macOS 下使用 tcpdump 抓包
  6. pycharm 设置虚拟工作空间_七、连Pycharm都不知道怎么用,学什么Python
  7. 筛选数据库_快速研读国外专利,轻松筛选核心技术…研发人必看6大数据库实用功能盘点!...
  8. 求矩阵不靠边元素之和_机器(深度)学习数学知识之范数
  9. windows server 2008r2 如何隐藏iis版本号_如何拥有自己炫酷的个人博客
  10. 计算机培训营,计算机学院举办科创训练营第三期培训