c# - 使用太多自定义控件时 WPF 会降低性能
问题描述
我是 WPF 编程的新手,我的应用程序性能很慢。
我有一个带有网格和 2 列的视图。第一列是 ListBox,其中是 RunTypes,第二列是 RunType 的详细视图。在此详细视图中,有一些用于标签等属性的基本控件和一个 NavBar 控件(来自 devexpress),其中是 RunConfigs。NavBar 的工作方式类似于手风琴控件,可显示所有 RunConfig,并且可以展开它们的详细信息。
我需要一个选项来控制所有字段的可见性和启用状态,所以我创建了这个自定义控件:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:ManualDistill.Controls"
xmlns:converters="clr-namespace:ManualDistill.Converters">
<Style TargetType="{x:Type controls:EditorControl}" BasedOn="{StaticResource {x:Type ContentControl}}">
<Style.Triggers>
<Trigger Property="IsInEditMode" Value="True">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource Self}, Path=EditContent}" />
</Trigger>
<Trigger Property="IsInEditMode" Value="False">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource Self}, Path=ViewContent}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
和cs文件:
public class EditorControl: ContentControl
{
public EditorControl()
{
DefaultStyleKey = typeof(EditorControl);
//DefaultStyleKeyProperty.OverrideMetadata(typeof(EditorControl), new FrameworkPropertyMetadata(typeof(EditorControl)));
Messenger.Default.Register<LayoutChangeMessage>(this,
(message) =>
{
if (this.Feature == message.Feature && this.DisplayField == message.DisplayField)
{
if (message.LayoutData != null)
{
DispatcherHelper.RunAsync(() =>
{
ResetLayout();
SetLayout(message.LayoutData);
});
}
}
});
}
#region DependencyProperties
/// <summary>
/// If the control is currently in edit mode
/// </summary>
public bool IsInEditMode
{
get { return (bool)GetValue(IsInEditModeProperty); }
set { SetValue(IsInEditModeProperty, value); }
}
public static readonly DependencyProperty IsInEditModeProperty =
DependencyProperty.Register("IsInEditMode",
typeof(bool),
typeof(EditorControl),
new PropertyMetadata(false));
public string DisplayField
{
get { return (string)GetValue(DisplayFieldProperty); }
set { SetValue(DisplayFieldProperty, value); }
}
public static readonly DependencyProperty DisplayFieldProperty =
DependencyProperty.Register("DisplayField",
typeof(string),
typeof(EditorControl),
new PropertyMetadata(""));
public string Feature
{
get { return (string)GetValue(FeatureProperty); }
set { SetValue(FeatureProperty, value); }
}
public static readonly DependencyProperty FeatureProperty =
DependencyProperty.Register("Feature",
typeof(string),
typeof(EditorControl),
new PropertyMetadata(""));
public object EditContent
{
get { return GetValue(EditContentProperty); }
set
{
SetValue(EditContentProperty, value);
}
}
public static readonly DependencyProperty EditContentProperty =
DependencyProperty.Register("EditContent",
typeof(object),
typeof(EditorControl),
new FrameworkPropertyMetadata((object)null));
public object ViewContent
{
get { return GetValue(ViewContentProperty); }
set { SetValue(ViewContentProperty, value); }
}
public static readonly DependencyProperty ViewContentProperty =
DependencyProperty.Register("ViewContent",
typeof(object),
typeof(EditorControl),
new FrameworkPropertyMetadata((object)null));
#endregion
public Visibility DefaultVisibility { get; set; }
public bool DefaultIsEnabled { get; set; }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
this.DefaultVisibility = this.Visibility;
this.DefaultIsEnabled = this.IsEnabled;
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
var layouts = StillLayoutManager.Instance.GetActiveLayouts(this.Feature, this.DisplayField);
if (layouts != null)
{
SetLayout(layouts);
}
}
}
private void ResetLayout()
{
this.Visibility = this.DefaultVisibility;
this.IsEnabled = this.DefaultIsEnabled;
}
private void SetLayout(IEnumerable<ILayoutData> layouts)
{
foreach (var data in layouts)
{
this.Visibility = data.Visibility ? Visibility.Visible : Visibility.Collapsed;
this.IsEnabled = data.Editable;
}
}
}
如您所见,我有 2 个用于编辑/查看的内容属性,并根据编辑模式将它们设置在 Content 属性上。然后,此自定义控件就像我需要的每个字段或每个对象的容器一样工作。然后可以由操纵这些控件的系统管理字段或对象的可见性(这与问题无关,它只是解释为什么我在那里有这些控件)
问题是,当我单击带有 RunType 的列表框时,我必须等待几秒钟才能看到详细信息。所有控件都被创建然后被释放,所以什么都不会被记住。RunType 集合将被更改,因此可以添加更多或从中删除。所以控件总是动态创建的。
RunManagement、RunTypeControl 和 RunConfigControl 的 xaml 文件在这里:
<UserControl ...>
<Grid cmn:GridUtils.ColumnDefinitions="Auto, *">
<lc:LayoutGroup Orientation="Vertical" View="GroupBox" Header="{lex:LocTextUpper RunType}" MinWidth="250">
<dxe:ListBoxEdit x:Name="RunTypesList"
ItemsSource="{Binding RunTypes}"
SelectedItem="{Binding SelectedRunType}">
<dxe:ListBoxEdit.ContextMenu>
<ContextMenu>
<MenuItem Header="{lex:LocText Copy}" Command="{Binding CmdCopy}"/>
</ContextMenu>
</dxe:ListBoxEdit.ContextMenu>
<dxe:ListBoxEdit.ItemTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Left" Text="{Binding Label}" TextWrapping="Wrap"/>
</DataTemplate>
</dxe:ListBoxEdit.ItemTemplate>
</dxe:ListBoxEdit>
</lc:LayoutGroup>
<Grid Grid.Column="1">
<local:RunTypeControl DataContext="{Binding SelectedRunType}" />
</Grid>
</Grid>
</UserControl>
...
<UserControl ...>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<lc:LayoutControl Orientation="Horizontal">
<lc:LayoutGroup Orientation="Vertical">
<controls:EditorControl IsInEditMode="{Binding IsInEditMode}">
<controls:EditorControl.ViewContent>
<lc:LayoutItem Label="{lex:LocText Label}">
<TextBlock Text="{Binding Label}" />
</lc:LayoutItem>
</controls:EditorControl.ViewContent>
<controls:EditorControl.EditContent>
<lc:LayoutItem Label="{lex:LocText Label}">
<dxe:TextEdit Text="{Binding Label}"/>
</lc:LayoutItem>
</controls:EditorControl.EditContent>
</controls:EditorControl>
</lc:LayoutGroup>
<lc:LayoutGroup Orientation="Vertical">
<lc:LayoutItem>
</lc:LayoutItem>
</lc:LayoutGroup>
</lc:LayoutControl>
<lc:LayoutGroup Grid.Row="1" Margin="0,5" View="Tabs">
<lc:LayoutGroup Orientation="Horizontal" Header="{Binding TabHeaderRunStates}">
<lc:LayoutItem>
<dxn:NavBarControl Name="navBar" Grid.Column="1" ItemsSource="{Binding RunConfigs}" Margin="10 0 0 0">
<dxn:NavBarControl.ItemTemplate>
<DataTemplate>
<controls:EditorControl IsInEditMode="{Binding IsInEditMode}">
<controls:EditorControl.ViewContent>
<dxn:NavBarGroup DisplaySource="Content" Header="{Binding DisplayName}" IsExpanded="False">
<dxn:NavBarGroup.Content>
<local:RunConfigControl DataContext="{Binding}"/>
</dxn:NavBarGroup.Content>
</dxn:NavBarGroup>
</controls:EditorControl.ViewContent>
<controls:EditorControl.EditContent>
<dxn:NavBarGroup DisplaySource="Content" Header="{Binding DisplayName}" IsExpanded="False">
<dxn:NavBarGroup.Content>
<local:RunConfigControl DataContext="{Binding}"/>
</dxn:NavBarGroup.Content>
</dxn:NavBarGroup>
</controls:EditorControl.EditContent>
</controls:EditorControl>
</DataTemplate>
</dxn:NavBarControl.ItemTemplate>
<dxn:NavBarControl.View>
<dxn:ExplorerBarView Click="ExplorerBarView_Click"
GroupAdding="ExplorerBarView_GroupAdding" />
</dxn:NavBarControl.View>
</dxn:NavBarControl>
</lc:LayoutItem>
</lc:LayoutGroup>
</lc:LayoutGroup>
</Grid>
</UserControl>
...
<UserControl ...>
<AdornerDecorator>
<lc:LayoutControl Orientation="Horizontal">
<lc:LayoutGroup Orientation="Vertical">
<controls:EditorControl IsInEditMode="{Binding IsInEditMode}">
<controls:EditorControl.ViewContent>
<lc:LayoutItem Label="{lex:LocText CloseCut}">
<TextBlock Text="{Binding CloseCut}" />
</lc:LayoutItem>
</controls:EditorControl.ViewContent>
<controls:EditorControl.EditContent>
<lc:LayoutItem Label="{lex:LocText CloseCut}" HorizontalContentAlignment="Right">
<dxe:TextEdit Text="{Binding CloseCut}" Width="200"/>
</lc:LayoutItem>
</controls:EditorControl.EditContent>
</controls:EditorControl>
<controls:EditorControl IsInEditMode="{Binding IsInEditMode}">
<controls:EditorControl.ViewContent>
<lc:LayoutItem Label="{lex:LocText PressureSetpoint}">
<TextBlock Text="{Binding PressureSetpoint}"/>
</lc:LayoutItem>
</controls:EditorControl.ViewContent>
<controls:EditorControl.EditContent>
<lc:LayoutItem Label="{lex:LocText PressureSetpoint}" HorizontalContentAlignment="Right">
<dxe:TextEdit Text="{Binding PressureSetpoint}" Width="200"/>
</lc:LayoutItem>
</controls:EditorControl.EditContent>
</controls:EditorControl>
<controls:EditorControl IsInEditMode="{Binding IsInEditMode}">
<controls:EditorControl.ViewContent>
<lc:LayoutItem Label="{lex:LocText SpinningBand}">
<TextBlock Text="{Binding SpinningBand, Converter={StaticResource BooleanOnOffConverter}}" Width="200" />
</lc:LayoutItem>
</controls:EditorControl.ViewContent>
<controls:EditorControl.EditContent>
<lc:LayoutItem Label="{lex:LocText SpinningBand}" HorizontalContentAlignment="Right">
<dxe:ToggleSwitchEdit IsChecked="{Binding SpinningBand}" IsEnabled="True" ContentPlacement="Both"
CheckedStateContent="{lex:LocText ON}"
UncheckedStateContent="{lex:LocText OFF}"/>
</lc:LayoutItem>
</controls:EditorControl.EditContent>
</controls:EditorControl>
...
</lc:LayoutGroup>
</lc:LayoutControl>
</AdornerDecorator>
</UserControl>
我知道有太多的 xaml 代码我试图缩短它并只显示重要部分。
如果我不使用自定义控件并且只有 2 个列表框用于选择 runtype 和 runconfigs 并查看详细信息,那么它工作得很快。
我计算了在此过程中创建的控件数量。如果一个运行类型有 4 个属性和 5 个运行配置并且每个运行配置有 8 个属性,那么我创建了 88 个自定义控件:
8 个自定义控件 => 4 个属性(4 个用于编辑模式,4 个用于查看模式) 80 个自定义控件 => 5 x 8 个 runconfig 属性(40 个控件用于编辑模式,40 个用于查看模式)
这是降低性能的问题,但我认为 WPF 可以毫无问题地做到这一点,甚至更多的控件。我做错了什么,我需要在控件上设置一些设置吗?
谢谢你的帮助。
解决方案
推荐阅读
- python - 如何在类的元组中调用函数?
- linux - Docker 环境变量未正确传递
- html - 我的带有 zmdi 图标的 html 按钮不起作用
- julia - Julia:将 Float64 转换为日期和日期时间
- javascript - 为什么我需要额外的屏幕点击来隐藏元素?
- android - NDK 未配置(尽管已安装)——如何在没有 Android Studio 的情况下修复
- python - 是否可以在 AWS Lambda 函数之间共享一段代码?
- android - 我无法在 android studio 中安装插件
- mobile - 如何使页面的背景半透明以在 Flutter 中显示上一个屏幕?
- asp.net-core-2.0 - Jwt 认证:JwtTokenBuilder