因此,我尝试为使用树视图作为主导航控件的应用程序创建导航(后退/前进)按钮,但是当我向后和向后导航时,选择更改事件会立即触发多个项目即使我只选择一次设置。
下面是一个说明问题的示例应用程序,当您在树视图中选择项目时,它将显示在列表视图中,并且历史记录中的当前位置由列表视图中选择的项目标识。如果您选择项目1,项目5,项目9和项目16,然后按后退按钮直到您在第一项,这可以正常工作。然而,当向前按压时,当从第5项到第9项时,它还会触发第4项和第1项中的事件,这会导致导航逻辑失败。
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:WpfApplication1"
Title="MainWindow" Height="500" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.ColumnSpan="2" HorizontalAlignment="Center">
<Button x:Name="buttonBack" Content="Back" Width="150" Margin="0,0,20,0" Click="buttonBack_Click"/>
<Button x:Name="buttonForwards" Content="Forwards" Width="150" Margin="20,0,0,0" Click="buttonForwards_Click"/>
</StackPanel>
<TreeView x:Name="tvTest" Margin="2" Grid.Column="0" Grid.Row="1" SelectedItemChanged="tvTest_SelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type this:TreeViewObject}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Content}" Margin="3,0,0,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="true"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<ListView x:Name="lvTest" Grid.Column="1" Grid.Row="1" Margin="2"/>
</Grid>
</Window>
该应用程序的主要代码:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace WpfApplication1
{
public class TreeViewObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private int _id = -1;
public int ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("ID");
}
}
private string _content = "";
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("Content");
}
}
private ObservableCollection<TreeViewObject> _children = new ObservableCollection<TreeViewObject>();
public ObservableCollection<TreeViewObject> Children
{
get { return _children; }
set
{
_children = value;
OnPropertyChanged("Children");
}
}
}
public class TreeViewObjectManager
{
private static TreeViewObjectManager _instance = null;
public static TreeViewObjectManager Instance
{
get { return _instance ?? (_instance = new TreeViewObjectManager()); }
private set { _instance = value; }
}
private ObservableCollection<TreeViewObject> _items = new ObservableCollection<TreeViewObject>();
public ObservableCollection<TreeViewObject> Items
{
get { return _items; }
private set { _items = value; }
}
private List<TreeViewObject> _raw = new List<TreeViewObject>();
private TreeViewObjectManager()
{
TreeViewObject obj1 = new TreeViewObject();
obj1.ID = 1;
obj1.Content = "Item 1";
TreeViewObject obj2 = new TreeViewObject();
obj2.ID = 2;
obj2.Content = "Item 2";
TreeViewObject obj3 = new TreeViewObject();
obj3.ID = 3;
obj3.Content = "Item 3";
TreeViewObject obj4 = new TreeViewObject();
obj4.ID = 4;
obj4.Content = "Item 4";
TreeViewObject obj5 = new TreeViewObject();
obj5.ID = 5;
obj5.Content = "Item 5";
TreeViewObject obj6 = new TreeViewObject();
obj6.ID = 6;
obj6.Content = "Item 6";
TreeViewObject obj7 = new TreeViewObject();
obj7.ID = 7;
obj7.Content = "Item 7";
TreeViewObject obj8 = new TreeViewObject();
obj8.ID = 8;
obj8.Content = "Item 8";
TreeViewObject obj9 = new TreeViewObject();
obj9.ID = 9;
obj9.Content = "Item 9";
TreeViewObject obj10 = new TreeViewObject();
obj10.ID = 10;
obj10.Content = "Item 10";
TreeViewObject obj11 = new TreeViewObject();
obj11.ID = 11;
obj11.Content = "Item 11";
TreeViewObject obj12 = new TreeViewObject();
obj12.ID = 12;
obj12.Content = "Item 12";
TreeViewObject obj13 = new TreeViewObject();
obj13.ID = 13;
obj13.Content = "Item 13";
TreeViewObject obj14 = new TreeViewObject();
obj14.ID = 14;
obj14.Content = "Item 14";
TreeViewObject obj15 = new TreeViewObject();
obj15.ID = 15;
obj15.Content = "Item 15";
TreeViewObject obj16 = new TreeViewObject();
obj16.ID = 16;
obj16.Content = "Item 16";
obj1.Children.Add(obj2);
obj1.Children.Add(obj3);
obj1.Children.Add(obj4);
obj1.Children.Add(obj8);
obj4.Children.Add(obj5);
obj4.Children.Add(obj6);
obj4.Children.Add(obj7);
obj9.Children.Add(obj10);
obj9.Children.Add(obj11);
obj9.Children.Add(obj16);
obj11.Children.Add(obj12);
obj12.Children.Add(obj13);
obj12.Children.Add(obj14);
obj12.Children.Add(obj15);
Items.Add(obj1);
Items.Add(obj9);
_raw.Add(obj1);
_raw.Add(obj2);
_raw.Add(obj3);
_raw.Add(obj4);
_raw.Add(obj5);
_raw.Add(obj6);
_raw.Add(obj7);
_raw.Add(obj8);
_raw.Add(obj9);
_raw.Add(obj10);
_raw.Add(obj11);
_raw.Add(obj12);
_raw.Add(obj13);
_raw.Add(obj14);
_raw.Add(obj15);
_raw.Add(obj16);
}
public TreeViewObject GetObj(int id)
{
foreach(TreeViewObject obj in _raw)
{
if (obj.ID == id)
return obj;
}
return null;
}
}
public partial class MainWindow : Window
{
private ObservableCollection<int> _history = new ObservableCollection<int>();
public ObservableCollection<int> History
{
get { return _history; }
set { _history = value; }
}
private int _currentHistory = 0;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
tvTest.ItemsSource = TreeViewObjectManager.Instance.Items;
lvTest.ItemsSource = History;
}
private void tvTest_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var selected = (sender as TreeView).SelectedItem as TreeViewObject;
if(selected != null)
{
if(_history.Count == 0)
{
_history.Add(selected.ID);
_currentHistory = _history.Count - 1;
}
else
{
if(_history.Count - 1 == _currentHistory)
{
if (_history[_currentHistory] != selected.ID)
{
_history.Add(selected.ID);
_currentHistory = _history.Count - 1;
}
}
else
{
if (_history[_currentHistory] != selected.ID)
{
for (int i = _history.Count - 1; i > _currentHistory; --i)
_history.RemoveAt(i);
_history.Add(selected.ID);
_currentHistory = _history.Count - 1;
}
}
}
}
lvTest.SelectedIndex = _currentHistory;
e.Handled = true;
}
private void buttonBack_Click(object sender, RoutedEventArgs e)
{
if(_currentHistory > 0)
{
int tempID = _history[_currentHistory - 1];
var obj = TreeViewObjectManager.Instance.GetObj(tempID);
if(obj != null)
{
_currentHistory--;
Action uiAction = () =>
{
var container = GetTreeViewItem(tvTest, obj);
if (container != null)
container.IsSelected = true;
};
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, uiAction);
}
}
}
private void buttonForwards_Click(object sender, RoutedEventArgs e)
{
if (_history.Count - 1 > _currentHistory)
{
int tempID = _history[_currentHistory + 1];
var obj = TreeViewObjectManager.Instance.GetObj(tempID);
if (obj != null)
{
_currentHistory++;
Action uiAction = () =>
{
var container = GetTreeViewItem(tvTest, obj);
if (container != null)
container.IsSelected = true;
};
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, uiAction);
}
}
}
public TreeViewItem GetTreeViewItem(ItemsControl cont, object item)
{
if (cont != null)
{
if (cont.DataContext == item)
return cont as TreeViewItem;
if (cont is TreeViewItem && !((TreeViewItem)cont).IsExpanded)
cont.SetValue(TreeViewItem.IsExpandedProperty, true);
cont.ApplyTemplate();
ItemsPresenter itemsPres = (ItemsPresenter)cont.Template.FindName("ItemsHost", cont);
if (itemsPres != null)
itemsPres.ApplyTemplate();
else
{
itemsPres = FindVisualChild<ItemsPresenter>(cont);
if (itemsPres == null)
{
cont.UpdateLayout();
itemsPres = FindVisualChild<ItemsPresenter>(cont);
}
}
System.Windows.Controls.Panel itemsHostPanel = (System.Windows.Controls.Panel)VisualTreeHelper.GetChild(itemsPres, 0);
UIElementCollection children = itemsHostPanel.Children;
for (int i = 0, count = cont.Items.Count; i < count; ++i)
{
TreeViewItem subCont;
subCont = (TreeViewItem)cont.ItemContainerGenerator.ContainerFromIndex(i);
subCont.BringIntoView();
if (subCont != null)
{
TreeViewItem result = GetTreeViewItem(subCont, item);
if (result != null)
return result;
else
subCont.IsExpanded = false;
}
}
}
return null;
}
private T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;
}
}
}
非常感谢有关如何解决此问题的任何建议。
答案 0 :(得分:0)
我找到了答案,通过删除代码以折叠并展开树视图项目,仍然可以获取项目(请注意,这可能仅在您未在树视图中使用虚拟化时才有效)。因此树视图的结构不会改变,我仍然可以访问该项目。然后在选定的项目更改事件中,我刚刚在选定的树视图项目上添加了对BringIntoView的调用,因此展开树视图以在需要时显示所选对象。
以下是经过调整的GetTreeViewItem方法:
public TreeViewItem GetTreeViewItem(ItemsControl cont, object item)
{
if(cont != null)
{
if (cont.DataContext == item)
return cont as TreeViewItem;
cont.ApplyTemplate();
ItemsPresenter itemsPres = (ItemsPresenter)cont.Template.FindName("ItemsHost", cont);
if (itemsPres != null)
itemsPres.ApplyTemplate();
else
{
itemsPres = FindVisualChild<ItemsPresenter>(cont);
if(itemsPres == null)
{
cont.UpdateLayout();
itemsPres = FindVisualChild<ItemsPresenter>(cont);
}
}
System.Windows.Controls.Panel itemsHostPanel = (System.Windows.Controls.Panel)VisualTreeHelper.GetChild(itemsPres, 0);
UIElementCollection children = itemsHostPanel.Children;
for(int i = 0, count = cont.Items.Count; i < count; ++i)
{
TreeViewItem subCont;
subCont = (TreeViewItem)cont.ItemContainerGenerator.ContainerFromIndex(i);
if(subCont != null)
{
TreeViewItem result = GetTreeViewItem(subCont, item);
if (result != null)
return result;
}
}
}
return null;
}