博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
创建可拖拉的图形及连接图形的线条
阅读量:4032 次
发布时间:2019-05-24

本文共 12559 字,大约阅读时间需要 41 分钟。

使用wpf轻松创建可拖拉的图形,形状或控件,并且在这些对象之间用线条相连,在拖动对象时线条也跟随移动。

就像visio, powerpoint中一样的

 

转自 from:   http://dvuyka.spaces.live.com/Blog/cns!305B02907E9BE19A!155.entry

 

 

WPF. Draggable objects and simple shape connectors

Last weekend I found very good sample of using Thumb class for implementing draggable objects. In two words as all other elements in WPF the Thumb also can be templated. And nothing stops you to get all drag functionality of Thumb turning it to anything you need. That's perfect I think ;)

So how to easily create a simple draggable object based on a Thumb?

Here we declare a Thumb, set the "onDragDelta" handler for the "DragDelta" event and assign to it custom template called "template1".

After that we create the most simpliest shape template that can be found everywhere in the internet

As you can see it turns our thumb to a dummy black shape of (60;30) size.

 

The complete xaml for the window will be as following

As our dummy black shape is still a Thumb element it needs actually one event hanler for the basic drag support implementation - "onDragDelta". Implementing code behind is this way is too boring...

void onDragDelta(object sender, DragDeltaEventArgs e)         {
Canvas.SetLeft(myThumb, Canvas.GetLeft(myThumb) + e.HorizontalChange); Canvas.SetTop(myThumb, Canvas.GetTop(myThumb) + e.VerticalChange); }

We get the original position of the element being dragged and add the new offset values.

 

After playing a couple of minutes with the sample above I decided to complicate it a bit to get something more intresting. I wanted to create some workflow-like draggable objects connected to each other with simple shape connectors using basic line geometry. Upon moving the shapes across the canvas line connectors should followed the objects too. Additionally I wanted to have possibility of adding new shapes by clicking at any place of the canvas with establishing any simple line connector to the existing object.

Something like this

 

As far as we get the task to play, what will be the most simple concept of getting the desired result?

Each shape can possibly be connected to any number of another shapes. For hanling the position of each connector while dragging the object we need to somehow control the start and end points of the line element connected to two shapes. So it becomes obvious that each shape should contain the list of line's start and end points separately so that line positioning and length can be easily updated by the shape itself or outter code.

Let's inherit the basic Thumb class providing the required functionality

MyThumb.cs

using System.Collections.Generic; using System.Windows.Controls.Primitives; using System.Windows.Media; namespace ShapeConnectors {
public class MyThumb : Thumb {
public List
EndLines { get; private set; } public List
StartLines { get; private set; } public MyThumb() : base() {
StartLines = new List
(); EndLines = new List
(); } } }

It's very easy now to change the xaml part to use our extended Thumb element

 
Here we define our hamespace "ShapeConnectors" and prefix "my".
Note that should name the element by using "x:Name" syntax because we put the existing though inherited element to xaml.
As can be seen from the screenshots, our shape should contain an icon and a name elements. Let's change thumb's template to get it working.

We provide a default template for all the draggable objects. Each object contains an image element referencing "Images/user1.png" picture from the resources and contains a text block "User stage" (you can change it to anything you want). Later we will access this template directly from the code, so it is important to name the elements.

Full xaml for our window will be the following

I've added four thumbs by default. Additionally I've created a button called "btnNewAction" that will be enabling the mode of adding new objects by clicking somewhere on the canvas. One button click - one thumb to be created anywhere on the canvas and linked to the predefined "myThumb2" element.

As for line geometry we'll be using the Path element. Each path element will be hosting one line.

So here's going the main part of our application

Window1.xaml.cs

