欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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 }

运行结果:

WPF ListBox的进阶使用(二)

拉伸后:

WPF ListBox的进阶使用(二)