我的原型显示包含“页面”的“文档”
由缩略图图像表示。每个文件都可以
任意数量的页面。例如,可能有
1000个文档,每个5页,或5个文档,1000页
每个,或介于两者之间。文件不包含其他文件。
在我的xaml标记中,我有一个ListBox
,其ItemsTemplate
引用也有ListBox
的innerItemsTemplate。我想要
2级选定的项目,以便我可以执行各种操作
在文档或页面上(删除,合并,移动到新位置等)。
innerItemsTemplate ListBox
使用WrapPanel
作为ItemsPanelTemplate
。
对于我有大量文档的场景
每个页面(例如,10000个文档,每个5页),滚动
由于VirtualizingStackPanel
的UI虚拟化,效果很好。
但是,如果我有大量的页面,我会遇到问题。一个文件
1000个页面一次只显示大约50个(无论在屏幕上适合什么),当我向下滚动时,外部ListBox
移动到下一个文档,跳过950
页面左右不可见。除此之外,没有
VirtualzingWrapPanel
所以app内存确实增加了。
我想知道我是否正确地走这条路,特别是 因为它有点难以解释!我希望能够展示 10000个文档,每个1000页(仅显示适合屏幕的任何内容), 使用UI虚拟化,还可以平滑滚动。
如何确保滚动移动到文档中的所有页面 在它显示下一个文档之前,仍然保持UI虚拟化? 滚动条似乎只移动到下一个文档。
表示“文件”和“页面”似乎合乎逻辑 -
我目前在ListBox
中使用ListBox
的方法?
我非常感谢你的任何想法。 谢谢。
答案 0 :(得分:42)
如果您准备使用反射来访问VirtualizingStackPanel的私有功能,则可以在WPF 4.0中实现平滑滚动VirtualizingStackPanel而不会牺牲虚拟化。您所要做的就是将VirtualizingStackPanel的私有IsPixelBased属性设置为true。
请注意,在.Net 4.5中,不需要这个黑客,因为你可以设置VirtualizingPanel.ScrollUnit =" Pixel"。
为了让它变得非常简单,请参考以下代码:
public static class PixelBasedScrollingBehavior
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(PixelBasedScrollingBehavior), new UIPropertyMetadata(false, HandleIsEnabledChanged));
private static void HandleIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var vsp = d as VirtualizingStackPanel;
if (vsp == null)
{
return;
}
var property = typeof(VirtualizingStackPanel).GetProperty("IsPixelBased",
BindingFlags.NonPublic | BindingFlags.Instance);
if (property == null)
{
throw new InvalidOperationException("Pixel-based scrolling behaviour hack no longer works!");
}
if ((bool)e.NewValue == true)
{
property.SetValue(vsp, true, new object[0]);
}
else
{
property.SetValue(vsp, false, new object[0]);
}
}
}
例如,要在ListBox上使用它,您可以执行以下操作:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel PixelBasedScrollingBehavior.IsEnabled="True">
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
答案 1 :(得分:25)
这里的答案令人惊讶:
ItemsControl
或ListBox
,您将获得您遇到的行为,其中控件按“项目”滚动,以便您一次跳过整个文档,但TreeView
,控件将平滑滚动,以便您可以滚动浏览文档并进入下一个文档,但它仍然可以进行虚拟化。我认为WPF团队选择此行为的原因是TreeView
通常包含的项目大于可见区域,而ListBox
通常不会。
在任何情况下,通过简单地修改TreeView
,在WPF中使ListBox
看起来像ItemsControl
或ItemContainerStyle
一样是微不足道的。这非常简单。您可以自己滚动,也可以只从系统主题文件中复制相应的模板。
所以你会有这样的事情:
<TreeView ItemsSource="{Binding documents}">
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<ContentPresenter /> <!-- put your desired container style here with a ContentPresenter inside -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<DataTemplate TargetType="{x:Type my:Document}">
<Border BorderThickness="2"> <!-- your document frame will be more complicated than this -->
<ItemsControl ItemsSource="{Binding pages}">
...
</ItemsControl>
</Border>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
让基于像素的滚动和ListBox风格的多选项协同工作
如果使用此技术来获取基于像素的滚动,则显示文档的外部ItemsControl不能是ListBox(因为ListBox不是TreeView或TreeViewItem的子类)。因此,您将失去所有ListBox的多选支持。据我所知,没有办法将这两个功能结合使用,而不包括一些功能的代码或其他功能。
如果您需要同一控件中的两组功能,基本上有几个选项:
在TreeViewItem的子类中自己实现多选。使用TreeViewItem而不是TreeView作为外部控件,因为它允许选择多个子项。在ItemsContainerStyle中的模板中:在ContentPresenter周围添加一个CheckBox,模板将CheckBox绑定到IsSelected,并使用控件模板为CheckBox设置样式以获得所需的外观。然后添加自己的鼠标事件处理程序来处理Ctrl-Click和Shift-Click以进行多选。
自己在VirtualizingPanel的子类中实现像素滚动虚拟化。这相对简单,因为VirtualizingStackPanel的大部分复杂性与非像素滚动和容器回收有关。 Dan Crevier's Blog对于了解VirtualizingPanel有一些有用的信息。
答案 2 :(得分:16)
.NET 4.5现在具有VirtualizingPanel.ScrollUnit="ScrollUnit"
属性。我只是将我的一个TreeView转换为ListBox,性能明显更好。
答案 3 :(得分:7)
这对我有用。似乎有几个简单的属性可以做到(.NET 4.5)
<ListBox
ItemsSource="{Binding MyItems}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.ScrollUnit="Pixel"/>
答案 4 :(得分:0)
请允许我在此答案前加上一个问题:用户是否必须始终查看列表中每个项目中的每个缩略图?
如果该问题的答案为“否”,那么限制内部项目模板中可见页面的数量可能是可行的(假设您已指示滚动效果很好,例如5页)和使用更大的单独“选定项目”模板并显示该文档的所有页面? Billy Hollis解释了如何在dnrtv episode 115
列表框中“弹出”所选项目