Как асинхронно загружать пользовательские элементы управления в панель стека при отображении индикатора выполнения
Я разрабатываю приложение WPF с использованием C#.
Я пытаюсь разобраться с асинхронными потоками, задачами и т. Д.
В основном у меня есть usercontrol, который я добавляю в панель стека на своей странице. Может быть много итераций этого пользовательского контроля, добавленного к панели стека.
i.txtItemDescription.Text = "Item description " + ii.ToString() + ". Put a description here";
i.txtItemTitle.Text = "Item title " + ii.ToString() + ". Put a title here";
MainStack.Children.Add(i);
Это отлично работает. Однако, когда я переношу это приложение на мой планшет с Windows 8.1, это значительно замедлит отображение этих пользовательских контролей (UC).
Я попытался использовать виртуализацию, которая не имела никакого эффекта, поскольку доступ к данным и генерация UC не влияют на производительность. Это физический рисунок элементов управления на экране.
Итак, мой вопрос заключается в следующем:
- Будет ли запуск этого в фоновом потоке способствовать производительности?
- Если да, то как же можно загрузить панель стека с помощью BeginInvoke или Tasks? Я перепробовал почти каждый ответ, упомянутый на этом сайте, и либо получаю ошибку STA, либо он просто не выполняет то, что мне хотелось бы.
- Мои соображения, стоящие за пунктом 2, заключаются в том, что я хотел бы отображать индикатор формы ожидания (IsIndefinite=true) в форме до тех пор, пока не будет загружена панель стека. Но я в полном недоумении. Во всех случаях пользовательский интерфейс форм не обновляет индикатор выполнения до тех пор, пока панель стека не будет полностью загружена. В этом случае уже слишком поздно.
Любые мысли о том, как будут оценены!
Regds
Павел
1 ответ
Во-первых, вы не можете создавать элементы пользовательского интерфейса в другом потоке, отличном от того, в котором они в конечном итоге будут отображаться, поэтому забудьте об этой возможности.
Если физическое рисование элементов управления вызывает проблемы с производительностью, то, вероятно, вы неправильно используете виртуализацию пользовательского интерфейса. Весь смысл виртуализации пользовательского интерфейса состоит в том, чтобы выполнять макет и рендеринг только для тех элементов управления, которые на самом деле находятся в поле зрения, что означает предоставление некоторого вида элементов размещения (например, ItemsControl
) генерировать соответствующие элементы пользовательского интерфейса по мере необходимости. Предварительное заполнение всей панели побеждает цель, а регулярный StackPanel
В любом случае не поддерживает виртуализацию. Я предлагаю следующее:
Вместо использования
StackPanel
напрямую, используйтеItemsControl
сVirtualizingStackPanel
в егоItemsPanelTemplate
,Вместо добавления пользовательских элементов управления вручную, свяжите
ItemsSource
к основному списку предметов, и пустьItemsControl
генерировать контейнеры для элементов по мере их появления.Используйте
ItemTemplate
чтобы определить, как элементы отображаются, например, содержимое шаблона должно быть вашим пользовательским контролем с соответствующими привязками.
Вы можете поэкспериментировать с включением утилизации контейнеров на VirtualizingStackPanel
; в зависимости от вашего варианта использования это может помочь или снизить производительность.
В любом случае, если вы будете следовать этому совету, вам не понадобится индикатор прогресса, поскольку элементы пользовательского интерфейса для ваших элементов будут сгенерированы по мере необходимости, т. Е. Когда они прокручиваются в поле зрения.
Если вы никогда не использовали виртуализацию с простым старым ItemsControl
раньше (стиль по умолчанию не поддерживает его), вы можете использовать стиль ниже в качестве отправной точки. Обратите внимание, что он поддерживает прокрутку, в отличие от по умолчанию ItemsControl
стиль.
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="ScrollViewer.CanContentScroll"
Value="True" />
<Setter Property="ScrollViewer.PanningMode"
Value="Both" />
<Setter Property="Stylus.IsFlicksEnabled"
Value="False" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="VirtualizingStackPanel.IsVirtualizing"
Value="True" />
<Setter Property="VirtualizingStackPanel.VirtualizationMode"
Value="Recycling" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border x:Name="OuterBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Padding}"
Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="OuterBorder"
Property="Background"
Value="{DynamicResource {x:Static apthemes:AssetResourceKeys.ListBackgroundDisabledBrushKey}}" />
</Trigger>
<Trigger Property="IsGrouping"
Value="True">
<Setter Property="ScrollViewer.CanContentScroll"
Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="VirtualizingStackPanel.IsVirtualizing"
Value="True">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>