c# - WPF 网格行高自动,最大星号 (*)

标签 c# .net wpf datagrid

我有一个带有以下代码的 UserControl(已简化以使其可读):

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <TextBlock Grid.Row="0" />
    <TextBlock Grid.Row="1" />
    <DataGrid Grid.Row="2" />
    <TextBlock Grid.Row="3" />
</Grid>
现在我希望所有控件都显示在一个堆栈中,但仅限于窗口的大小。
问题是,当我在 DataGrid 中有大量数据时,它会超出边界并且最后一个 TextBlock 不可见 - 也不会出现 DataGrid 滚动条。
当我为 Star (*) 设置第三行定义时,DataGrid 的大小适合大量项目,但如果 DataGrid 中的项目很少,最后一个 TextBlock 出现在屏幕底部(不是紧接在后面) DataGrid 根据需要)。
当我使用 StackPanel 而不是 Grid 时,它看起来与上面的代码相同。如果我使用 DockPanel,DataGrid 可以正确滚动,但最后一个 TextBlock 根本不可见。
我想将第三行定义为 的解决方案高度=“自动” MaxHeight="*" ,但这显然是不可能的。
你能帮忙吗?

最佳答案

您需要以编程方式执行此操作,而不是在 xaml 中执行此操作。这是因为您希望它做两件不同的事情:

  • 如果只有几个项目,请将最后一个 TextBlock 靠近 DataGrid。
  • 如果 DataGrid 有大量项目,则保持最后一个 TextBlock 可见。

  • 这样做需要您在代码隐藏中 Hook 事件,确定最后一个 TextBlock 是否消失,然后在 RowDefinition 上相应地调整 Height="Auto"或 Height="*",然后是 UpdateLayout。
    这是一个示例项目。为简单起见,我用 TextBlock 替换了您的 DataGrid。
    XAML:
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Button Content="Make Grid.Row=2 Long, But Keep Text 3 Visible" Click="Button_Click" HorizontalAlignment="Center" Margin="5" Padding="5,10"/>
            <Grid Grid.Row="1" x:Name="myGrid">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition x:Name="myRowDefinition" Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <TextBlock Grid.Row="0" Text="This is Text 1" Background="Red"/>
                <TextBlock Grid.Row="1" Text="This is Text 2" Background="Green"/>
                <TextBlock Grid.Row="2" x:Name="myDataGrid" FontSize="64" Text="{Binding Output}" TextWrapping="Wrap" Background="Blue"/>
                <TextBlock Grid.Row="3" x:Name="lastTextBlock" Text="This is Text 3" Background="Violet"/>
            </Grid>
        </Grid>
    
    代码隐藏:
        public partial class MainWindow : Window, INotifyPropertyChanged
        {
            private string output;
    
            public MainWindow()
            {
                InitializeComponent();
                this.Loaded += OnLoaded;
                this.DataContext = this;
            }
    
            /// <summary>
            /// Handles the SizeChanged event of your data grid.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void MyDataGrid_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                if (!IsUserVisible(lastTextBlock, this))
                {
                    if (this.myRowDefinition.Height == GridLength.Auto)
                    {
                        // Edit the row definition and redraw it
                        this.myRowDefinition.Height = new GridLength(1, GridUnitType.Star);
                    }
                }
                else
                {
                    if (this.myRowDefinition.Height != GridLength.Auto && CanDataGridBeSmaller(this.myRowDefinition.ActualHeight))
                    {
                        // If the datagrid can be smaller, change the row definition back to Auto
                        this.myRowDefinition.Height = GridLength.Auto;
                    }
                }
                this.UpdateLayout();
            }
    
            /// <summary>
            /// It is possible for the DataGrid to take on more space than it actually needs.  This can happen if you are messing with the window resizing.
            /// So always check to make sure that if you can make the DataGrid smaller, that it stays small.
            /// </summary>
            /// <param name="actualHeight">the actual height of the DataGrid's row definition</param>
            /// <returns></returns>
            private bool CanDataGridBeSmaller(double actualHeight)
            {
                // Create a dummy control that is equivalent to your datagrid (for my purposes, I used a Textblock for simplicity, so I will recreate it fully here.
                TextBlock dummy = new TextBlock() { TextWrapping = TextWrapping.Wrap, FontSize = 64, Text = this.Output };
                dummy.Measure(new Size(this.myGrid.ActualWidth, this.myGrid.ActualHeight));
    
                // Get the dummy height and compare it to the actual height
                if (dummy.DesiredSize.Height < myRowDefinition.ActualHeight)
                    return true;
                return false;
            }
    
            /// <summary>
            /// This method determines if the control is fully visible to the user or not.
            /// </summary>
            private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
            {
                if (!element.IsVisible)
                    return false;
    
                Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
                Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
                return rect.Contains(bounds);
            }
    
            /// <summary>
            /// This is purely for setup.
            /// </summary>
            private void OnLoaded(object sender, RoutedEventArgs e)
            {
                this.myDataGrid.SizeChanged += MyDataGrid_SizeChanged;
                this.Output = "This row is short, so Text 3 below me should be flush with my bottom.";
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public string Output { get => this.output; set { this.output = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Output))); } }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                this.Output = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
            }
        }
    
    启动时的示例输出:
    enter image description here
    单击顶部按钮后的示例输出:
    enter image description here

    关于c# - WPF 网格行高自动,最大星号 (*),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62539364/

    相关文章:

    c# - 创建调用表达式树的表达式树

    c# - 卸载 "eager-loaded"属性导致返回 json 数据时出现问题

    c# - 如何在 F# 中处理多个文件和 C# 库?

    c# - 如何在 C# 中比较两个列表框的项目名称?

    c# - 不使用 ShowDialog 打印会出现空白页

    c# - 从 wpf 中的文件路径列表填充 TreeView

    c# - 以编程方式更改 SPFolder 的名称

    c# - 是否有可能将虚拟打印机与物理打印机区分开来?

    c# - 为什么 HashSet<Point> 比 HashSet<string> 慢这么多?

    wpf - 绑定(bind)到 wpf 中父元素的属性