前几天写了文章:WPF中的DataGrid控件的VerticalScrollBarVisibility属性失效
今天继续补充StackPanel的特点,可以作为对上述文章的进一步解释。
在WPF中,StackPanel是十分常用的布局元素。然而,该元素和很多其它元素不同,当其内部元素需要的尺寸较大时(超出StackPanel父元素)的尺寸时,如果没有明确限制StackPanel元素的MaxWidth/MaxHeight或者直接赋值Width/Height,那么StackPanel的尺寸会超出父元素的尺寸。
以下用一个实例来证明:
Xaml代码:
<Window
xmlns="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包裹下?)