我有一个ItemsControl数据列表,我想虚拟化,但VirtualizingStackPanel.IsVirtualizing="True"似乎不适用于ItemsControl.
这是真的吗,还是有另一种方法可以做到这一点,我不知道?
要测试我一直在使用以下代码块:
<ItemsControl ItemsSource="{Binding Path=AccountViews.Tables[0]}"
VirtualizingStackPanel.IsVirtualizing="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Initialized="TextBlock_Initialized"
Margin="5,50,5,50" Text="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Run Code Online (Sandbox Code Playgroud)
如果我将其更改ItemsControl为a ListBox,我可以看到该Initialized事件只运行了几次(巨大的边距只是因此我只需要通过一些记录),但是ItemsControl每个项目都会被初始化.
我试过设置ItemsControlPanelTemplate为a VirtualizingStackPanel但似乎没有帮助.
如果启用了TreeView具有各种大小的项目的虚拟化,则会出现多个问题:
垂直滚动条随机更改其大小,并且在查看整个树后不记得元素的大小.用鼠标滚动很难.
在上下滚动之后,ArgumentNullException会从框架代码中抛出.
重现很简单:创建一个新的WPF应用程序,然后将此代码放入MainWindow.xaml中
<Window x:Class="VirtualTreeView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="800" Width="400" Left="0" Top="0"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TreeView x:Name="tvwItems" ItemsSource="{Binding Items}"
VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<DataTemplate>
<Border Height="{Binding Height}" Width="{Binding Height}"
BorderThickness="1" Background="DarkGray" BorderBrush="DarkBlue"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)
并将此代码放入MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.Linq;
namespace VirtualTreeView
{
public partial class MainWindow
{
public ObservableCollection<Item> Items { get; set; }
public MainWindow ()
{
Items = new ObservableCollection<Item>(Enumerable.Range(0, 20).Select(i => new Item {
Height = i*20, …Run Code Online (Sandbox Code Playgroud) 我已经实现类似于在所描述的一个的选择图案此篇使用视图模型来存储IsSelected值,并通过结合所述ListViewItem.IsSelected的视图模型IsSelected:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</ListView.ItemContainerStyle>
Run Code Online (Sandbox Code Playgroud)
它一般工作,但我遇到了严重的问题.通过VirtualizingStackPanel在列表视图中使用a 作为面板,仅ListViewItem创建了可见的.如果我使用"Ctrl + A"选择所有项目,或者使用第一项上的"Shift + Ctrl + End"快捷键组合,则会选择所有项目,但对于不可见项目,ViewModel不会获取其IsSelected设为true.这是合乎逻辑的,因为如果ListViewItem没有创建,绑定就无法工作.
任何人都遇到了同样的问题,并找到了解决方案(除了不使用a VirtualizingStackPanel)?
VirtualizingStackPanel.VirtualizationMode = Recycling/Standard实际发生了什么.
我有一些数据的两个视图:一个列表视图(一个ListBox现在,但我一直意味着切换到ListView)和一个奇特的图形表示在地图上.在任一视图中,用户都可以单击对象,并且将在两个视图中选择该对象.Multiselect也是可能的,因此每个ViewModel实例都有自己的IsSelected属性.
目前我绑定ListBoxItem.IsSelected到ViewModel.IsSelected,但是这只是工作,如果正常的ListBox不是虚拟化(见这里).不幸的是,禁用虚拟化会损害性能,而我的应用程序变得太慢了.
所以我必须再次启用虚拟化.为了保持ViewModel.IsSelected屏幕外项目的属性,我注意到ListBox并且ListView有一个SelectionChanged事件我可以(可能)用来将选择状态从传播ListBox/ListView到ViewModel.
我的问题是,如何反向传播选择状态?该SelectedItems属性ListBox/ListView是只读的!假设用户单击图形表示中的项目,但它在列表中是屏幕外的.如果我只是设置,ViewModel.IsSelected那么ListBox/ListView将不知道新的选择,因此如果用户单击列表中的其他项目,它将无法取消选择该项目.我可以打电话ListBox.ScrollIntoView给ViewModel,但有几个问题:
ListBox/ListView. 那么,亲爱的WPF专家,有什么想法吗?
编辑:我最终切换到Infragistics控件并使用一个丑陋而缓慢的解决方案.关键是,我不再需要答案了.
首先,免责声明,我正在使用.net 3.5的virtualizingstackpanel.如果您在将来的版本中有不同的行为,请告诉我.使用listviews设置测试用例相对简单,您可以对其进行测试.
我在virtualizingstackpanel中有一个itemcontainer样式,它将属性IsSelected绑定到viewmodel.
当我在视图模型中选择一个未选择的项目在屏幕外,然后滚动到该项目时,datacontext(viewmode)和实际listviewitem都将IsSelected属性设置为true(预期行为).触发器正确应用于listviewitem突出显示它.
但是,当我取消选择不在视图中的项目的datacontext然后向下滚动直到该项目在视图中时,在到达项目并创建它时,项目的datacontext现在具有IsSelected = true并且listviewitem的IsSelected属性也为true ,因此listviewitem以触发器中的选择矩形结束(不正确的行为).
这几乎就像ListViewItem的属性在创建项目时都被恢复一样(这对我来说很有意义,但是之后他们应该将datacontext的值绑定到项目之后).
但这似乎并没有发生.此外,未能取消选择该项目并向后滚动以找到它.如果我然后选择/取消它,绑定对该项目没有影响.
我在视频模型中选择一个在屏幕外的项目而不是在我取消选择屏幕外的项目时,看不出它为什么会起作用的逻辑原因.在这两种情况下,新创建的项目都需要反弹到viewmodel的当前值.但是,一个有效,另一个没有.
任何帮助或想法将不胜感激.
编辑:好的,所以我只是不能使用回收模式和绑定似乎.谢谢devhedgehog.会给你赏金,但你需要一个答案.我发誓我之前尝试过,但也许我之前没有处理绑定列表视图中的点击事件,所以我打破了物理选择或其他东西的绑定.我确实记得在某一点尝试过这两种模式,但可能还有其他干扰,所以它不起作用.无论如何它现在有效.
既然你提到了它,我想最好避免保留不必要的代码并继承virtualizingstackpanel而不是虚拟化面板.但我希望能够设置水平滚动范围,这需要我重新实现Iscrollinfo.但是,我无法让virtualizingstackpanel与iscrollinfo进行良好的交互
<ListView
x:Name="TestLV"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Green"
ItemsSource="{Binding Path=AddedItems, Mode=OneWay}"
SnapsToDevicePixels="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="true"
ScrollViewer.IsDeferredScrollingEnabled="False"
Grid.Column ="4"
MouseDown="TestLV_MouseDown"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWay}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid
x:Name="SignalGrid"
Background="Transparent"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border
Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="PART_Header"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
/>
</Border>
<ItemsPresenter
x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="0"
/>
</Grid> …Run Code Online (Sandbox Code Playgroud) 我有一个ListBox可能有很多行的模板化DB记录,包括一个Image,绑定到一个ObservableCollection<MyItem>.有时收藏可以容纳数千件物品.
性能很好,但滚动是默认的跳跃行为.我希望它有平滑的滚动,所以我取消选中ScrollViewer.CanContentScroll.
现在我有平滑的滚动,但性能很可怕:数据在一个单独的线程中检索,并且线程快速完成,但结果显示在中需要10-20秒ListBox.我假设这是因为取消选中ScrollViewer.CanContentScroll将底层更改VirtualizingStackPanel为常规StackPanel,因此在显示结果之前加载整个集合.
所以我的问题是:如何在不牺牲VirtualizingStackPanel行为和性能的情况下保持平滑滚动?
在WPF中stackpanel和virtualizingstackpanel之间有什么区别?
我已经做了一个简单的例子来重现我遇到的问题TreeView.如果我选择了一个项目TreeView,向下滚动,更改Focus,然后TreeView再次选择一个项目,列表开始跳转.
只有我有这种情况才会发生VirtualizingStackPanel.IsVirtualizing="True".这是一个已知的问题吗?有工作吗?
所以重现:
我正在使用.Net 3.5 SP1,我已经尝试过64位XP和Vista.我没有在其他环境上尝试过.
更新: 我已经在.Net 4.0 Beta中测试过,但问题并未发生.某处有修补程序吗?
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"></TextBox>
<TreeView Grid.Row="1"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
>
<TreeView.Items>
<TreeViewItem Header="Header 1" IsExpanded="True">
<TreeViewItem.Items>
<TreeViewItem Header="Item 1" />
<TreeViewItem Header="Item 2" />
<TreeViewItem Header="Item 3" />
<TreeViewItem Header="Item 4" />
<TreeViewItem Header="Item 5" />
<TreeViewItem Header="Item 6" />
<TreeViewItem Header="Item 7" />
<TreeViewItem Header="Item 8" …Run Code Online (Sandbox Code Playgroud) 我有一个奇怪的行为VirtualizingStackPanel.我有一个包含项目的列表TextBlock与TextWrap="Wrap".这是代码:
<ListBox x:Name="messagesList" ItemsSource="{Binding Messages}" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
...
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<CheckBox Style="{Binding Own, Converter={StaticResource MsgTypeToStyle}}"
Tag="{Binding TimeString}"
IsEnabled="True">
<TextBlock Text="{Binding Content}" TextWrapping="Wrap"/>
</CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Run Code Online (Sandbox Code Playgroud)
它工作得很好,但是如果我尝试滚动得非常快(在模拟器上使用鼠标,而不是主要的)滚动中有一些滞后,可能HorizontallOffset有时会计算错误,并且在底部结尾会产生非常奇怪的结果(见图像,右图)说明正常行为).

研究后,我想通了,在组合问题VirtualizingStackPanel及TextBlock.TextWrap="Wrap",如果我从这个夫妻都工作正常删除一个元素.
但我需要虚拟化,因为大项目数量,以及TextWrap正确的文本显示.
所以我考虑自己实现虚拟化面板,请指导我,如何做到这一点,或者如何解决当前的问题?
UPD:问题:
在前两个图像ListBox已经(!)滚动到底部(它不能再向下滚动),但元素放置不正确,正确放置在右图像上.只有在滚动速度非常快时才会发生这种情况.
UPD2:感谢Milan Aggarwal.他在这里提供了一个很好的问题.似乎这真的是一个错误ListBox.提供的解决方法不适合我的方案,因为我需要与ListBox项目内的控件进行交互.现在我正在尝试捕获ManipulationCompleted事件并检查它是否是Inertial,如果是这样意味着滚动并将焦点设置为页面:
void messagesList_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{ …Run Code Online (Sandbox Code Playgroud) wpf ×9
c# ×3
.net ×2
mvvm ×2
scroll ×2
stackpanel ×2
treeview ×2
focus ×1
itemscontrol ×1
listbox ×1
listview ×1
multi-select ×1
panel ×1
performance ×1
silverlight ×1
xaml ×1