如何分别渲染用户控件
Posted
技术标签:
【中文标题】如何分别渲染用户控件【英文标题】: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 并设置<ListView ScrollViewer.IsDeferredScrollingEnabled="True"
有一个 ScrollViewer,但这不是问题。滚动效果很好。问题是它花费了太多时间来绘制所有 UIElement。我正在尝试优化项目控制。最好的情况是每个 UserControl 都有一个线程。每个 UserControl 之间没有依赖关系,除了它们是 UIElement 和 WPF,它们只使用一个线程来绘制这些元素:/ 太糟糕了,因为当今大多数处理器都有多个内核。
你试过 ScrollViewer.IsDeferredScrollingEnabled="True" 吗?
【参考方案1】:
如果不使用ViewBox
以及与ActualWidth
和ActualHeight
的绑定,这样做会很有帮助,如果可以用更轻的替代品替换的话。
【讨论】:
【参考方案2】:虚拟化ItemsControl
不仅仅是设置VirtualizingStackPanel.IsVirtualizing
。详情请见this question
基本上你的ItemsControl.Template
需要ScrollViewer
来启用虚拟化。我怀疑一旦您的ItemsControl
正确虚拟化,您会看到性能显着提高。
虚拟化意味着只渲染可见的项目。当您滚动时,UI 容器会被重新使用,只是它们后面的 DataContext
会发生变化。这意味着您不会绘制 2000 条记录,而只会绘制大约 20 条(或者无论多少都是可见的)
【讨论】:
虽然听起来很奇怪,但所有元素都同时可见:/ @Numm3n 您是否尝试过设置断点并找出哪一段需要这么长时间?例如,是数据访问,还是渲染 UI 元素?【参考方案3】:对于DataGrid
,我找到了这篇文章:
http://wpf.codeplex.com/wikipage?title=SelectAll%28%29%20Performance&referringTitle=Tips%20%26%20Tricks&ProjectName=wpf
还有 http://wpf.codeplex.com/discussions/66291?ProjectName=wpf
也许它已经有所帮助。
【讨论】:
【参考方案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 ;)
【讨论】:
以上是关于如何分别渲染用户控件的主要内容,如果未能解决你的问题,请参考以下文章