using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace ShapeConnectors {
public partial class Window1 : Window {
// simple flag for enabling "New thumb" mode bool isAddNew = false; // Paths for our predefined thumbs Path path1; Path path2; Path path3; Path path4; public Window1() {
InitializeComponent(); } // Event hanlder for dragging functionality support same to all thumbs private void onDragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { MyThumb thumb = e.Source as MyThumb; double left = Canvas.GetLeft(thumb) + e.HorizontalChange; double top = Canvas.GetTop(thumb) + e.VerticalChange; Canvas.SetLeft(thumb, left); Canvas.SetTop(thumb, top); // Update lines's layouts UpdateLines(thumb); } // This method updates all the starting and ending lines assigned for the given thumb // according to the latest known thumb position on the canvas private void UpdateLines(MyThumb thumb) {
double left = Canvas.GetLeft(thumb); double top = Canvas.GetTop(thumb); for (int i = 0; i < thumb.StartLines.Count; i++) thumb.StartLines[i].StartPoint = new Point(left + thumb.ActualWidth / 2, top + thumb.ActualHeight / 2); for (int i = 0; i < thumb.EndLines.Count; i++) thumb.EndLines[i].EndPoint = new Point(left + thumb.ActualWidth / 2, top + thumb.ActualHeight / 2); } private void Window_Loaded(object sender, RoutedEventArgs e) {
// Move all the predefined thumbs to the front to be over the lines Canvas.SetZIndex(myThumb1, 1); Canvas.SetZIndex(myThumb2, 1); Canvas.SetZIndex(myThumb3, 1); Canvas.SetZIndex(myThumb4, 1); #region Initialize paths for predefined thumbs path1 = new Path(); path1.Stroke = Brushes.Black; path1.StrokeThickness = 1; path2 = new Path(); path2.Stroke = Brushes.Blue; path2.StrokeThickness = 1; path3 = new Path(); path3.Stroke = Brushes.Green; path3.StrokeThickness = 1; path4 = new Path(); path4.Stroke = Brushes.Red; path4.StrokeThickness = 1; myCanvas.Children.Add(path1); myCanvas.Children.Add(path2); myCanvas.Children.Add(path3); myCanvas.Children.Add(path4); #endregion #region Initialize line geometry for predefined thumbs LineGeometry line1 = new LineGeometry(); path1.Data = line1; LineGeometry line2 = new LineGeometry(); path2.Data = line2; LineGeometry line3 = new LineGeometry(); path3.Data = line3; LineGeometry line4 = new LineGeometry(); path4.Data = line4; #endregion #region Setup connections for predefined thumbs myThumb1.StartLines.Add(line1); myThumb2.EndLines.Add(line1); myThumb2.StartLines.Add(line2); myThumb3.EndLines.Add(line2); myThumb3.StartLines.Add(line3); myThumb4.EndLines.Add(line3); myThumb4.StartLines.Add(line4); myThumb1.EndLines.Add(line4); #endregion #region Update lines' layouts UpdateLines(myThumb1); UpdateLines(myThumb2); UpdateLines(myThumb3); UpdateLines(myThumb4); #endregion this.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(Window1_PreviewMouseLeftButtonDown); } // Event handler for creating new thumb element by left mouse click // and visually connecting it to the myThumb2 element void Window1_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
if (isAddNew) {
// Create new thumb object MyThumb newThumb = new MyThumb(); // Assign our custom template to it newThumb.Template = this.Resources["template1"] as ControlTemplate; // Calling ApplyTemplate enables us to navigate the visual tree right now (important!) newThumb.ApplyTemplate(); // Add the "onDragDelta" event handler that is common to all objects newThumb.DragDelta +=new DragDeltaEventHandler(onDragDelta); // Put newly created thumb on the canvas myCanvas.Children.Add(newThumb); // Access the image element of our custom template and assign it to the new image Image img = (Image)newThumb.Template.FindName("tplImage", newThumb); img.Source = new BitmapImage(new Uri("Images/gear_connection.png", UriKind.Relative)); // Access the textblock element of template and change it too TextBlock txt = (TextBlock)newThumb.Template.FindName("tplTextBlock", newThumb); txt.Text = "System action"; // Set the position of the object according to the mouse pointer Point position = e.GetPosition(this); Canvas.SetLeft(newThumb, position.X); Canvas.SetTop(newThumb, position.Y); // Move our thumb to the front to be over the lines Canvas.SetZIndex(newThumb, 1); // Manually update the layout of the thumb (important!) newThumb.UpdateLayout(); // Create new path and put it on the canvas Path newPath = new Path(); newPath.Stroke = Brushes.Black; newPath.StrokeThickness = 1; myCanvas.Children.Add(newPath); // Create new line geometry element and assign the path to it LineGeometry newLine = new LineGeometry(); newPath.Data = newLine; // Predefined "myThumb2" element will host the starting point myThumb2.StartLines.Add(newLine); // Our new thumb will host the ending point of the line newThumb.EndLines.Add(newLine); // Update the layout of line geometry UpdateLines(newThumb); UpdateLines(myThumb2); isAddNew = false; Mouse.OverrideCursor = null; btnNewAction.IsEnabled = true; e.Handled = true; } } // Event handler for enabling new thumb creation by left mouse button click private void btnNewAction_Click(object sender, RoutedEventArgs e) {
isAddNew = true; Mouse.OverrideCursor = Cursors.SizeAll; btnNewAction.IsEnabled = false; } } }

 

Here's what we can have upon playing a bit with the applicaition

This sample if too far from the real life application but I tried to keep the code as simple as possible for all to be able to investigate the process and find out own ways of implementing the desired idea.

Have a nice testing and coding.

转载地址:http://laqbi.baihongyu.com/

你可能感兴趣的文章
K均值算法(K-means)
查看>>
机器学习中的损失函数
查看>>
机器学习中的性能度量
查看>>
机器学习中的优化问题
查看>>
机器学习中的参数估计方法
查看>>
机器学习中的特征工程
查看>>
Softmax数值不稳定问题
查看>>
Spark学习笔记(一)——Spark编程
查看>>
奇异值分解(Singular Value Decomposition, SVD)
查看>>
文本处理—LSA、 LDA
查看>>
文本匹配(Text Matching)
查看>>
机器学习中的正则化方法
查看>>
广告学与在线广告
查看>>
计算广告
查看>>
Web广告--广告定向
查看>>
卷积神经网络中的算术问题(Convolution arithmetic)
查看>>
卷积神经网络在计算机视觉中的演进
查看>>
推荐系统初探
查看>>
分布式机器学习
查看>>
迁移学习(Transfer Learning)
查看>>