处理TreeView.SelectedItemChanged事件仍然是气泡

时间:2011-05-24 22:16:01

标签: .net wpf com treeview event-bubbling

我有一个TreeView绑定到一个层次结构,通过HierarchicalDataTemplate s包含几个不同的类。当选择树中的项目时,SelectedItemChanged事件当然会愉快地通过父项向上冒泡,就像它应该的那样。应该做什么,但仍然如此,在我将e.Handled设置为true之后,我很高兴继续冒泡。

该事件仍然会触发父项,RoutedPropertyChangedEventArgs看起来与所选的父项完全相同;甚至OriginalSource属性也将指向父项,而不是最初选择的项。 e.Handled当然是false

几乎同样的问题被问到here,但我没有使用EventAggregator或CAL,而且here找到的解决方法没有多大帮助,因为我没有特别关注鼠标事件。

有没有办法精确地获取实际选择的项目或强制停止冒泡的疯狂(不使用我能想到的全局变量来进行非常暴力和不道德的黑客攻击)?

感谢您的任何见解。

2 个答案:

答案 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事件分开的推动。