WPF 如何支撑一个灵活的流程图编辑器?

前言

软件开发领域,流程设计与可视化是提升系统可维护性、增强用户体验的重要手段。无论是工作流管理、业务逻辑编排还是算法流程展示,一个灵活、易用的流程节点编辑框架都能极大地提高开发效率与系统灵活性。

本文将推荐一款基于 WPF 的开源流程节点编辑框架,通过对其核心设计与实现逻辑的解析,带领大家从零开始手写一个具备基础功能的 WPF 流程图编辑器,为实际项目中的可视化流程开发提供有价值的参考。

项目介绍

一款基于WPF的图形化流程节点编辑工具,为大家提供一个直观、高效的流程设计与编辑环境。通过拖拽节点、连接线等操作,可以轻松开发复杂的流程图,实现业务逻辑的可视化表达。

项目功能

1、节点创建与编辑

支持多种类型的节点创建,包括但不限于开始节点、结束节点、处理节点、决策节点等。

通过简单的点击或拖拽操作,在画布上添加、删除或修改节点属性,如节点名称、颜色、形状等。

2、连线管理

节点间通过连线表示流程走向,提供灵活的连线创建与编辑功能。

轻松地在节点间绘制直线、曲线或折线,调整连线的起点与终点,甚至设置连线的条件表达式,实现条件分支。

3、布局调整与边界扩展

满足不同场景下的展示需求,支持画布的缩放、平移以及节点的自动布局功能。

根据需要调整画布大小,通过手势或按钮控制画布的显示范围。同时,框架还提供了边界扩展功能,当节点靠近画布边缘时,自动扩展画布大小,确保所有节点都能完整显示。

4、框选与拖动

支持框选多个节点进行批量操作,如移动、删除等。可以通过鼠标拖拽选择框,选中多个节点后进行统一操作。同时,框架还提供节点的拖动功能,用户可以拖动单个或多个节点到指定位置。

5、数据绑定与交互

支持与后端数据的绑定,将流程图中的节点属性与数据库字段或API接口关联,实现数据的动态展示与交互。

另外,框架还提供了事件处理机制,用户可以自定义节点点击、连线双击等事件的处理逻辑,增强流程图的交互性。

项目特点

1、界面友好

WPF的丰富UI控件与动画效果,提供直观、美观的操作界面。无论是节点的拖拽、连线的绘制还是属性的编辑,都能带来流畅的操作体验。

2、扩展性强

设计遵循模块化原则,各个功能模块相对独立,便于开发者根据需求进行二次开发或功能扩展。同时,框架提供丰富的API接口,方便与其他系统进行集成。

3、交互丰富

支持多种交互方式,如框选、拖动、右键菜单等,可以根据需要选择合适的交互方式,提高操作效率。

项目技术

1、MVVM设计模式

为了实现界面与逻辑的分离,提高代码的可维护性与可测试性,框架采用MVVM(Model-View-ViewModel)设计模式。通过数据绑定与命令机制,实现了视图与模型之间的松耦合。

2、自定义控件开发

针对流程节点编辑的特殊需求,开发一系列自定义控件,如节点控件、连线控件等。控件通过继承WPF的基础控件类,实现了特定的功能与行为。

4、事件处理与委托

通过事件处理与委托机制,实现用户交互的响应与处理。无论是节点的点击、连线的双击还是画布的缩放,都能通过事件处理函数实现相应的逻辑。

项目代码

框选拖动

csharp 复制代码
/// <summary>
/// 添加连线更新方法
/// </summary>
/// <param name="selectedControls">选中的控件</param>
/// <param name="Xoffset">连线位置X轴的偏移量</param>
/// <param name="Yoffset">连线位置Y轴的偏移量</param>
private void UpdateConnectionsForNode(List<Control> selectedControls, double Xoffset = 0, double Yoffset = 0)
{
    // 当前不存在连线的话直接结束
    if (connections.Count == 0) return;

    foreach (XNode node in selectedControls)
    {
        List<Connection> refConns = connections.FindAll(conn => IsChildOf(conn.FromPort, node) || IsChildOf(conn.ToPort, node));

        refConns.ForEach(conn =>
        {
            Point p1 = PointAdd(GetPortCenter(conn.FromPort), new Point(Xoffset, Yoffset));
            Point p2 = PointAdd(GetPortCenter(conn.ToPort), new Point(Xoffset, Yoffset));

            var geometry = new PathGeometry();
            var figure = new PathFigure { StartPoint = p1 };
            var segment = CreateSegment("polyline", p1, p2);
            figure.Segments.Add(segment);
            geometry.Figures.Add(figure);
            conn.Path.Data = geometry;
        });
    }
}

// 点位相加
private Point PointAdd(Point p1, Point p2)
{
    return new Point(p1.X + p2.X, p1.Y + p2.Y);
}

