这个功能是为另一个小工具作基础实现用的
参考了:
一.使用事件自实现
1.先上全部代码
Xml代码-点击展开
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<HierarchicalDataTemplate x:Key="TreeViewDropItemTemplate" ItemsSource="{Binding Childern}">
<Grid MouseLeftButtonDown="mouseDown" MouseLeftButtonUp="mouseUp">
<TextBlock Name="TV_Tb" Grid.Column="1" Height="20" Text="{Binding TextName}" FontSize="16" HorizontalAlignment="Center"/>
<Rectangle Name="Rect"
MouseEnter="mouseEnter"
MouseLeave="mouseLeave"
Height="5" Width="auto" VerticalAlignment="Bottom" Fill="#00292929" Visibility="Visible" />
<Rectangle Name="Rect2" Height="1" Width="auto" VerticalAlignment="Bottom" Fill="#FF5489C9" Visibility="Collapsed" />
</Grid>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Add}" Value="true" >
<Setter TargetName="Rect2" Property="Visibility" Value="Visible"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid Margin="50">
<Border Background="#FFC7C7C7">
<TreeView x:Name="treeView" Width="240" ItemTemplate="{DynamicResource TreeViewDropItemTemplate}" HorizontalAlignment="Left"></TreeView>
</Border>
</Grid>
</Window>
CS后台代码-点击展开
public class TreeItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string TextName { get; set; }
/// <summary>
/// 父节点
/// </summary>
public TreeItem Parent { get; set; }
/// <summary>
/// 子节点集合(若集合保持Null,则在new后,页面不会更新,所以初始化不为Null)
/// </summary>
public ObservableCollection<TreeItem> Childern { get; set; } = new ObservableCollection<TreeItem>();
bool u_Add;
public bool Add
{
get => u_Add;
set
{
u_Add = value;
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Add"));
}
}
}
public partial class MainWindow : Window
{
ObservableCollection<TreeItem> treeItemlist = new ObservableCollection<TreeItem>();
public MainWindow()
{
InitializeComponent();
initTree();
}
TreeItem moveItem = null;
void mouseDown(object sender, MouseEventArgs e)
{
var obj = e.OriginalSource as FrameworkElement;
var data = obj.DataContext as TreeItem;
moveItem = data;
}
void mouseUp(object sender, MouseEventArgs e)
{
var isadd = false;
if (e.OriginalSource is TextBlock) isadd = true;
var obj = e.OriginalSource as FrameworkElement;
var data = obj.DataContext as TreeItem;
if (moveItem.TextName == data.TextName)
return;
var oldParChi = moveItem.Parent?.Childern ?? treeItemlist;
if (oldParChi == null)
throw new Exception("旧子树空异常");
oldParChi.Remove(moveItem);
//判断为兄弟
if (isadd == false)
{
var list = data.Parent?.Childern ?? treeItemlist;
var borIndex = list.IndexOf(data) + 1;
moveItem.Parent = data.Parent;
list.Insert(borIndex, moveItem);
}
//新节点
if (isadd == true)
{
moveItem.Parent = data;
data.Childern.Add(moveItem);
}
moveItem = null;
data.Add = false;
}
void mouseEnter(object sender, MouseEventArgs e)
{
if (moveItem == null)
return;
var obj = e.OriginalSource as FrameworkElement;
var data = obj.DataContext as TreeItem;
if (moveItem.TextName == data.TextName)
return;
data.Add = true;
}
void mouseLeave(object sender, MouseEventArgs e)
{
if (moveItem == null)
return;
var obj = e.OriginalSource as FrameworkElement;
var data = obj.DataContext as TreeItem;
data.Add = false;
}
private void initTree()
{
var root1 = new TreeItem
{
TextName = "root1"
};
var root2 = new TreeItem
{
TextName = "root2"
};
var root3 = new TreeItem
{
TextName = "root3",
Childern = new ObservableCollection<TreeItem> { new TreeItem { TextName = "233" } }
};
foreach (var item in root3.Childern)
{
item.Parent = root3;
}
treeItemlist.Add(root1);
treeItemlist.Add(root2);
treeItemlist.Add(root3);
treeView.ItemsSource = treeItemlist;
}
}
2.谈谈思路
基于组件TreeView,所以Grid内只有TreeView
并其内容使用自定义的模板TreeViewDropItemTemplate,并设置他们绑定的数据TreeViewDropItemTemplate
和Childern
Triggers
是用于绑定了Add
属性,对Rectangle
中的Visibility
进行是否显示控制
两个Rectangle
分别作为鼠标移动的下划线事件触发,下划线则为加到当前节点下面否则为加到目标节点的中;第二个下划线为在目标节点的下划线样式实现
1.在按下树节点后,记录按下的节点信息,并在鼠标抬起时,获取抬起的目标控件和节点信息
2.移除对应的父节点的自己节点,并将父节点指向新的父节点,在新的父节点中添加自己到其子节点列中
二.继承用户组件,重写已有事件
1.先上全部代码:
MainXml代码-点击展开
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Margin="50" >
<TreeView x:Name="tree" AllowDrop="True">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeItem}" ItemsSource="{Binding Childern}">
<local:UserControl1 ></local:UserControl1>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
自定义组件代码-点击展开
<UserControl x:Class="WpfApp1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid >
<TextBlock Grid.Column="1" Height="20" Text="{Binding TextName}" FontSize="16" HorizontalAlignment="Center"/>
<Grid Height="5" VerticalAlignment="Bottom" Background="#00E87474">
<Rectangle Height="1" Width="auto" VerticalAlignment="Bottom" Fill="#FF5489C9">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Add}" Value="True" >
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
</Grid>
</UserControl>
CS后台代码-点击展开
public partial class UserControl1 : UserControl
{
TreeItem _treeItem;
TreeItem treeItem
{
get
{
if (_treeItem == null)
_treeItem = DataContext as TreeItem;
return _treeItem;
}
}
public UserControl1()
{
InitializeComponent();
}
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
var data = e.Data.GetData("TreeItemModel") as TreeItem;
var tagElement = e.OriginalSource as FrameworkElement;
var tagData = tagElement.DataContext as TreeItem;
var isAdd = e.OriginalSource is Rectangle || e.OriginalSource is Grid;
if (isAdd && IsAddChi(data, tagData) == false)
treeItem.Add = true;
e.Effects = DragDropEffects.Move;
if (IsAddChi(data, tagData))
e.Effects = DragDropEffects.None;
e.Handled = true;
}
protected override void OnDragLeave(DragEventArgs e)
{
base.OnDragLeave(e);
treeItem.Add = false;
var data = e.Data.GetData("TreeItemModel") as TreeItem;
var tagElement = e.OriginalSource as FrameworkElement;
var tagData = tagElement.DataContext as TreeItem;
e.Effects = DragDropEffects.Move;
if (IsAddChi(data, tagData))
e.Effects = DragDropEffects.None;
e.Handled = true;
}
protected override void OnDrop(DragEventArgs e)
{
var data = e.Data.GetData("TreeItemModel") as TreeItem;
var tagElement = e.OriginalSource as FrameworkElement;
var tagData = tagElement.DataContext as TreeItem;
if (IsAddChi(data, tagData))
return;
var isAddIn = false;
if (e.OriginalSource is TextBlock)
isAddIn = true;
var oldParChi = data.Parent?.Childern ?? data.Root;
if (oldParChi == null)
throw new Exception("旧子树空异常");
oldParChi.Remove(data);
//判断为兄弟
if (isAddIn == false)
{
var list = tagData.Parent?.Childern ?? tagData.Root;
var borIndex = list.IndexOf(tagData) + 1;
data.Parent = tagData.Parent;
list.Insert(borIndex, data);
}
//新节点
if (isAddIn == true)
{
data.Parent = tagData;
tagData.Childern.Add(data);
}
if (data.Parent == null)
data.Root = tagData.Root;
treeItem.Add = false;
}
protected override void OnDragOver(DragEventArgs e)
{
var isAdd = e.OriginalSource is Rectangle || e.OriginalSource is Grid;
if (isAdd == true)
e.Effects = DragDropEffects.Move;
else
e.Effects = DragDropEffects.Copy;
var data = e.Data.GetData("TreeItemModel") as TreeItem;
var tagElement = e.OriginalSource as FrameworkElement;
var tagData = tagElement.DataContext as TreeItem;
if (IsAddChi(data, tagData))
e.Effects = DragDropEffects.None;
e.Handled = true;
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
var obj = e.OriginalSource as FrameworkElement;
var data = obj.DataContext as TreeItem;
DataObject dragData = new DataObject();
dragData.SetData("TreeItemModel", data);
dragData.SetData("Object", this);
DragDrop.DoDragDrop(this, dragData, DragDropEffects.Copy | DragDropEffects.Move);
}
bool IsAddChi(TreeItem old, TreeItem @new)
{
while (@new.Parent != null)
{
if (old.TextName == @new.TextName)
return true;
@new = @new.Parent;
}
return false;
}
}
2.谈谈思路
第二种方式主要是利用了自带的AllowDrop
拖拽方法,因为我们主要是实现拖拽,所以用Drop的好处是只需要重写事件就行,不需要在前台额外声明事件函数,二是能实现鼠标的形态变化(拖拽文件出现的移动和复制样式)
其他的思路,基本与第一种方式一样
用这种方式,需要注意的是:
1.AllowDrop
属性需要打开
2.注意子组件的Triggers
写法结构
3.打开拖拽需要执行DragDrop.DoDragDrop()
,不然没有拖拽效果
Q.E.D.