WPF ListBox的进阶使用(二)
程序员文章站
2022-06-11 18:35:41
项目中经常使用需要根据搜索条件查询数据,然后用卡片来展示数据。用卡片展示数据时,界面的宽度发生变化,希望显示的卡片数量也跟随变化。WrapPanel虽然也可以实现这个功能,但是将多余的部分都留在行尾,十分不美观,最好是能够将多余的宽度平分在每个ListBoxItem之间,比较美观,也符合项目需求。如 ......
项目中经常使用需要根据搜索条件查询数据,然后用卡片来展示数据。用卡片展示数据时,界面的宽度发生变化,希望显示的卡片数量也跟随变化。wrappanel虽然也可以实现这个功能,但是将多余的部分都留在行尾,十分不美观,最好是能够将多余的宽度平分在每个listboxitem之间,比较美观,也符合项目需求。如下便是我自己实现的panel:
1 using system; 2 using system.collections.generic; 3 using system.linq; 4 using system.text; 5 using system.threading.tasks; 6 using system.windows; 7 using system.windows.controls; 8 9 namespace wpfdemo 10 { 11 public class mywrappanel : panel 12 { 13 protected override system.windows.size measureoverride(system.windows.size availablesize) 14 { 15 size currentlinesize = new size(); 16 size panelsize = new size(); 17 18 foreach (uielement element in base.internalchildren) 19 { 20 element.measure(availablesize); 21 size desiredsize = element.desiredsize; 22 23 if (currentlinesize.width + desiredsize.width > availablesize.width) 24 { 25 panelsize.width = math.max(currentlinesize.width, panelsize.width); 26 panelsize.height += currentlinesize.height; 27 currentlinesize = desiredsize; 28 29 if (desiredsize.width > availablesize.width) 30 { 31 panelsize.width = math.max(desiredsize.width, panelsize.width); 32 panelsize.height += desiredsize.height; 33 currentlinesize = new size(); 34 } 35 } 36 else 37 { 38 currentlinesize.width += desiredsize.width; 39 currentlinesize.height = math.max(desiredsize.height, currentlinesize.height); 40 } 41 } 42 43 panelsize.width = math.max(currentlinesize.width, panelsize.width); 44 panelsize.height += currentlinesize.height; 45 46 return panelsize; 47 } 48 49 protected override system.windows.size arrangeoverride(system.windows.size finalsize) 50 { 51 int firstinline = 0; 52 int linecount = 0; 53 54 size currentlinesize = new size(); 55 56 double accumulatedheight = 0; 57 58 uielementcollection elements = base.internalchildren; 59 double interval = 0.0; 60 for (int i = 0; i < elements.count; i++) 61 { 62 63 size desiredsize = elements[i].desiredsize; 64 65 if (currentlinesize.width + desiredsize.width > finalsize.width) //need to switch to another line 66 { 67 interval = (finalsize.width - currentlinesize.width) / (i - firstinline + 2); 68 arrangeline(accumulatedheight, currentlinesize.height, firstinline, i, interval); 69 70 accumulatedheight += currentlinesize.height; 71 currentlinesize = desiredsize; 72 73 if (desiredsize.width > finalsize.width) //the element is wider then the constraint - give it a separate line 74 { 75 arrangeline(accumulatedheight, desiredsize.height, i, ++i, 0); 76 accumulatedheight += desiredsize.height; 77 currentlinesize = new size(); 78 } 79 firstinline = i; 80 linecount++; 81 } 82 else //continue to accumulate a line 83 { 84 currentlinesize.width += desiredsize.width; 85 currentlinesize.height = math.max(desiredsize.height, currentlinesize.height); 86 } 87 } 88 89 if (firstinline < elements.count) 90 { 91 if (linecount == 0) 92 { 93 interval = (finalsize.width - currentlinesize.width) / (elements.count - firstinline + 1); 94 } 95 arrangeline(accumulatedheight, currentlinesize.height, firstinline, elements.count, interval); 96 } 97 98 99 return finalsize; 100 } 101 102 private void arrangeline(double y, double lineheight, int start, int end, double interval) 103 { 104 double x = 0; 105 uielementcollection children = internalchildren; 106 for (int i = start; i < end; i++) 107 { 108 x += interval; 109 uielement child = children[i]; 110 child.arrange(new rect(x, y, child.desiredsize.width, lineheight)); 111 x += child.desiredsize.width; 112 } 113 } 114 } 115 }
接下来,便是将这个mywrappanel作为listbox的itemspaneltemplate即可:
1 <window x:class="wpfdemo.mainwindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:comm="clr-namespace:wpfdemo.commoncontrols;assembly=wpfdemo.commoncontrols" 5 xmlns:local="clr-namespace:wpfdemo" 6 title="mainwindow" height="350" width="525"> 7 8 <grid> 9 <listbox itemssource="{binding datasource}" scrollviewer.horizontalscrollbarvisibility="disabled" 10 verticalalignment="center" borderthickness="0"> 11 <listbox.itemspanel> 12 <itemspaneltemplate> 13 <local:mywrappanel isitemshost="true"/> 14 </itemspaneltemplate> 15 </listbox.itemspanel> 16 <listbox.itemcontainerstyle> 17 <style targettype="{x:type listboxitem}"> 18 <setter property="template"> 19 <setter.value> 20 <controltemplate targettype="{x:type listboxitem}"> 21 <border horizontalalignment="stretch" verticalalignment="stretch" background="green" borderbrush="yellow" borderthickness="1"> 22 <textblock text="{binding cameraname}" width="100" horizontalalignment="center" verticalalignment="center"/> 23 </border> 24 </controltemplate> 25 </setter.value> 26 </setter> 27 </style> 28 </listbox.itemcontainerstyle> 29 <listbox.style> 30 <style targettype="{x:type listbox}"> 31 32 </style> 33 </listbox.style> 34 </listbox> 35 </grid> 36 </window>
界面对应的viewmodel:
1 using system; 2 using system.collections.generic; 3 using system.collections.objectmodel; 4 using system.linq; 5 using system.text; 6 using system.threading.tasks; 7 using system.windows.threading; 8 9 namespace wpfdemo 10 { 11 public class mainwindowvm : notifypropertybase 12 { 13 private dispatchertimer timer; 14 public mainwindowvm() 15 { 16 datasource = new observablecollection<wndviewmodel>(); 17 colums = 1; 18 for(int i =0; i < 60; ++i) 19 { 20 var temp = new wndviewmodel() 21 { 22 cameraname = string.format("camera {0}", ++count), 23 }; 24 datasource.add(temp); 25 } 26 //timer = new dispatchertimer(); 27 //timer.interval = new timespan(0, 0, 1); 28 //timer.tick += timer_tick; 29 //timer.start(); 30 } 31 32 private int count = 0; 33 void timer_tick(object sender, eventargs e) 34 { 35 var temp = new wndviewmodel() 36 { 37 cameraname = string.format("camera {0}", ++count), 38 }; 39 datasource.add(temp); 40 console.writeline(temp.cameraname); 41 if (count <= 6) 42 { 43 colums = count; 44 } 45 else if (count > 100) 46 { 47 count = 0; 48 datasource.clear(); 49 colums = 1; 50 } 51 } 52 53 private int colums; 54 public int colums 55 { 56 get { return colums; } 57 set 58 { 59 setproperty(ref colums, value); 60 } 61 } 62 63 private observablecollection<wndviewmodel> datasource; 64 public observablecollection<wndviewmodel> datasource 65 { 66 get { return datasource; } 67 set 68 { 69 setproperty(ref datasource, value); 70 } 71 } 72 } 73 }
运行结果:
拉伸后:
上一篇: Python实现文件复制删除