最近因为项目需要,开始学习如何使用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
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);
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重新进行一次数据绑定。这个问题的原因我暂时没有找到。
专门登录写个评论,为了信创而信创没有任何意义。 如果开源的spring被认为是非自主可控不安全的,那请问Linux和Java属于自主可控吗?那些大数据和人工智能的组件属于自助可控吗?Docker和K8s属于自主可控吗? 真要完全安全自主的话,国内的计算机行业应该从汇编开始发展。 师夷长技以制夷,我支持自主研发、自主可控的信创策略,但应该是更底层的芯片、操作系统、虚拟机、容器化这些基础组件的研发。像中间件、应用框架这些东西,就算一夜之间所有github的代码全部消失了,集合几家大厂的能力,开发出来又能花多少时间呢?况且现在大厂本身就有很多自研的中间件和框架。 本身东西就不复杂,现在spring、redis这些东西流行只是因为它们诞生的早生态好罢了,而不是难度。为此单独再搞一套轮子我觉得大可不必。
本人原创作者,在此解释声明一二: 1. 我的父母还没有无耻到做这种无耻的事情,我也对我的能力有信心,目前也不需要这些偷鸡摸狗的见不得光的东西来造假,请各位不要以小人之心度君子之腹。 2. 我今年初三,项目是从初一开始做的,各位不相信的欢迎上github看提交记录,可以看一下初版和现在的区别,UI和js都有不小更改。 3. 团队组成:今年一位初三,一位初一,一位5+4制的初一。更新记录中明确记录了哪些功能是哪位所开发的。 4. 我在github上,bilibili上没有受到过任何一个人的质疑,我对中国的网络环境表示蔑视。 5. 本项目的初中只是为了兴趣,没有想到火了起来。 我不理解你们是如何通过代码读出作者年龄的?难道是语文考试要加入代码阅读赏析的题目了吗?真心觉得很厉害。 6. 不愿与某些人同流,也无众位深厚阅历经验,只望能得清白之名。感谢大家让我懂得了何乃人情世故,孰谓世态炎凉,世俗红尘。你们给我的人生上了重要的一课。人间哪有什么真善美啊,呵。社会的病胎罢了。
看了 wangEditor 的公告,如鲠在喉。去年七月,我在一篇《关于剔除 layedit 组件》的公告中,还推荐了几款 editor 组件用于替代,其中就包括了 wangEditor, 转眼之间,仿若时空交错,不免有些感慨。 在国内由个人发起的开源项目,似乎都很难跳出相同的宿命,不断有人走进这个赛道,但能抵达终点并完成突破的却屈指可数。Layui 曾经同样倒在了赛道,2021 年宣布关站之前,Layui 的百度指数还一度领先 Bootstrap, 如此一个拥有广泛用户群体的 UI 库,本该迎来新的突破,却在疾跑中戛然止步,至今令人迷惑。人们看到的,是一篇充满悲情色彩的公告,而公告的背后,是创作者在面对内外交织的困境中不得已做出的举措,当我们没有足够的力量冲破眼前的障碍,除了停下来避开它,还有别的更优选择么。你很难想象除此之外还有多少历史包袱… 譬如,也是由于种种原因,当初 Layui 在 Github 和 Gitee 待处理的 Issue 数量,不下于 2000,各种议题参次不齐,我差不多花了半年时间去逐一审阅,多少个日夜消耗,多少次自我情绪的对抗… 就不多提了。 尽管这两年来,Layui 的受众者已呈断崖式流失,但正因为小众,反而如释重负,甚至让我重新找回了一些开源的纯粹。 共勉 🙂