简体   繁体   English

WPF - UserControls非常慢

[英]WPF - UserControls very slow

I am designing something similar a PropertyGrid where I want to show properties of objects. 我正在设计类似于PropertyGrid的东西,我想在其中显示对象的属性。 For special reasons I am not going to use the PropertyGrid but create my own. 出于特殊原因,我不打算使用PropertyGrid,而是创建自己的。

For each property I have created a custom usercontrol. 对于每个属性,我创建了一个自定义用户控件。 Now to my horror the performance is very bad. 令我恐惧的是,表现非常糟糕。 If I have something like 100 properties it takes 500 milliseconds to show them in a StackPanel/Listbox. 如果我有100个属性,则需要500毫秒才能在StackPanel / Listbox中显示它们。

I did an experiment where I add 200 default UserControls to a StackPanel. 我做了一个实验,在那里我向StackPanel添加了200个默认UserControl。 It took about 50 milliseconds. 花了大约50毫秒。 Still a very high number I think. 我认为仍然是一个非常高的数字。

Should I not use usercontrols for such a purpose? 我不应该将usercontrol用于此目的吗? It seems very object-oriented to do it this way and I can not really see another solution. 这样做似乎非常面向对象,我无法真正看到另一种解决方案。

However I can see that PropertyGrid and TreeView performs good, so what have they done and what should I do? 但是我可以看到PropertyGrid和TreeView表现良好,所以他们做了什么,我该怎么办?

Edit: 编辑:

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        using (var suspend = Dispatcher.DisableProcessing())
        {
            // Add all children here
            for (int i = 0; i < 200; i++)
            {
                this.propertiesStackPanel.Children.Add(new System.Windows.Controls.Button(){Content = "Testing"});
            }
        }
        stopwatch.Stop();

This still takes about 50 milliseconds. 这仍然需要大约50毫秒。 If I change to my own custom usercontrol it is much higher. 如果我更改为我自己的自定义用户控件,它会更高。 I might add that scrolling is not a problem. 我可能会补充说滚动不是问题。

Edit2: EDIT2:

OK. 好。 It has nothing to do with stackpanel. 它与stackpanel无关。 I have found out that it is because creating UserControls is a very expensive operation. 我发现这是因为创建UserControls是一项非常昂贵的操作。 If you have any other idea of what to do I would gladly hear them :) 如果您对我该做什么有任何其他想法,我很乐意听到他们:)

Edit3: Nothing is going on in the constructor of my usercontrol other than InitializeComponent method. Edit3:除了InitializeComponent方法之外,我的usercontrol的构造函数中没有任何内容。 Here is an example of a usercontrol I am adding. 这是我正在添加的用户控件的示例。

<UserControl
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"
mc:Ignorable="d"
x:Class="PropertyBox.GroupUC"
x:Name="UserControl"
d:DesignWidth="640" d:DesignHeight="480" Background="#FF32B595" BorderThickness="0">

<Grid x:Name="LayoutRoot">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="20px"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border x:Name="border" BorderThickness="0,1" Grid.Column="1">
        <TextBox Text="TextBox" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Right" BorderThickness="0" Padding="0" Visibility="Hidden"/>
    </Border>
    <Label x:Name="groupNameLabel" HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Center" Content="Label" Padding="0" Grid.Column="1"/>
    <Button x:Name="expandButton" HorizontalAlignment="Left" VerticalAlignment="Center" Width="12" Height="12" Content="" Click="ExpandButtonClick" Margin="4,0,0,0" Padding="0" Grid.ColumnSpan="2" d:IsHidden="True"/>
    <Image x:Name="expandButton2" Visibility="Hidden"  Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None"/>
</Grid>

My suspicion is that you're triggering many layout updates while adding your hundreds of children. 我怀疑你在添加数百个孩子的同时触发了许多布局更新。

If that is the bottleneck, you may want to consider doing: 如果这是瓶颈,您可能需要考虑:

using(var suspend = Dispatcher.DisableProcessing())
{
       // Add all children here
}

This will cause the dispatcher to stop processing messages while you add your controls, and do the entire layout and render in one pass at the end. 这将导致调度程序在您添加控件时停止处理消息,并执行整个布局并在最后一次渲染中进行渲染。

If you find that creating many UserControl s is too expensive an operation, what about a solution where you don't need to create UserControl s at all? 如果您发现创建许多UserControl的操作过于昂贵,那么您根本不需要创建UserControl的解决方案呢?

I'm by no means a WPF expert, but what about using data binding a ListBox or ListView against a list of objects each of which represents one property of the object under inspection? 我不是WPF专家,但是如何使用ListBoxListView数据绑定对象列表,每个对象代表被检查对象的一个​​属性? Instead of a StackPanel , you'd have a Listbox or ListView ; 而不是StackPanel ,你有一个ListboxListView ; instead of UserControl s, you would define one or more DataTemplate s. 而不是UserControl ,您将定义一个或多个DataTemplate


Basic code example: 基本代码示例:

Define a type that represents an entry in your custom property grid; 定义表示自定义属性网格中的条目的类型; for example: 例如:

public namespace YourApplication
{
    public class Prop
    {
        public string Name { get; set; }
        public Type Type { get; set; }
    }

    public class Props : List<Prop> { }
}

Then, in XAML (I can't currently check this for 100% correctness, but hopefully you get the idea): 然后,在XAML中(我目前无法检查这个100%的正确性,但希望你能得到这个想法):

<Window ... xmlns:local="clr-namespace:YourApplication">
  <Window.Resources>
    <!-- this Prop list serves only as a demonstration in the XAML designer -->
    <local:Props x:Key="somePropsForDemonstration">
      <local:Prop Name="Name" Type="System.String" />
      <local:Prop Name="Age" Type="System.TimeSpan" />
    </local:Props>
  </Window.Resources>
  <!-- here's your StackPanel replacement; bind it to a real items source -->
  <ListView ItemsSource={StaticResource somePropsForDemonstration}>
    <ListView.ItemsTemplate>
      <!-- this is a UI template that defines how a Prop object gets displayed;
           together with the Prop type, it replaces your UserControl -->
      <DataTemplate DataType="local:Prop">
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Name}" FontWeight="Bold" />
          <TextBlock Text=": " />
          <TextBlock Text="{Binding Type}" FontStyle="Italic" />
        </StackPanel>
      </DataTemplate>
    </ListView.ItemsTemplate>
  </ListView>
</Window>

I seem to remember that there are ways to have "polymorphic" data templates; 我似乎记得有多种方法可以获得“多态”数据模板; ie. 即。 depending on a property's type, you could use a different sub-class of Prop and also use a different data template per item type. 根据属性的类型,您可以使用不同的Prop子类,并且每个项类型也使用不同的数据模板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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