WPF C#TreeView选择的项目在父项目上被触发

时间:2015-04-10 13:24:12

标签: c# wpf treeview event-bubbling

因此,我尝试为使用树视图作为主导航控件的应用程序创建导航(后退/前进)按钮,但是当我向后和向后导航时,选择更改事件会立即触发多个项目即使我只选择一次设置。

下面是一个说明问题的示例应用程序,当您在树视图中选择项目时,它将显示在列表视图中,并且历史记录中的当前位置由列表视图中选择的项目标识。如果您选择项目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;
    }
}
}

非常感谢有关如何解决此问题的任何建议。

1 个答案:

答案 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;
    }