Как отображать пользовательские элементы управления отдельно

У меня есть UserControl, рисующий контекст данных с помощью Itemscontrol и т. д. У меня есть три объекта одного и того же пользовательского контроля, но с разными контекстами данных, которые инициализируются при запуске программы. Проблема в том, что контекст данных настолько велик, что отрисовывает около 2000 UI-элементов для каждого из объектов. Это занимает так много времени, и что меня действительно раздражает, так это то, что WPF работает в одном UIThread, поэтому мой компьютер использует 1/8 от общей мощности ЦП, чтобы нарисовать это. Как я могу сделать это быстрее?

Код (DynamicShelViewModel — это пользовательский элемент управления)

dataContextBestSellers = new DynamicShelfViewModel(_config, dataRepository, ProductSelectionMode.BestSellers);
dataContextNormal = new DynamicShelfViewModel(_config, dataRepository, ProductSelectionMode.Normal);
dataContextFull = new DynamicShelfViewModel(_config, dataRepository, ProductSelectionMode.AllProducts); 
ScrollGrid = new Grid();
ScrollGrid.Children.Add(dataContextBestSellers);
ScrollGrid.Children.Add(dataContextNormal);
ScrollGrid.Children.Add(dataContextFull);

Это занимает около 2 минут.

Это код XAML DynamicShelfViewModel.

<Grid>
    <ItemsControl x:Name="ShelvesControl" VirtualizingStackPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.VirtualizationMode="Recycling" DataContext="{Binding}" Width="{Binding Size.Width}"
                  VerticalAlignment="Top" Height="{Binding Size.Height}" Grid.Column="0" Grid.Row="1"
                  ItemsSource="{Binding ShelvesInViewPort}">
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>


          <StackPanel VerticalAlignment="Stretch  " HorizontalAlignment="Stretch" Orientation="Vertical" />
        </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <Grid Name="ShelfGrid" Height="{Binding Height}" MaxHeight="{Binding Height}"
                DataContext="{Binding}">
            <Grid.Background>
              <SolidColorBrush Color="{Binding Color}" />
            </Grid.Background>
            <Grid.RowDefinitions>
              <RowDefinition Height="*" />
              <RowDefinition Height="{Binding SplitterSize}" />

            </Grid.RowDefinitions>
            <TextBlock Name="stretchingLabel" Height="{Binding SplitterSize}" Padding="0" Grid.Row="1"
                       VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
              <TextBlock.Background>
                <ImageBrush
ImageSource="{Binding Converter={Utilities1:FancySourceConverter}, ConverterParameter=Images/ShelvesImages/shelfhorisontal.png}" />
              </TextBlock.Background>
              <Grid VerticalAlignment="Stretch"
                    Width="{Binding ElementName=stretchingLabel,Path=ActualWidth}"
                    Height="{Binding ElementName=stretchingLabel,Path=ActualHeight}" HorizontalAlignment="Stretch">
                <Grid.RowDefinitions>
                  <RowDefinition Height="0.24*" />
                  <RowDefinition Height="0.62*" />
                  <RowDefinition Height="0.24*" />
                </Grid.RowDefinitions>
                <my:CategoryLine VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1"
                                 DataContext="{Binding ElementName=ShelfGrid, Path=DataContext}">
                </my:CategoryLine>
              </Grid>
            </TextBlock>

            <TextBlock Name="stretchedLabel" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                       Grid.Row="0" Padding="0">
              <ItemsControl ItemsSource="{Binding Products}" VerticalAlignment="Stretch"
                            HorizontalAlignment="Stretch">
                <ItemsControl.ItemsPanel>
                  <ItemsPanelTemplate>

                    <UniformGrid DataContext="{Binding}"
                                 Width="{Binding ElementName=stretchedLabel, Path=ActualWidth}"
                                 MaxHeight="{Binding ElementName=stretchedLabel, Path=ActualHeight}"
                                 Height="{Binding ElementName=stretchedLabel, Path=ActualHeight}"
                                 Columns="{Binding Path=DataContext.Slots, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" Rows="1"
                                 VerticalAlignment="Stretch" Name="ParentUniformGrid" HorizontalAlignment="Stretch" />

                  </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                  <DataTemplate>


                    <ItemsControl>


                      <Grid Height="{Binding ElementName=stretchedLabel, Path=ActualHeight}"
                            VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
                        <Grid.RowDefinitions>
                          <RowDefinition Height="*" />
                          <RowDefinition
