WPF:通过高度网格快速自动调整大小

时间:2022-11-13 08:51:26

I have to implement window containing several tables into one scroll viewer. This means that this tables should be stretched by the content. User able to add / remove items (rows) to these tables.

我必须将包含多个表的窗口实现到一个滚动查看器中。这意味着这些表应该由内容拉伸。用户可以向这些表添加/删除项(行)。

The concept was shown on the following mockup. The main problem is that grid should not have scroll bar, it should have dynamic height.

这个概念在以下模型中显示。主要问题是网格不应该有滚动条,它应该有动态高度。

WPF:通过高度网格快速自动调整大小

For the moment this interface is implemented as ItemsControl inside the ScrollViewer. In the ItemTemplate of the ItemsControl added DataGrid to represent tabular data.

目前,此接口在ScrollViewer中实现为ItemsControl。在ItemsControl的ItemTemplate中添加了DataGrid来表示表格数据。

<ScrollViewer CanContentScroll="True"
              HorizontalScrollBarVisibility="Auto">

    <ItemsControl ItemsSource="{Binding Path=Groups}"
                  VirtualizingStackPanel.IsVirtualizing="True">

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel Orientation="Vertical" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="125" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <Label Grid.Column="0" Grid.Row="0"
                           Style="{StaticResource ResourceKey=LabelBaseStyle}"
                           Content="{Binding Path=GroupLabel}"
                           HorizontalAlignment="Right"/>

                    <Button Content="{x:Static Member=Resources:CommonStrings.Delete}"
                            Style="{StaticResource ResourceKey=ButtonBaseStyle}"
                            VerticalAlignment="Center" />

                    <DataGrid Grid.Row="0" Grid.Column="1"
                              AutoGenerateColumns="False"
                              ItemsSource="{Binding Path=Items}">
                        <DataGrid.Columns>
                        <!-- 21 text columns here -->
                        </DataGrid.Columns>
                    </DataGrid>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

</ScrollViewer>

This solution works fine until some amount of data has been added. Now on the 7 tables (grids) with about 50 items in the each the application is impossible slow: it takes several minutes to render it. After profiling I've found that almost all the time is spent on Measure and Arrange methods. Also I've found recomentation to not to use DataGrid into contrainers which measures it with infinity. So I undestand the problem, but I cannot change interface.

此解决方案可以正常工作,直到添加了一些数据。现在在7个表(网格)上,每个应用程序中大约有50个项目,应用程序不可能很慢:渲染它需要几分钟。在分析后,我发现几乎所有的时间都花在了测量和排列方法上。此外,我发现重新贴合不要将DataGrid用于使用无穷大测量它的对手。所以我没有解决问题,但我无法改变界面。

I've tried to write the same interface without DataGrid: replaced it by ItemsControl with TextBlocks. This solution works fine. But I've several similar interfaces and it's not looks very good to write so much similar code. The code shown below is replacement for DataGrid in previous sample:

我试图在没有DataGrid的情况下编写相同的接口:将ItemsControl替换为TextBlocks。此解决方案工作正常。但是我有几个类似的接口,编写这么多相似的代码并不是很好。下面显示的代码是上一个示例中DataGrid的替代:

<ItemsControl ItemsSource="{Binding Path=Items}"
          VirtualizingStackPanel.IsVirtualizing="True"
          Grid.Row="0" Grid.Column="1">

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <VirtualizingStackPanel Orientation="Vertical" />
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Path=Type}" />
            <!-- Another 20 TextBlocks here -->
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>

So I need to implement own UserControl or find one ready to use. Can anyone suggest me something? Maybe some workaround or lightweight control?

所以我需要实现自己的UserControl或找到一个随时可用的。谁能建议我什么?也许一些解决方法或轻量级控制?

1 个解决方案

#1


Re-did my answer....this is a better solution for your issue. I'm sure you can figure out how to modify this to work:

重新做了我的回答......这是一个更好的解决方案。我相信你可以弄清楚如何修改它来工作:

In my Main Window Xaml:

在我的主窗口Xaml中:

<ScrollViewer Height="Auto" Width="Auto" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
    <StackPanel Height="Auto" Width="Auto">
        <ItemsControl ItemsSource="{Binding ItemList}">
            <ItemsControl.Resources>
                <DataTemplate DataType="{x:Type wpfmvvmtest:itemTypeA}">
                    <wpfmvvmtest:testUC></wpfmvvmtest:testUC>
                </DataTemplate>
            </ItemsControl.Resources>
        </ItemsControl>
    </StackPanel>
