【发布时间】:2022-03-07 01:24:09
【问题描述】:
我手头有一个更复杂的代码,但为了问这个问题,我带来了一个更简单的代码示例。
我的应用程序将以特定字体遍历所有字形(预计 500 到 5000 个字形)。每个字形都应该有一个特定的自定义视觉效果,以及一些功能。为此,我认为实现这一目标的最佳方法是为每个字形创建一个 UserControl。
在我所做的检查中,随着我的UserControl 变得越来越复杂,构建它需要更多时间。即使是简单地添加Style 也会对性能产生有意义的影响。
我在此示例中尝试的是在 ListBox 2000 字形中显示。为了注意性能差异,我放了 2 个 ListBoxes - First 绑定到一个简单的 ObservableCollection 字符串。 Second 绑定到我的 UserControl 的 ObservableCollection。
这是我的MainWindowxaml:
<Grid Background="WhiteSmoke">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ListBox Margin="10" ItemsSource="{Binding MyCollection}"></ListBox>
<ListBox Margin="10" Grid.Row="1" ItemsSource="{Binding UCCollection}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"></ListBox>
</Grid>
在后面的代码上,我有 2 个 ObservableCollection,如上所述:
public static ObservableCollection<string> MyCollection { get; set; } = new ObservableCollection<string>();
public static ObservableCollection<MyUserControl> UCCollection { get; set; } = new ObservableCollection<MyUserControl>();
对于string 的第一个列表,我添加如下:
for (int i = 0; i < 2000; i++)
{
string glyph = ((char)(i + 33)).ToString();
string hex = "U+" + i.ToString("X4");
MyCollection.Add($"Index {i}, Hex {hex}: {glyph}");
}
对于MyUserControl 的第二个列表,我添加如下:
for (int i = 0; i < 2000; i++)
{
UCCollection.Add(new MyUserControl(i + 33));
}
MyUserControl xaml 看起来像这样:
<Border Background="Black" BorderBrush="Orange" BorderThickness="2" MinWidth="80" MinHeight="80">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center" Foreground="White" FontSize="40" Text="{Binding Glyph}"/>
<TextBlock HorizontalAlignment="Center" Foreground="OrangeRed" Text="{Binding Index}" Grid.Row="1"/>
<TextBlock HorizontalAlignment="Center" Foreground="White" Text="{Binding Hex}" Grid.Row="2"/>
</Grid>
</Border>
以及MyUserControl的代码背后:
public partial class MyUserControl : UserControl
{
private int OrgIndex { get; set; } = 0;
public string Hex => "U+" + OrgIndex.ToString("X4");
public string Index => OrgIndex.ToString();
public string Glyph => ((char)OrgIndex).ToString();
public MyUserControl(int index)
{
InitializeComponent();
OrgIndex = index;
}
}
为了跟踪性能问题,我使用了Stopwatch。将 2000 个字符串项添加到第一个列表需要 1ms。将 2000 个用户控件添加到第二个列表需要 ~1100 毫秒。而且它只是一个简单的 UserControl,当我向它添加一些东西时,它会花费更多时间并且性能会变得更差。例如,如果我只是将这个 Style 添加到 Border 时间会上升到 ~1900ms:
<Style TargetType="{x:Type Border}" x:Key="BorderMouseOver">
<Setter Property="Background" Value="Black" />
<Setter Property="BorderBrush" Value="Orange"/>
<Setter Property="MinWidth" Value="80"/>
<Setter Property="MinHeight" Value="80"/>
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" Value="True">
<Setter Property="Background" Value="#FF2A3137" />
<Setter Property="BorderBrush" Value="#FF739922"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
我并不完全熟悉 WPF 解决方法,所以非常感谢您的帮助。这是完全错误的方法吗?我已经阅读了一些关于它的帖子,但无法通过以下方式阅读:here、here、here 和 here 等等。
本示例完整工程可以下载Here
【问题讨论】:
-
如果用户必须滚动才能看到其中任何一个,那么您应该使用 mvvm 方法和数据模板来实例化这些用户控件。然后你可以虚拟化并且只构建那些可见的。
-
@Andy - 感谢您的回复。在真实的应用程序中,这些用户控件假设在
WrapPanel中,但用户确实需要滚动。正如我所提到的:我并不完全熟悉 WPF 解决方法,如果您能进一步指导这个示例如何采用 "mvvm 方法和数据模板,这将非常有帮助"。找不到有用答案的帖子。 -
您可以只使用 itemspaneltemplate 来为每个项目生成用户控件。绑定一个可观察的集合或项目视图模型列表。项目视图模型通过绑定提供了用户控件所需的所有信息。
标签: c# wpf performance user-controls