4

I have a UserControl drawing the datacontext with Itemscontrol etc. I'm having three objects of the same usercontrol but with different datacontexts which is initialized on startup of the program. The problem is that the datacontext is so big so it's drawing about 2000 UIelements on each of the objects. This takes so much time and what really is annoying me is that WPF is running in a single UIThread, so my computer is using 1/8 of the total CPU power to draw this. How can I make this faster?

The code (DynamicShelViewModel is a usercontrol)

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);

This takes about 2 minutes to accomplish.

This is the XAML code of 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>
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Numm3n
  • 1,021
  • 2
  • 9
  • 26
  • Can you provide XAMl of the UserControl which bound to so large DataContext? – sll Nov 09 '11 at 09:54
  • 1
    @sll Updated the question with the XAML :) – Numm3n Nov 09 '11 at 10:02
  • Are you using ListView for the root ItemsControl? Is there any ScrollViewer? Try out using ListView for root ItemsControl and set ` – sll Nov 09 '11 at 10:07
  • There is a ScrollViewer, but thats not the problem. The scrolling works perfect. The problem is that it uses so much time to draw all the UIElements. I'm trying to optimize the itemscontrol. The best thing would be if you could have one thread for each UserControl. There are no dependency between each UserControl except that they are UIElement and WPF using only one thread for drawing these elements :/ Too bad, as most of the processors today have more than one core. – Numm3n Nov 09 '11 at 13:15
  • Have you tried ScrollViewer.IsDeferredScrollingEnabled="True"? – sll Nov 09 '11 at 13:20

4 Answers4

0

As far as I know, there is only one way to get more UIThreads:

Every Window can have it's own UIThread (e.g).

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();
}

Don't know, if that would be a solution for you, but you asked for more UIThreads ;)

takko
  • 331
  • 2
  • 10
0

For DataGrid, I found this article: http://wpf.codeplex.com/wikipage?title=SelectAll%28%29%20Performance&referringTitle=Tips%20%26%20Tricks&ProjectName=wpf

and also http://wpf.codeplex.com/discussions/66291?ProjectName=wpf

Perhaps it already helps.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Nasenbaer
  • 4,810
  • 11
  • 53
  • 86
0

There's more to virtualizing an ItemsControl than just setting VirtualizingStackPanel.IsVirtualizing. See this question for more details

Basically your ItemsControl.Template needs a ScrollViewer to enable Virtualization. I suspect that once you get your ItemsControl is virtualizing correctly, you'll see a dramatic improvement in performance.

Virtualization means that only the visible items are rendered. When you scroll, the UI containers get re-used and just the DataContext behind them changes. This means that instead of drawing 2000 records, you would only be drawing about 20 (or however many are visible)

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • As strange as it sounds, all the elements are visible at once :/ – Numm3n Nov 09 '11 at 14:02
  • @Numm3n Have you tried setting breakpoints and figuring out which piece takes so long? For example, is it the data access, or is it rendering the UI elements? – Rachel Nov 09 '11 at 14:23
0

Doing the same without the ViewBox and the bindings to ActualWidth and ActualHeight will help a lot, if it is feasible to replace with an lighter alternative.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Alex Maker
  • 1,529
  • 2
  • 19
  • 27