(效果图,如见最下面)
需求:根据设置的行数列数,控制展示控件个数,并且填充的控件们大小刚刚好自适应填充满固定的区域,并且调整窗体大小的时候控件动态自适应窗体大小,即自适应大小并不显示滚动条(比如,设置了1行1列,则第一页显示一个控件,如设置了2行2列,则第一页显示第一行2个控件,第二行2个控件)。
解决方案,我总结有3
1.在cs代码里面动态生成Grid控件,根据设定的行列动态生成行列,将控件自适应宽高填充进去
2.固定Grid。使用WrapPanel排序,当Grid实际宽高发生改变时,动态计算每一个格子控件的宽高,通过绑定每一个控件的宽高实现
3.使用MVVM方式,利用Converter实现
其实第一第二种都特别winform写法,当年做MVC的时候,已经被MVC的模式洗脑,自己万不得已的时候才会使用这两种方式去实现,所以不推荐大家使用
当接手这个功能的时候已经有人使用方法2实现,在绑定的实体里面加了三个属性,FontSize、Width、Height,而且计算也不准确,忽略了margin,最小值设定等东西。(耦合性强,代码错乱,不好维护等缺点不一一列举),现在改成方式3去实现,特别简单,我们只需要使用Converter实现就好,因为ConverterParameter并不支持Binding写法,这里需用用到了多绑定方式去实现
(代码仅做参考,异常处理未写)
<ItemsControl Grid.Row="1" x:Name="list" ItemsSource="{Binding DefectCodeInfoPage}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--高度应设最小值-->
<Grid Margin="0 5 5 5" MinHeight="30">
<Grid.Width>
<MultiBinding Converter="{StaticResource WidthConverter}">
<Binding ElementName="list" Path="ActualWidth" />
<Binding Path="DataContext.Colunm" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type local:ModPqcHeaderAdd}}"/>
<Binding Path="Margin" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</Grid.Width>
<Grid.Height>
<MultiBinding Converter="{StaticResource HeightConverter}">
<Binding ElementName="list" Path="ActualHeight" />
<Binding Path="DataContext.Row" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type local:ModPqcHeaderAdd}}"/>
<Binding Path="Margin" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</Grid.Height>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="auto"
Height="auto"
Click="Button_MouseDown"
Content="{Binding DEFECT_CODE_NAME, Mode=TwoWay}"
FontFamily="Microsoft YaHei"
FontSize="{Binding FontSize}"
Foreground="Black"
FontWeight="Bold"
Tag="{Binding DEFECT_CODE, Mode=TwoWay}" />
<TextBox
Grid.Column="1"
Width="auto"
Height="auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
FontFamily="Microsoft YaHei"
FontSize="{Binding FontSize}"
Text="{Binding QTY, Mode=TwoWay}"
TextAlignment="Center" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
部分代码xaml
多绑定的Converter实现如下
1 [ValueConversion(typeof(double), typeof(double))]
2 public class WidthConverter : IMultiValueConverter
3 {
4 public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
5 {
6 double intWidth = 100;
7 try
8 {
9
10 double actualWidth = (double)value[0];
11 int cols = System.Convert.ToInt32(value[1]);
12 Thickness margin = (Thickness)(value[2]);
13 double lmargin = margin.Left;
14 double rmargin = margin.Right;
15
16 if (cols == 0)
17 return cols = 1;
18
19 intWidth = actualWidth / cols - cols * (lmargin + rmargin);
20 }
21 catch(Exception ex)
22 {
23 }
24 return intWidth;
25
26 }
27 public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
28 {
29 return null;
30
31 }
32
33 }
34
35 [ValueConversion(typeof(double), typeof(double))]
36 public class HeightConverter : IMultiValueConverter
37 {
38
39 public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
40 {
41 double intHeight = 30;
42 try
43 {
44
45 double actualHeight = (double)value[0];
46 int rows = System.Convert.ToInt32(value[1]);
47 Thickness margin = (Thickness)(value[2]);
48 double tmargin = margin.Top;
49 double bmargin = margin.Bottom;
50 if (rows == 0)
51 rows = 1;
52 intHeight = actualHeight / rows - rows * (tmargin + bmargin);
53 }
54 catch
55 {
56 }
57 return intHeight;
58
59 }
60 public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
61 {
62 return null;
63
64 }
65
66 }
Converters.cs
当当当当~~~~ 是不是超简单,超简洁的。这样根本不用写一堆属性,还在各处去控制它们,总而言之一句话,“术业有专攻”,前台的东西莫要带走
设置一列一行时
设置两行一列时
设置一行两列时
设置N行N列时(忽略不截图了)