WPF中StackPanel的尺寸的怪癖

 

前几天写了文章: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包裹下?)

 

 

 

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值