扩展WPF的TreeView,完成数据绑定和拖拽功能

WPF看了很长时间了,但一直没怎么动手实践,正好任务需要,扩展了WPF的标准TreeView控件,并作为基础控件,支持下面的功能:

  • 数据绑定
  • 拖拽

下载地址: https://download.csdn.net/download/jfyy/11107213

控件做好后的使用方法:

  1. 先定义TreeNode的Model,Model用来定义TreeNode的状态。
public class TreeNode : ViewModelBase, IDargDropMgr
    {
        private List<TreeNode> children;

        public TreeNode(string name)
        {
            Name = name;
            Menu = new ContextMenu();
            Menu.Items.Add(new MenuItem() { Header = "Test" });
            CanDrop = true;
        }
        public List<TreeNode> Children
        {
            get
            {
                return children;
            }
            set
            {
                children = value;
                OnPropertyChanged("Children");
            }
        }
        public string Name
        {
            get;
            set;
        }

        private ContextMenu menu;

        public ContextMenu Menu
        {
            get { return menu; }
            set { menu = value; OnPropertyChanged("Menu"); }
        }

使用派生TreeView的MyTreeView,使用时,创建好Model后,用下面语句就能完成TreeView的创建。

<local:MyTreeView x:Name="MainTreeView" ItemsSource="{Binding TreeNodes}">

具体实现方法:

1. 在网上找到下面的代码,完成TreeNode节点名字和Image的bind。好像必须使用HierarchicalDataTemplate

            <local:MyTreeView x:Name="MainTreeView" ItemsSource="{Binding TreeNodes}">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type local:TreeNode}">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="Resources/save.png" Stretch="None" />
                            <TextBlock Margin="10,0,0,0" Text="{Binding Name}" />
                        </StackPanel>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </local:MyTreeView>

2. 完成菜单项目和其他属相的绑定(CanDragDrop)。

注意TreeView.SelectedItem就是选中的Model(TreeNode ), 用下面的方法就能使用它的Menu和CanDrop属性,完成绑定

    <Window.Resources>
        <Style TargetType="{x:Type local:MyTreeView}">
            <Setter Property="ContextMenu" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=SelectedItem.Menu}"/>
            <Setter Property="AllowDrop" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=SelectedItem.CanDrop}"/>
            <Style.Triggers>
            </Style.Triggers>
        </Style>
    </Window.Resources>

3. 实现拖拽

先定义拖拽接口,定义下面这个广泛的拖拽的接口,并让TreeNode来实现它。

    public interface IDargDropMgr
    {
        bool CanMoveToParent(TreeNode node);
        bool CanMoveToSibling(TreeNode node);
        bool DragDrop(TreeNode node);
    }

在MyTreeView.cs重写下面的方法即可完成拖拽。这些方法没有涉及到具体的业务逻辑,具体的业务逻辑,留给TreeNode或其派生类去实现。

        private TreeNode currentNode;
        private Point _lastMouseDown;
        private TreeViewItem _target;
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                _lastMouseDown = e.GetPosition(this);
            }
            base.OnMouseDown(e);
        }

        protected override void OnDragOver(DragEventArgs e)
        {
            try
            {
                Point currentPosition = e.GetPosition(this);
                if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
                    (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
                {
                    // Verify that this is a valid drop and then store the drop target
                    var item = GetNearestContainer(e.OriginalSource as UIElement).DataContext as TreeNode;
                    if (item != currentNode && currentNode.CanMoveToParent(item))
                    {
                        e.Effects = DragDropEffects.Move;
                    }
                    else
                    {
                        e.Effects = DragDropEffects.None;
                    }
                }
                e.Handled = true;
            }
            catch (Exception)
            {
            }

            base.OnDragOver(e);
        }

        protected override void OnDrop(DragEventArgs e)
        {
            try
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;

                // Verify that this is a valid drop and then store the drop target
                TreeViewItem TargetItem = GetNearestContainer(e.OriginalSource as UIElement);
                var targetNode = TargetItem.DataContext as TreeNode;
                if (targetNode != null && currentNode != null)
                {
                    _target = TargetItem;
                    e.Effects = DragDropEffects.Move;
                }
            }
            catch (Exception)
            {
            }
            base.OnDrop(e);
        }

        
        protected override void OnMouseMove(MouseEventArgs e)
        {
            try
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    Point currentPosition = e.GetPosition(this);
                    if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
                        (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
                    {
                        currentNode = (TreeNode)this.SelectedItem;
                        if (currentNode != null)
                        {
                            DragDropEffects finalDropEffect = DragDrop.DoDragDrop(this, currentNode,
                                DragDropEffects.Move);


                            //Checking target is not null and item is dragging(moving)
                            if (finalDropEffect == DragDropEffects.Move || finalDropEffect == DragDropEffects.Copy)
                            {
                                // A Move drop was accepted
                                currentNode.DragDrop(this.SelectedValue as TreeNode);
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
            }

            base.OnMouseMove(e);
        }

        private TreeViewItem GetNearestContainer(UIElement element)
        {
            // Walk up the element tree to the nearest tree view item.
            TreeViewItem container = element as TreeViewItem;
            while ((container == null) && (element != null))
            {
                element = VisualTreeHelper.GetParent(element) as UIElement;
                container = element as TreeViewItem;
            }
            return container;
        }

DragDrop.DoDragDrop()开始执行拖拽,在没有决定是否允许拖拽之前(OnDrop方法),这个方法会被阻塞,直到OnDrop方法将e.Effects = DragDropEffects.Move设置好后,DoDragDrop后面的代码根据Effects的值决定是否进行拖拽。

在实现中,拖拽总不响应,后来才发现TreeView的AllowDrop属性默认是false。。。(拖拽前提要将其设置成true.)

浪费了我好多时间。。。

猜你喜欢

转载自blog.csdn.net/jfyy/article/details/89214756