WPF TreeView drag and sort drag and drop arrangement

A demo example is attached at the bottom. Friends who need it can download it for reference

1. Icon

For some reason, the GIF always looks very stuck, but it is actually very smooth.

Due to recording problems, the GIF animation will only be played once. If you need to watch it repeatedly, please close the webpage and reopen it to watch it

 

There is too little WPF information, and there is nothing to find some animations. Recently, I need to use Treeview to drag and sort in my work. However, there are almost no related demo examples on the Internet, so I can only play by myself. In the process, I also learned some knowledge.

2. Principle

I use the translation of the control to achieve the drag effect. When panning to the boundary, it will trigger the up and down scrolling of the TreeView (it will also pan when scrolling); if it leaves the boundary, it will stop scrolling; when it rolls to the top and bottom, it will stop panning; when the finger is lifted up, capture the current mouse position, insert the dragged Item in front of the item at the current mouse position, delete the old data of the data set, and add new data to the added position

Technical points:

1. TreeView code control scrolling

2. Control dragging, panning

3. Get the relative position of the control in other parent controls

4. Use mouse events for panning and sorting after dragging

3. Code analysis

First, we write the layout, which is an ordinary TreeView. However, we need to add a MouseMove event to the control, which is used to determine whether the mouse has moved to the boundary, and then perform a corresponding automatic scrolling action.

<Window x:Class="WpfAppTest.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:WpfAppTest"
        mc:Ignorable="d"
        Loaded="OnViewLoaded"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TreeView x:Name="mTreeView" Width="300" HorizontalAlignment="Left" MouseMove="ParentMove"/>
    </Grid>
</Window>

Next, during initialization, we add the TreeView data to the Dictionary, and then initialize the TreeView Item according to the Dictionary data.

private void initData()
{
	datas["A"] = new List<string>();
	datas["A"].Add("阿轲");
	datas["A"].Add("艾琳");
	datas["A"].Add("安其拉");

	datas["B"] = new List<string>();
	datas["B"].Add("白起");
	datas["B"].Add("百里守约");
	datas["B"].Add("扁鹊");
	datas["B"].Add("百里玄策");
	datas["B"].Add("不知火舞");

	datas["C"] = new List<string>();
	datas["C"].Add("蔡文姬");
	datas["C"].Add("曹操");
	datas["C"].Add("陈咬金");
	datas["C"].Add("嫦娥");
	datas["C"].Add("成吉思汗");

	datas["D"] = new List<string>();
	datas["D"].Add("狄仁杰");
	datas["D"].Add("典韦");
	datas["D"].Add("貂蝉");
	datas["D"].Add("盾山");
	datas["D"].Add("东皇太一");
	datas["D"].Add("妲己");
	datas["D"].Add("大乔");
	datas["D"].Add("达摩");

	datas["G"] = new List<string>();
	datas["G"].Add("宫本武藏");
	datas["G"].Add("高渐离");
	datas["G"].Add("鬼谷子");
	datas["G"].Add("干将莫邪");
	datas["G"].Add("关羽");
	datas["G"].Add("公孙离");

	datas["H"] = new List<string>();
	datas["H"].Add("后裔");
	datas["H"].Add("韩信");
	datas["H"].Add("花木兰");
	datas["H"].Add("黄忠");
}

private void initViews()
{
	foreach (KeyValuePair<string, List<string>> kv in datas)
	{
		TreeViewItem parentItem = new TreeViewItem();
		parentItem.Header = kv.Key;
		parentItem.Tag = kv.Value;
		mTreeView.Items.Add(parentItem);
		foreach (string info in kv.Value)
			parentItem.Items.Add(creatItem(info));
	}
}

private TreeViewItem creatItem(object header)
{
	TreeViewItem newItem = new TreeViewItem();
	newItem.Header = header;
	newItem.PreviewMouseDown += ItemMouseDown;
	newItem.PreviewMouseMove += ItemMove;
	newItem.PreviewMouseUp += ItemMouseUp;
	newItem.Height = 30;
	return newItem;
}

After the view and data are ready, I started to prepare for the dragging action. I added 3 events to the Item above, namely PreviewMouseDown, PreviewMouseMove, and PreviewMouseUp. These 3 events are the key to our dragging Item

