我有一个TreeView绑定到一个层次结构,通过HierarchicalDataTemplate
s包含几个不同的类。当选择树中的项目时,SelectedItemChanged
事件当然会愉快地通过父项向上冒泡,就像它应该的那样。应该不做什么,但仍然如此,在我将e.Handled
设置为true
之后,我很高兴继续冒泡。
该事件仍然会触发父项,RoutedPropertyChangedEventArgs
看起来与所选的父项完全相同;甚至OriginalSource
属性也将指向父项,而不是最初选择的项。 e.Handled
当然是false
。
几乎同样的问题被问到here,但我没有使用EventAggregator
或CAL,而且here找到的解决方法没有多大帮助,因为我没有特别关注鼠标事件。
有没有办法精确地获取实际选择的项目或强制停止冒泡的疯狂(不使用我能想到的全局变量来进行非常暴力和不道德的黑客攻击)?
感谢您的任何见解。
答案 0 :(得分:2)
您的问题让我感到困惑,因为SelectedItemChanged
事件是TreeView
事件,而不是TreeViewItem
事件。 “嘿,伙计,我的活动远不及你的活动!”
当所选项目发生变化时,TreeView
会在本身,SelectedItemChanged
和(如果未处理)上引发TreeView
事件它开始冒泡到页面的根元素。
编写一个小型测试程序有助于您在需要证明时使用。
这是TreeView
中包含的小Grid
:
<Grid TreeView.SelectedItemChanged="Grid_SelectedItemChanged">
<TreeView SelectedItemChanged="TreeView_SelectedItemChanged">
<TreeViewItem TreeView.SelectedItemChanged="TreeViewItem_SelectedItemChanged" Header="Item1">
<TreeViewItem TreeView.SelectedItemChanged="TreeViewItem_SelectedItemChanged" Header="Item2">
<TreeViewItem TreeView.SelectedItemChanged="TreeViewItem_SelectedItemChanged" Header="Item3"/>
</TreeViewItem>
</TreeViewItem>
</TreeView>
</Grid>
这是测试的代码隐藏:
private void Grid_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SelectedItemChanged(sender, e, "Grid");
}
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SelectedItemChanged(sender, e, "TreeView");
e.Handled = true;
}
private void TreeViewItem_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SelectedItemChanged(sender, e, "TreeViewItem");
}
private void SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e, string handler)
{
Debug.WriteLine("");
Debug.WriteLine(string.Format("SelectedItemChanged: handler = {0}", handler));
Debug.WriteLine(string.Format("e.NewValue.Header = {0}", (e.NewValue as TreeViewItem).Header));
Debug.WriteLine(string.Format("sender = {0}", sender));
Debug.WriteLine(string.Format("e.Source = {0}", e.Source));
Debug.WriteLine(string.Format("e.OriginalSource = {0}", e.OriginalSource));
}
并运行它并单击第一项产生此调试输出:
SelectedItemChanged: handler = TreeView
e.NewValue.Header = Item1
sender = System.Windows.Controls.TreeView Items.Count:1
e.Source = System.Windows.Controls.TreeView Items.Count:1
e.OriginalSource = System.Windows.Controls.TreeView Items.Count:1
表示该事件是TreeView
本身引发的,而e.Handled
设置为true
会阻止Grid
收到该事件。注释掉线,它确实冒出Grid
。
但在任何情况下TreeViewItem
都没有调用SelectedItemChanged
处理程序。
如果事情没有按照您的想法行事,请尝试使用小型测试程序。做实验并解决问题的核心要容易得多!
答案 1 :(得分:2)
在阅读了Rick的回答后,我和一位同事谈过,结果发现之前有同样的问题。我在我的应用程序中尝试了各种各样的东西(发现:TreeViewItem.Selected事件表现完全相同的错误方式)和一个测试项目,并发现在测试应用程序中事件的发射完全符合预期。因此,周围环境(XAML和ViewModel类几乎完全相同)必须存在显着差异,这导致行为的这种差异 - 并且罪魁祸首看起来是COM,精确地托管COM应用程序中的WPF控件。
我的同事的应用程序是使用VSTO的Word扩展,而我的是Visual Studio 2010的VSPackage - 而Word和VS2010在很大程度上仍然是本机代码。另一方面,我的测试应用程序当然是一个小型的纯WPF项目。我添加了一个带有ElementHost的WinForms表单,然后它继承了一个带有TreeView的UserControl,但仍然可以正常工作,所以对我而言,它看起来好像主机COM应用程序在某种程度上干扰了在TreeView和TreeViewItems。
幸运的是,我的同事发现/搜索了一个暂时可以解决的解决方案 - 在完成对事件的实际反应之后,在事件处理程序方法结束时,再次将TreeViewItem设置为选中并关注它:
item.Focus();
item.IsSelected = true;
我们不明白为什么,但这会阻止项目被错误地重新选择(这显然不是WPF意义上的冒泡事件)。
再次感谢Rick在我的脑海中将TreeView和TreeViewItem事件分开的推动。