WPF看了很长时间了,但一直没怎么动手实践,正好任务需要,扩展了WPF的标准TreeView控件,并作为基础控件,支持下面的功能:
- 数据绑定
- 拖拽
下载地址: https://download.csdn.net/download/jfyy/11107213
控件做好后的使用方法:
- 先定义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.)
浪费了我好多时间。。。