如何加速以下WPF UI代码

时间:2017-06-08 07:46:33

标签: c# wpf performance grid stackpanel

我试图理解为什么我用来显示输入信息的网格视图的以下WPF C#代码是如此之慢,特别是如何改进它以加快控件的速度渲染。

我有以下输入,可根据用户选择而有所不同:

public class Field
{
    public string Key;
    public Tuple<string, bool> Value;
}


var fields = new List<Field>();
// fill fields...

对于每个字段,我创建一个插入VirtualizingStackPanel的控件:

StackPanelFields.Children.Clear();
foreach (var f in fields)
    StackPanelFields.Children.Add(GetFieldControl(f.Key, f.Value));


private Grid GetFieldControl(string name, Tuple<string, bool> value)
{
    Debug.Assert(!String.IsNullOrWhiteSpace(name));
    Debug.Assert(value != null);

    // two-column grid
    var grid = new Grid();
    grid.ColumnDefinitions.Add(new ColumnDefinition());
    grid.ColumnDefinitions.Add(new ColumnDefinition());

    var color = value.Item2 ? Brushes.Black : Brushes.Red;

    var nameTextblock = new TextBlock { Text = name, Margin = new Thickness(5, 0, 0, 5), Foreground = color };
    grid.Children.Add(nameTextblock);

    // value
    var valueTextBox = new TextBox
    {
        Text = value.Item1,
        Foreground = color,
        TextWrapping = TextWrapping.Wrap,
        IsReadOnly = true,
        BorderThickness = new Thickness(0)
    };

    Grid.SetColumn(valueTextBox, 1);
    grid.Children.Add(valueTextBox);

    return grid;
}

字段数平均在1000-2000之间。在我的机器上填充VirtualizingStackPanel可能需要超过1秒;我没有测量过这个时间,但显然对用户来说非常慢。

1 个答案:

答案 0 :(得分:0)

我在呈现1.000.000项目(甚至更多)时没有任何问题,创建数据集合然后呈现它将花费更多时间。

模型类

public class Field : ObservableObject
{
    private string _name;
    private string _value;
    private bool _flag;

    public string Name { get => _name; set => Set(ref _name, value); }
    public string Value { get => _value; set => Set(ref _value, value); }
    public bool Flag { get => _flag; set => Set(ref _flag, value); }
}

MainViewModel

public class MainViewModel : ViewModelBase
{
    private int _fieldcount;
    private ObservableCollection<Models.Field> _fields;

    public MainViewModel()
    {
        if (IsInDesignMode)
        {
            _fieldcount = 100;
        }
        else
        {
            _fieldcount = 1000000;
        }

        Initialize();

    }

    private void Initialize()
    {
        var fieldquery = Enumerable.Range(1, _fieldcount).Select(e => new Models.Field { Name = $"Field {e}", Value = $"Value {e}", Flag = false, });
        Fields = new ObservableCollection<Models.Field>(fieldquery);
    }

    public ObservableCollection<Models.Field> Fields { get => _fields; set => Set(ref _fields, value); }

}

视图

<Window x:Class="WpfApp6.MainWindow"
        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:models="clr-namespace:WpfApp6.Models"
        xmlns:local="clr-namespace:WpfApp6"
        mc:Ignorable="d"
        DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ItemsControl VirtualizingStackPanel.IsVirtualizing="True"
                      ScrollViewer.CanContentScroll="True"
                      ItemsSource="{Binding Path=Fields}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type models:Field}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Name}" />
                        <TextBox Grid.Column="1" Text="{Binding Value}" IsReadOnly="True"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.Template>
                <ControlTemplate>
                    <Border BorderThickness="{TemplateBinding Border.BorderThickness}"
                            Padding="{TemplateBinding Control.Padding}"
                            BorderBrush="{TemplateBinding Border.BorderBrush}"
                            Background="{TemplateBinding Panel.Background}"
                            SnapsToDevicePixels="True">
                        <ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                        </ScrollViewer>
                    </Border>
                </ControlTemplate>
            </ItemsControl.Template>
        </ItemsControl>
    </Grid>
</Window>

整个项目是here