PreviewMouseDown: Get the mouse click position as the initial position of the movement. Initialize the translation object transform

PreviewMouseMove: Pan the Item according to the movement of the mouse

PreviewMouseUp: Capture the item to be inserted when the mouse is raised, and insert the item

Didn’t we also add the MouseMove event of TreeView above? This event is mainly for edge detection, and automatic scrolling will also be enabled here.

The code of the PreviewMouseDown event is relatively simple. lastPosition records the coordinates of the last movement, which is used to calculate how far the mouse has moved, and how far we move the Item. _isMouseDown is the drag mark, only when it is pressed, will the mouse move distance be calculated, and transform is the object we use to drag the Item

private void ItemMouseDown(object sender, MouseButtonEventArgs e)
{
	lastPosition = 0;
	var c = sender as UIElement;
	_isMouseDown = true;
	TreeViewItem treeViewItem = (TreeViewItem)sender;
	mouseDownOffset = e.GetPosition(treeViewItem).Y + 5;
	_mouseDownPosition = e.GetPosition(this);
	var transform = c.RenderTransform as TranslateTransform;
	if (transform == null)
	{
		transform = new TranslateTransform();
		c.RenderTransform = transform;
	}
	c.CaptureMouse();
}

The PreviewMouseMove event calculates the distance the mouse moves, and then drags the Item by the corresponding distance. Then give the current Y-axis position to lastPosition for the next calculation.

e.GetPosition(UIElement) is to obtain the position of the mouse in the control. To obtain the relative position of which control, pass the control as a parameter to GetPosition to obtain the position. Addition and subtraction can be done between Position and Position to calculate the straight-line distance between two coordinates

if (_isMouseDown && !isRollingItem[0])
{
	TreeViewItem viewItem = (TreeViewItem)sender;
	dragingItemHeight = viewItem.ActualHeight;
	var c = sender as UIElement;
	var pos = e.GetPosition(this);
	var dp = pos - _mouseDownPosition;
	transform = c.RenderTransform as TranslateTransform;
	//transform.X = _mouseDownControlPosition.X + dp.X;
	//transform.Y = _mouseDownControlPosition.Y + dp.Y;
	transform.Y = transform.Y + (dp.Y - lastPosition);
	lastPosition = dp.Y;
}

The PreviewMouseUp event directly calculates the current position, and inserts the dragged Item into the current position.

When I use the control in the project, I found that dragging up will be on top of other Items, and dragging down will be under the Item (occluded). Of course my Item is my custom layout. When the mouse pops up, it cannot capture which item the mouse stays on in time, and it needs to wait for about 100 milliseconds to get it. So if you encounter a problem, you have to work around it.

Let's look at the code of TreeView's automatic scrolling. This is more important. While the TreeView is scrolling, the dragged Item must also be translated.

private void ScrollingOnMainThread(object obj)
{
	bool isDownScroll = (bool)obj;
	if (isDownScroll)
	{
		if (scrollViewer.VerticalOffset >= (scrollViewer.ExtentHeight - mTreeView.ActualHeight)) return;
		scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + 6);    //向下调节垂直滚动条的位置;
		transform.Y += 6;
	}
	else
	{
		if (scrollViewer.VerticalOffset <= 0) return;
		scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - 6);    //向上调节垂直滚动条的位置;
		transform.Y -= 6;
	}
}

That's all that matters, now your controls should be able to move as well.

If you still need demo example reference or check the effect. Please see the link

WPFTreeView Drag Sort Drag and Drop Arrangement-Desktop System Documentation Resources-CSDN Download

The code in this article is simple to implement and has strong scalability. The Demo example has a total of 267 lines. The code also includes non-logic codes such as data initialization and View control initialization. The code is simple to maintain. 

If you do not understand something during use, you can also ask through private messages. I am often online to help you answer your difficulties during use. The demo example may not be perfect. If you find a problem, you can bring it up for everyone to learn together and improve the project. This article tries the code I handcrafted. If you like it, please like it and support it.

Guess you like

Origin blog.csdn.net/baoolong/article/details/128302036