Я новичок в программировании WPF, и у меня проблема с низкой производительностью в моем приложении.
У меня есть вид с сеткой и 2 столбцами. Первый столбец - это ListBox, где - RunTypes, а второй столбец - подробный вид RunType. В этом подробном представлении есть некоторые основные элементы управления для свойств, таких как Label и т. Д., И элемент управления NavBar (из devexpress), где есть RunConfigs. NavBar работает как аккордеонный элемент управления, который показывает все RunConfigs и их детализация может быть расширена.
Мне нужна была опция для управления видимостью и включенным состоянием всех полей, поэтому я создал этот пользовательский элемент управления:
<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 свойства контента для редактирования / просмотра, и я установил их в свойстве контента на основе режима редактирования. Этот пользовательский элемент управления работает как контейнер для каждого поля или каждого нужного мне объекта. Затем видимость поля или объекта может управляться системой, которая управляет этими элементами управления (это не связано с вопросом, это просто объяснение, почему у меня есть эти элементы управления там)
Проблема в том, что когда я щелкаю по списку с помощью RunType, мне приходится ждать пару секунд, пока я не увижу детали. Все элементы управления создаются и затем удаляются, поэтому ничего не запоминается. Коллекция RunType будет изменена, чтобы ее можно было добавить или удалить из. Поэтому элементы управления всегда создаются динамически.
Файлы xaml для RunManagement, RunTypeControl и RunConfigControl находятся здесь:
<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 и просмотра подробностей, то это работает быстро.
Я посчитал количество элементов управления, созданных в процессе. Если runtype имеет 4 свойства и 5 runconfig и каждый runconfig имеет 8 свойств, то я создаю 88 пользовательских элементов управления:
8 пользовательских элементов управления => 4 свойства (4 для режима редактирования и 4 для режима просмотра)
80 пользовательских элементов управления => 5 x 8 свойств runconfig (40 элементов управления для режима редактирования и 40 для режима просмотра)
Это проблема, которая замедляет производительность, но я думал, что WPF может сделать это без проблем для еще большего количества элементов управления. Я делаю что-то не так, мне нужно настроить некоторые элементы управления?
Спасибо за любую помощь.