private bool IsChildOf(FrameworkElement child, FrameworkElement parent)
{
    var current = child;
    while (current != null)
    {
        if (current == parent)
            return true;
        current = VisualTreeHelper.GetParent(current) as FrameworkElement;
    }
    return false;
}

线条拖动 | 线条类型切换

csharp 复制代码
private void OutputPort_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (sender is FrameworkElement outputPort)
    {
        fromPort = outputPort;
        startPoint = GetPortCenter(outputPort);

        currentPath = new System.Windows.Shapes.Path
        {
            Stroke = Brushes.MediumPurple,
            StrokeThickness = lineThickness,
            Data = new PathGeometry()
        };
        currentPath.MouseLeftButtonDown += Path_MouseLeftButtonDown;
        currentPath.MouseEnter += Path_MouseEnter;
        currentPath.MouseLeave += Path_MouseLeave;

        MainCanvas.Children.Add(currentPath);
        isConnecting = true;

        e.Handled = true;
    }
}

private Point GetPortCenter(FrameworkElement port)
{
    var point = new Point(port.Width / 2, port.Height / 2);
    // 将当前点相对于port的坐标转换为当前点相对于Canvas的坐标位置,Canvas会先获取point左上角的位置,然后再偏移point.X,point.Y
    var position = port.TranslatePoint(point, MainCanvas);
    return position;
}

private PathSegment CreateSegment(string type, Point startPoint, Point endPoint)
{
    if (string.IsNullOrEmpty(type))
        throw new Exception("type 类型不能为空");
    PathSegment segment;
    if (type == "polyline")
    {
        if (startPoint.X <= endPoint.X - 40) // 两边距离大于40
        {
            double centerX = (startPoint.X + endPoint.X) / 2;
            var polyline = new PolyLineSegment
            {
                Points = new PointCollection()
                {
                    new Point(centerX,startPoint.Y),
                    new Point(centerX,endPoint.Y),
                    new Point(endPoint.X,endPoint.Y)    // 终点
                }
            };
            segment = polyline;
        }
        else
        {
            double centerY = (startPoint.Y + endPoint.Y) / 2;
            var polyline = new PolyLineSegment
            {
                Points = new PointCollection()
                {
                    new Point(startPoint.X + 20,startPoint.Y),
                    new Point(startPoint.X + 20,centerY),
                    new Point(endPoint.X - 20,centerY),
                    new Point(endPoint.X - 20,endPoint.Y),
                    new Point(endPoint.X,endPoint.Y)    // 终点
                }
            };
            segment = polyline;
        }
    }
    else
    {
        var bezier = new BezierSegment
        {
            Point1 = new Point(startPoint.X + 50, startPoint.Y),
            Point2 = new Point(endPoint.X - 50, endPoint.Y),
            Point3 = endPoint
        };
        segment = bezier;
    }

    return segment;
}

项目效果

框架极大地简化复杂流程的设计与实现过程,为项目的快速迭代与交付提供有力支持。

项目源码

源码结构清晰,注释详细,为大家提供良好的学习与参考价值。

代码中包含节点的创建、连线的绘制、布局的调整、框选与拖动的实现等核心功能,是学习WPF开发与流程节点编辑的好示例。

Gitee:https://gitee.com/Zero_0002/process-node-editing-framework

总结

流程节点编辑框架是一款功能强大、易于使用的图形化流程设计工具。通过其丰富的功能特性、友好的操作界面以及强大的技术,框架在软件开发领域展现出了广泛的用途。

关键词

WPF、流程节点编辑、连线管理、框选拖动、边界扩展、数据绑定、MVVM模式、自定义控件、项目源码、交互丰富流程图、节点编辑、可视化、C#、开源项目、JSON、MVVM、拖拽、连线

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号[DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

相关推荐
PfCoder7 小时前
C#中定时器之System.Timers.Timer
c#·.net·visual studio·winform
ahxdyz11 小时前
.NET平台MCP
ai·.net·mcp
の天命喵星人11 小时前
.net 使用NLog记录日志
.net
绿荫阿广13 小时前
将SignalR移植到Esp32—让小智设备无缝连接.NET功能拓展MCP服务
.net·asp.net core·mcp
EmmaXLZHONG17 小时前
Reinforce Learning Concept Flow Chart (强化学习概念流程图)
人工智能·深度学习·机器学习·流程图
LcVong17 小时前
WPF MediaPlayer获取网络视频流当前帧并展示图片完整范例
网络·wpf
有来技术18 小时前
ASP.NET Core 权限管理系统(RBAC)设计与实现|vue3-element-admin .NET 后端
vue.js·后端·c#·asp.net·.net
bugcome_com18 小时前
WPF数据绑定入门:从传统事件到5种绑定模式
wpf
云草桑19 小时前
15分钟快速了解 Odoo
数据库·python·docker·postgresql·.net·odoo
Traced back20 小时前
SQL Server数据自动清理系统最终版(C# WinForms完整源码)
数据库·c#·.net