简体   繁体   中英

WPF: Fast auto-sizing by height grid

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.

在此处输入图片说明

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 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.

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:

<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?

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:

<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:

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:

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 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM