【问题标题】:How to render usercontrols separately如何分别渲染用户控件
【发布时间】:2011-12-25 04:13:16
【问题描述】:

我有一个 UserControl 使用 Itemscontrol 等绘制数据上下文。我有三个相同用户控件的对象,但具有不同的数据上下文,这些对象在程序启动时初始化。问题是数据上下文太大了,所以它在每个对象上绘制了大约 2000 个 UI 元素。这需要很多时间,真正让我烦恼的是 WPF 在单个 UIThread 中运行,所以我的计算机使用了总 CPU 功率的 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 分钟才能完成。

这是 DynamicShelfViewModel 的 XAML 代码

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

【问题讨论】:

  • 你能提供绑定这么大DataContext的UserControl的XAMl吗?
  • @sll 用 XAML 更新了问题 :)
  • 您是否将 ListView 用于根 ItemsControl?有 ScrollViewer 吗?尝试对根 ItemsControl 使用 ListView 并设置 &lt;ListView ScrollViewer.IsDeferredScrollingEnabled="True"
  • 有一个 ScrollViewer,但这不是问题。滚动效果很好。问题是它花费了太多时间来绘制所有 UIElement。我正在尝试优化项目控制。最好的情况是每个 UserControl 都有一个线程。每个 UserControl 之间没有依赖关系,除了它们是 UIElement 和 WPF,它们只使用一个线程来绘制这些元素:/ 太糟糕了,因为当今大多数处理器都有多个内核。
  • 你试过 ScrollViewer.IsDeferredScrollingEnabled="True" 吗?

标签: .net wpf multithreading user-controls ui-thread


【解决方案1】:

如果不使用ViewBox 以及与ActualWidthActualHeight 的绑定,这样做会很有帮助,如果可以用更轻的替代品替换的话。

【讨论】:

    【解决方案2】:

    虚拟化ItemsControl 不仅仅是设置VirtualizingStackPanel.IsVirtualizing。详情请见this question

    基本上你的ItemsControl.Template 需要ScrollViewer 来启用虚拟化。我怀疑一旦您的ItemsControl 正确虚拟化,您会看到性能显着提高。

    虚拟化意味着只渲染可见的项目。当您滚动时,UI 容器会被重新使用,只是它们后面的 DataContext 会发生变化。这意味着您不会绘制 2000 条记录,而只会绘制大约 20 条(或者无论多少都是可见的)

    【讨论】:

    • 虽然听起来很奇怪,但所有元素都同时可见:/
    • @Numm3n 您是否尝试过设置断点并找出哪一段需要这么长时间?例如,是数据访问,还是渲染 UI 元素?
    【解决方案3】:
    【解决方案4】:

    据我所知,只有一种方法可以获得更多的 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 ;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-17
      • 1970-01-01
      • 1970-01-01
      • 2017-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多