pou*_*usi 18 c# wpf treeview mvvm
有没有办法在虚拟化TreeView中手动选择节点然后将其带入视图?
我在TreeView中使用的数据模型是基于VM-MV模型实现的.每个TreeViewItem的IsSelected属性绑定到ViewModel中的对应属性.我还为TreeView的ItemSelected事件创建了一个监听器,我为所选的TreeViewItem调用了BringIntoView().
这种方法的问题似乎是在创建实际的TreeViewItem之前不会引发ItemSelected事件.因此,启用虚拟化后,节点选择将无法执行任何操作,直到TreeView足够滚动,然后在最终引发事件时"神奇地"跳转到所选节点.
我真的很喜欢使用虚拟化,因为我的树中有数千个节点,并且在启用虚拟化时我已经看到了相当令人印象深刻的性能改进.
spl*_*tor 16
Estifanos Kidane给出的链接破了.他可能意味着"在虚拟化TreeView中更改选择"MSDN示例.但是,此示例显示了如何在树中选择节点,但使用代码隐藏而不是MVVM和绑定,因此在更改绑定的SelectedItem时,它也不会处理缺少的SelectedItemChanged事件.
我能想到的唯一的解决办法就是打破MVVM模式,当确保新值绑定到SelectedItem属性的变化,得到了查看和调用代码隐藏方法(类似于MSDN样品)视图模型属性实际上是在树中选择的.
这是我编写的代码来处理它.假设您的数据项属于Node具有Parent属性的类型:
public class Node
{
public Node Parent { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我写了以下行为类:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
public class NodeTreeSelectionBehavior : Behavior<TreeView>
{
public Node SelectedItem
{
get { return (Node)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(Node), typeof(NodeTreeSelectionBehavior),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var newNode = e.NewValue as Node;
if (newNode == null) return;
var behavior = (NodeTreeSelectionBehavior)d;
var tree = behavior.AssociatedObject;
var nodeDynasty = new List<Node> { newNode };
var parent = newNode.Parent;
while (parent != null)
{
nodeDynasty.Insert(0, parent);
parent = parent.Parent;
}
var currentParent = tree as ItemsControl;
foreach (var node in nodeDynasty)
{
// first try the easy way
var newParent = currentParent.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem;
if (newParent == null)
{
// if this failed, it's probably because of virtualization, and we will have to do it the hard way.
// this code is influenced by TreeViewItem.ExpandRecursive decompiled code, and the MSDN sample at http://code.msdn.microsoft.com/Changing-selection-in-a-6a6242c8/sourcecode?fileId=18862&pathId=753647475
// see also the question at http://stackoverflow.com/q/183636/46635
currentParent.ApplyTemplate();
var itemsPresenter = (ItemsPresenter)currentParent.Template.FindName("ItemsHost", currentParent);
if (itemsPresenter != null)
{
itemsPresenter.ApplyTemplate();
}
else
{
currentParent.UpdateLayout();
}
var virtualizingPanel = GetItemsHost(currentParent) as VirtualizingPanel;
CallEnsureGenerator(virtualizingPanel);
var index = currentParent.Items.IndexOf(node);
if (index < 0)
{
throw new InvalidOperationException("Node '" + node + "' cannot be fount in container");
}
CallBringIndexIntoView(virtualizingPanel, index);
newParent = currentParent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
}
if (newParent == null)
{
throw new InvalidOperationException("Tree view item cannot be found or created for node '" + node + "'");
}
if (node == newNode)
{
newParent.IsSelected = true;
newParent.BringIntoView();
break;
}
newParent.IsExpanded = true;
currentParent = newParent;
}
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
}
private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SelectedItem = e.NewValue as Node;
}
#region Functions to get internal members using reflection
// Some functionality we need is hidden in internal members, so we use reflection to get them
#region ItemsControl.ItemsHost
static readonly PropertyInfo ItemsHostPropertyInfo = typeof(ItemsControl).GetProperty("ItemsHost", BindingFlags.Instance | BindingFlags.NonPublic);
private static Panel GetItemsHost(ItemsControl itemsControl)
{
Debug.Assert(itemsControl != null);
return ItemsHostPropertyInfo.GetValue(itemsControl, null) as Panel;
}
#endregion ItemsControl.ItemsHost
#region Panel.EnsureGenerator
private static readonly MethodInfo EnsureGeneratorMethodInfo = typeof(Panel).GetMethod("EnsureGenerator", BindingFlags.Instance | BindingFlags.NonPublic);
private static void CallEnsureGenerator(Panel panel)
{
Debug.Assert(panel != null);
EnsureGeneratorMethodInfo.Invoke(panel, null);
}
#endregion Panel.EnsureGenerator
#region VirtualizingPanel.BringIndexIntoView
private static readonly MethodInfo BringIndexIntoViewMethodInfo = typeof(VirtualizingPanel).GetMethod("BringIndexIntoView", BindingFlags.Instance | BindingFlags.NonPublic);
private static void CallBringIndexIntoView(VirtualizingPanel virtualizingPanel, int index)
{
Debug.Assert(virtualizingPanel != null);
BringIndexIntoViewMethodInfo.Invoke(virtualizingPanel, new object[] { index });
}
#endregion VirtualizingPanel.BringIndexIntoView
#endregion Functions to get internal members using reflection
}
Run Code Online (Sandbox Code Playgroud)
使用此类,您可以编写如下的XAML:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:MyProject">
<Grid>
<TreeView ItemsSource="{Binding MyItems}"
ScrollViewer.CanContentScroll="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<i:Interaction.Behaviors>
<local:NodeTreeSelectionBehavior SelectedItem="{Binding MySelectedItem}" />
</i:Interaction.Behaviors>
</TreeView>
<Grid>
<UserControl>
Run Code Online (Sandbox Code Playgroud)
下面是一个来自MSDN 问题的示例 public void ScrollToItem(int index)
{
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background,
(System.Windows.Threading.DispatcherOperationCallback)delegate(object arg)
{
int N = fileList.Items.Count;
if (N == 0)
return null;
if (index < 0)
{
fileList.ScrollIntoView(fileList.Items[0]); // scroll to first
}
else
{
if (index < N)
{
fileList.ScrollIntoView(fileList.Items[index]); // scroll to item
}
else
{
fileList.ScrollIntoView(fileList.Items[N - 1]); // scroll to last
}
}
return null;
}, null);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11490 次 |
| 最近记录: |