Height="{Binding ElementName=ParentUniformGrid,Path=DataContext.SplitterSize}" />
                        </Grid.RowDefinitions>
                        <Image VerticalAlignment="Bottom" Grid.Row="0"
                               HorizontalAlignment="Center" x:Name="ProductImage" Source="{Binding Converter={Utilities:ProductImageConverter}}"
                               Height="{Binding ImageHeight}" MaxHeight="{Binding ImageHeight}" MouseLeftButtonUp="BuyProductUsingImage" />
                        <Label Padding="0" Margin="0 1 0 0" Grid.Row="1" Background="LightGray"
                               VerticalAlignment="Stretch" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
                               HorizontalAlignment="Stretch">
                          <Label VerticalAlignment="Center" Margin="1"
                                 HorizontalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Background="#fff"
                                 Padding="0" MaxWidth="{Binding ElementName=ShelvesControl, Path=DataContext.PriceLabelWidthSize}"
                                 Width="{Binding ElementName=ShelvesControl, Path=DataContext.PriceLabelWidthSize}">
                            <Viewbox VerticalAlignment="Center"
                                     HorizontalAlignment="Center">
                              <StackPanel Orientation="Horizontal"
                                          VerticalAlignment="Bottom" Margin="1" Background="#fff" HorizontalAlignment="Left">
                                <Label Background="Yellow" Padding="2 0 2 0"
                                       VerticalContentAlignment="Bottom" FontWeight="Bold" Content="{Binding Price}">
                                </Label>
                              </StackPanel>
                            </Viewbox>
                          </Label>
                        </Label>


                      </Grid>


                    </ItemsControl>
                  </DataTemplate>
                </ItemsControl.ItemTemplate>
              </ItemsControl>
            </TextBlock>

          </Grid>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>

  </Grid>
</UserControl>

person Numm3n    schedule 09.11.2011    source источник
comment
Можете ли вы предоставить XAMl UserControl, который привязан к такому большому DataContext?   -  person sll    schedule 09.11.2011
comment
@sll Обновил вопрос с XAML :)   -  person Numm3n    schedule 09.11.2011
comment
Используете ли вы ListView для корневого ItemsControl? Есть ли ScrollViewer? Попробуйте использовать ListView для корневого ItemsControl и установите <ListView ScrollViewer.IsDeferredScrollingEnabled="True"   -  person sll    schedule 09.11.2011
comment
Есть ScrollViewer, но это не проблема. Прокрутка работает идеально. Проблема в том, что для отрисовки всех UIElements требуется очень много времени. Я пытаюсь оптимизировать элементы управления. Лучше всего было бы, если бы у вас был один поток для каждого UserControl. Между каждым UserControl нет никакой зависимости, за исключением того, что они являются UIElement и WPF, использующими только один поток для рисования этих элементов :/ Очень плохо, поскольку большинство современных процессоров имеют более одного ядра.   -  person Numm3n    schedule 09.11.2011
comment
Вы пробовали ScrollViewer.IsDeferredScrollingEnabled=True?   -  person sll    schedule 09.11.2011


Ответы (4)


Выполнение того же самого без ViewBox и привязки к ActualWidth и ActualHeight очень поможет, если возможно заменить более легкой альтернативой.

person Alex Maker    schedule 05.12.2011

Виртуализация ItemsControl — это нечто большее, чем просто настройка VirtualizingStackPanel.IsVirtualizing. Подробнее см. этот вопрос.

По сути, вашему ItemsControl.Template нужен ScrollViewer для включения виртуализации. Я подозреваю, что как только вы правильно виртуализируете свой ItemsControl, вы увидите значительное улучшение производительности.

Виртуализация означает, что отображаются только видимые элементы. При прокрутке контейнеры пользовательского интерфейса используются повторно, и меняется только DataContext за ними. Это означает, что вместо того, чтобы рисовать 2000 записей, вы будете рисовать только около 20 (или столько, сколько будет видно).

person Rachel    schedule 09.11.2011
comment
Как ни странно, все элементы видны сразу :/ - person Numm3n; 09.11.2011
comment
@Numm3n Numm3n Вы пытались установить точки останова и выяснить, какая часть занимает так много времени? Например, это доступ к данным или рендеринг элементов пользовательского интерфейса? - person Rachel; 09.11.2011


Насколько я знаю, есть только один способ получить больше UIThreads:

Каждое окно может иметь свой собственный UIThread (например).

http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/

private void OnCreateNewWindow(object sender, RoutedEventArgs e)
{
    Thread thread = new Thread(() =>
    {
        Window1 w = new Window1();
        w.Show();

        w.Closed += (sender2, e2) =>
        w.Dispatcher.InvokeShutdown();

        System.Windows.Threading.Dispatcher.Run();
    });

    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
}

Не знаю, будет ли это решением для вас, но вы просили больше UIThreads;)

person takko    schedule 12.02.2014