</ScrollViewer>

Here's the Main ViewModel for the Main Window (testVM) all done in constructor for example:

这是主窗口的主ViewModel(testVM),所有这些都在构造函数中完成,例如:

public testVM()
{
    ItemList = new ObservableCollection<object>();
    Random rnd = new Random();

    for (int i = 0; i < 3; i++)
    {
        itemTypeA item = new itemTypeA(rnd.Next(100));

        ItemList.Add(item);
    }
}

public ObservableCollection<object> ItemList { set; get; } 

and here's what "itemTypeA" is like:

这是“itemTypeA”的样子:

public itemTypeA(int count)
{
    Items = new DataTable();
    Items.Columns.Add("One");
    Items.Columns.Add("Two");
    Items.Columns.Add("Three");
    Items.Columns.Add("Four");

    Description = "Count = " + count;
    for (int i = 0; i < count; i++)
    {
        DataRow dr = Items.NewRow();
        dr[0] = i*1;
        dr[1] = i * 2;
        dr[2] = i * 3;
        dr[3] = i * 4;
        Items.Rows.Add(dr);
    }
}

public DataTable Items { set; get; } 

Here's the important part(Make sure you have no height/width properties set in your UserControl for the DataTemplate (That allows for the DataGrid to set the height/width):

这是重要的部分(确保您的UserControl中没有为DataTemplate设置高度/宽度属性(允许DataGrid设置高度/宽度):

<UserControl x:Class="wpfmvvmtest.testUC"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             d:DataContext="d:designInstance itemTypeA"
             mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" HorizontalAlignment="Stretch" Content="{Binding Description}"></Label>
        <Border Grid.Row="1" BorderBrush="Black" BorderThickness="2">
        <DataGrid ItemsSource="{Binding Path=Items}">
        </DataGrid>
        </Border>
    </Grid>
</UserControl>

#1


Re-did my answer....this is a better solution for your issue. I'm sure you can figure out how to modify this to work:

重新做了我的回答......这是一个更好的解决方案。我相信你可以弄清楚如何修改它来工作:

In my Main Window Xaml:

在我的主窗口Xaml中:

<ScrollViewer Height="Auto" Width="Auto" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
    <StackPanel Height="Auto" Width="Auto">
        <ItemsControl ItemsSource="{Binding ItemList}">
            <ItemsControl.Resources>
                <DataTemplate DataType="{x:Type wpfmvvmtest:itemTypeA}">
                    <wpfmvvmtest:testUC></wpfmvvmtest:testUC>
                </DataTemplate>
            </ItemsControl.Resources>
        </ItemsControl>
    </StackPanel>
</ScrollViewer>

Here's the Main ViewModel for the Main Window (testVM) all done in constructor for example:

这是主窗口的主ViewModel(testVM),所有这些都在构造函数中完成,例如:

public testVM()
{
    ItemList = new ObservableCollection<object>();
    Random rnd = new Random();

    for (int i = 0; i < 3; i++)
    {
        itemTypeA item = new itemTypeA(rnd.Next(100));

        ItemList.Add(item);
    }
}

public ObservableCollection<object> ItemList { set; get; } 

and here's what "itemTypeA" is like:

这是“itemTypeA”的样子:

public itemTypeA(int count)
{
    Items = new DataTable();
    Items.Columns.Add("One");
    Items.Columns.Add("Two");
    Items.Columns.Add("Three");
    Items.Columns.Add("Four");

    Description = "Count = " + count;
    for (int i = 0; i < count; i++)
    {
        DataRow dr = Items.NewRow();
        dr[0] = i*1;
        dr[1] = i * 2;
        dr[2] = i * 3;
        dr[3] = i * 4;
        Items.Rows.Add(dr);
    }
}

public DataTable Items { set; get; } 

Here's the important part(Make sure you have no height/width properties set in your UserControl for the DataTemplate (That allows for the DataGrid to set the height/width):

这是重要的部分(确保您的UserControl中没有为DataTemplate设置高度/宽度属性(允许DataGrid设置高度/宽度):

<UserControl x:Class="wpfmvvmtest.testUC"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             d:DataContext="d:designInstance itemTypeA"
             mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" HorizontalAlignment="Stretch" Content="{Binding Description}"></Label>
        <Border Grid.Row="1" BorderBrush="Black" BorderThickness="2">
        <DataGrid ItemsSource="{Binding Path=Items}">
        </DataGrid>
        </Border>
    </Grid>
</UserControl>