前几天写了文章:WPF中的DataGrid控件的VerticalScrollBarVisibility属性失效
今天继续补充StackPanel的特点,可以作为对上述文章的进一步解释。
在WPF中,StackPanel是十分常用的布局元素。然而,该元素和很多其它元素不同,当其内部元素需要的尺寸较大时(超出StackPanel父元素)的尺寸时,如果没有明确限制StackPanel元素的MaxWidth/MaxHeight或者直接赋值Width/Height,那么StackPanel的尺寸会超出父元素的尺寸。
以下用一个实例来证明:
Xaml代码:
<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfApp19"xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2"x:Class="WpfApp19.MainWindow"mc:Ignorable="d"Title="MainWindow" Height="600" Width="800" x:Name="this" Loaded="this_Loaded" ><Grid><Grid.RowDefinitions><RowDefinition Name="row0"/><RowDefinition Name="row1"/></Grid.RowDefinitions><DockPanel><TextBox DockPanel.Dock="Top" Background="AliceBlue" Height="200" VerticalContentAlignment="Top" Margin="20,10" Name="tBox"/><Button Grid.Column="0" Content="增加2行数据" Margin="20,0,20,5" Click="AddData_Click" Height="54.5" VerticalAlignment="Bottom" /></DockPanel><StackPanel Grid.Row="1" Margin="20" Name="stackPanel"><DataGrid ItemsSource="{Binding Students, ElementName=this}" VerticalScrollBarVisibility="Auto" AutoGenerateColumns="False"><DataGrid.Columns><DataGridTextColumn Width="1*" Header="Name" Binding="{Binding Name}" /><DataGridTextColumn Width="1*" Header="Hobby" Binding="{Binding Hobby}"/><DataGridTextColumn Width="1*" Header="Sex" Binding="{Binding Sex}"/></DataGrid.Columns></DataGrid></StackPanel></Grid>
</Window>
C#代码:
//Stu是数据类 public class Stu:INotifyPropertyChanged{private string _name;public string Name{get{return _name;}set{if(_name!=value){_name = value;PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));}}}private string _hobby;public string Hobby{get{return _hobby;}set{if (_hobby != value){_hobby = value;PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hobby"));}}}private string _sex="Male";public string Sex{get{return _sex;}set{if (_sex != value){_sex = value;PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Sex"));}}}public event PropertyChangedEventHandler PropertyChanged;}/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}public ObservableCollection<Stu> Students { get; } = new ObservableCollection<Stu>();private void ShowHeightData(){int row1h = (int)row1.ActualHeight;int stackheight = (int)stackPanel.ActualHeight;int gridHeight = (int)grid.ActualHeight;int count = Students.Count;string newHeightString = $"data rows={count},row1 Height={(int)row1h},stackPanel Height={(int)stackheight},datagrid height={gridHeight}";if (string.IsNullOrWhiteSpace(HeightDataString)){HeightDataString = newHeightString;}else{HeightDataString = HeightDataString + "\n" + newHeightString;}tBox.Text = HeightDataString;}private string HeightDataString = "";private async void AddData_Click(object sender, RoutedEventArgs e){var stu1 = new Stu(){Name = "zhangsan",Hobby = "football"};for (int i = 0; i < 2; i++){Students.Add(stu1);}await DoWorkAsync(); //这个方法中什么都不做,仅仅等待UI重新排列与显示。要等显示后再测量height数据ShowHeightData(); //测量height,并显示出来}private async Task DoWorkAsync(){await Task.Run(() =>{Thread.Sleep(50);});}private void this_Loaded(object sender, RoutedEventArgs e){ShowHeightData();}}
上面的代码仅仅是在一个StackPanel中方式DataGrid,并添加一个按钮和一个文本框。没点击一次按钮,就会向DataGrid中添加两行数据,同时测量StackPanel和DataGrid的高度。
根据测试,发现:
(1)当添加的数据数量较少(没有占满屏幕控件)的时候,StackPanel的高度一直不变,即与父元素的高度的一样的。因此,StackPanel的大小在默认情况下是正好充满父元素的内部空间的。
(2)当添加的数据梁较少(超出屏幕显示量)的时候,stackPanel的高度继续增加,超过了父元素(Grid的第二行)的尺寸。也就是说,子元素StackPanel的实际尺寸超出了父元素的实际尺寸,但超出部分是不显示出来的。对于DataGrid控件,虽然设置了VerticalScrollBarVisibility="Auto",但始终显示不出来滑动条。
(3)当程序运行过程中改变窗口大小(拉伸窗口边框的方式)后,再添加数据发现row1 Height也会改变,即Grid的行高自适应窗口大小进行调整了。
(4)这个例子中的StackPanel中仅包含一个元素,在测试过程中,包含了多个元素,结论都是一样的:内部元素占据空间较大时,StackPanel的尺寸会超过父元素的尺寸;DataGrid的竖向滚动条无法显示出来。
(5)那既然StackPanel是这样的,那DockPanel如何呢?直接把StackPanel改成DockPanel,发现DockPanel的尺寸始终与父元素一样大,始终不会超过父元素。当添加较多数据时,DataGrid控件的滚动条就显示了出来了,一点问题都没有。(在StackPanel外层在用ScrollViewer包裹下?)