绘制多条徒手折线或曲线图 - 添加撤消功能

2024-01-07

我正在尝试创建一个具有撤消和重做功能的简单绘图应用程序。我假设您可以将要绘制的内容添加到列表中,并调用该列表来绘制所有内容。然后撤消应该只是删除最后添加的项目并再次重新绘制所有内容。问题是,如何将绘制的内容添加到列表中并使用该列表撤消?

我正在使用位图重绘方法。 我是这样画的:

    Point start, end;
    bool painting;
    private List<PointF> myPoints = new List<PointF>();

    private void pnlMain_MouseDown(object sender, MouseEventArgs e)
    {
        start = e.Location;
        painting = true;
    }

    private void pnlMain_MouseUp(object sender, MouseEventArgs e)
    {
        painting = false;
    }

    private void pnlMain_MouseMove(object sender, MouseEventArgs e)
    {
        if (painting == true)
        {
            end = e.Location;
            g.DrawLine(p, start, end);
            myPoints.Add(e.Location);
            pnlMain.Refresh();
            start = end;
        }
    }

    private void btnUndo_Click(object sender, EventArgs e)
    {
        g.Clear(cldFill.Color);
        if (myPoints.Count > 2)
        {
            myPoints.RemoveAt(myPoints.Count - 1);
            g.DrawCurve(p, myPoints.ToArray());
        }
        pnlMain.Refresh();
        //This works but you have to spam it to get rid of
        //a line and does some weird connections.
    }

您需要将行存储在List<List<Point>>。列表中的每个元素都包含使用向下、移动和向上绘制的绘图点。您绘制的下一条线将存储在列表的下一个元素中。每次撤消,都会删除最后一个绘图。

将此控件的一个实例放在您的窗体上,它将为您处理绘图。要执行撤消操作,请调用其Undo method.

using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public class DrawingSurface : Control
{
    public DrawingSurface() { this.DoubleBuffered = true; }
    List<List<Point>> Lines = new List<List<Point>>();
    bool drawing = false;
    protected override void OnMouseDown(MouseEventArgs e) {
        Lines.Add(new List<Point>());
        Lines.Last().Add(e.Location);
        drawing = true;
        base.OnMouseDown(e);
    }
    protected override void OnMouseMove(MouseEventArgs e) {
        if (drawing) { Lines.Last().Add(e.Location); this.Invalidate(); }
        base.OnMouseMove(e);
    }
    protected override void OnMouseUp(MouseEventArgs e) {
        if (drawing) {
            this.drawing = false;
            Lines.Last().Add(e.Location);
            this.Invalidate();
        }
        base.OnMouseUp(e);
    }
    protected override void OnPaint(PaintEventArgs e) {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        foreach (var item in Lines)
            e.Graphics.DrawLines(Pens.Black, item.ToArray()); /*or DrawCurve*/
    }
    public void Undo() {
        if (Lines.Count > 0) { this.Lines.RemoveAt(Lines.Count - 1); this.Invalidate(); }
    }
}

Note

  • 使用此逻辑,您可以简单地使用其他方法实现重做List<List<Point>>。使用以下命令将撤消之前的最后一项复制到重做列表就足够了RedoBuffer.Add(Lines.Last());。然后对于每个重做命令,将重做缓冲区的最后一项添加到Lines并将其从重做缓冲区中删除。您还应该在每次按下鼠标后清除重做缓冲区。
  • 您可以使用其中之一DrawLines or DrawCurve根据您的要求。DrawLines绘制多段线,同时DrawCurve绘制出更平滑的曲线。

  • 我更喜欢封装Lines.Count > 0在像这样的财产bool CanUndo并使其可以从控制之外访问。

  • 这只是一个示例,您可以简单地扩展解决方案。例如,代替List<List<Point>>你可以创建一个Shape类包含List<Point>, LineWidth, LineColor等并使用执行任务List<Shape>.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

绘制多条徒手折线或曲线图 - 添加撤消功能 的相关文章

随机推荐

  • WPF:自定义区域设置

    我今天刚刚发现 WPF 将忽略 CultureInfo CurrentCulture 并始终使用 en US 我也找到了一个很棒的答案here https stackoverflow com questions 2764615 wpf st
  • Android 数据绑定与自定义适配器

    我正在尝试使用Android 的数据绑定功能 http developer android com tools data binding guide html使用自定义适配器和 ListView 我在覆盖自定义适配器时遇到问题getView
  • 如何在 bash for 循环中执行 psql 命令

    我想在 bash 脚本中执行 psql 语句并将结果输出到文件中 我下面的代码可以按需要工作 bin bash query select from mytable psql lt
  • 如何将 FlexBox 项目扩展到全屏而不移动其他 Flex 项目?

    我有一个盒子的弹性盒子布局 单击这些框时 它们会扩展为全屏 问题是 当盒子展开时 它会移动其他弹性元素 导致动画看起来跳跃 弹性布局还可以防止展开的框接触屏幕顶部 这是一个小提琴 向您展示我在说什么 fiddle https jsfiddl
  • WPF 通过覆盖方法更新 UI 不起作用

    我正在尝试从 ViewModel 更新 WPF UI 风景
  • 什么是 JSP 上下文?

    我想知道这个 java 对象到底是什么以及它的主要用途是什么 我看过java文档 但这让我更困惑 它是否有会话 请求 应用程序等关系范围 JspContext JspContext 令人困惑 因为它似乎没有做任何 ServletContex
  • 如何在Python中反转字典(其值是列表)? [复制]

    这个问题在这里已经有答案了 我想编写一个函数 它接收字典作为输入参数并返回输入字典的反向值 其中原始字典的值用作返回字典的键 原始字典的键用作返回字典的值返回的字典解释如下 dict Accurate exact precise exact
  • 如何使用 septice 在明火中创建持久房间?

    我正在使用以下内容iq在 openfire 中创建持久房间的消息 var configiq iq to chatObj getActiveChatRoomName chatObj groupChatService type set c x
  • 使用js将字符串附加到图像src

    我有一个页面 里面有 img src images product whatever image jpg alt 我希望在加载页面时 将字符串 Action thumbnail 附加到 src 的值 使其成为src images produ
  • 为什么stream api不是为异常处理而设计的?

    Fixtures BiConsumer
  • 使用 Python 读取 UTF8 CSV 文件

    我正在尝试使用 Python 读取带有重音字符的 CSV 文件 仅限法语和 或西班牙语字符 基于 csvreader 的 Python 2 5 文档 http docs python org library csv html http do
  • 使用光流进行特征跟踪

    我找到了一个类似的问题 https stackoverflow com questions 9701276 opencv tracking using optical flow 9702540 comment13031247 9702540
  • 将材质图标与样式组件一起使用

    刚刚开始使用样式组件 有没有办法设置第三方图标 例如 Material Design Icon 的样式 这是我到目前为止的代码 但显然它不起作用 相关代码位于内容组件下方 Thanks const MaterialIcon props gt
  • localStorage html5 功能在 Samsung Android 设备上的 WebView 中不起作用

    我有一个用 WebView 包装的 html5 应用程序 为了在页面之间存储和检索用户输入值 我使用本地存储html5 功能 它在我的 Nexus 4 Android 4 4 4 上运行良好 但在 Samsung Galaxy Tab 2
  • 罗马尼亚语区域设置

    经过谷歌搜索一段时间后 我发现 Android 2 3 版本确实支持它 但我没有找到它支持的最低版本 如果 Android 2 2 版本不支持罗马尼亚语那么有什么替代方案 感谢您的关注 这是已解决的类似问题 Android 支持的语言 区域
  • 使用正则表达式检查文本框不允许小数

    我想创建一个 TextChanged 事件来检查输入文本是否符合特定条件 如果不符合则删除最后输入的字符 在本例中 标准是数字 1 位小数和 1 位分数 我正在测试仅用于数字和小数的正则表达式 并遇到了问题 我尝试了几种不同的表达式 我不擅
  • 如何在 Laravel 代码中嵌入视频

    我正在开发一个项目 在该项目中 我将 youtube 视频链接存储在数据库中 然后检索这些链接并使用刀片模板引擎尝试将它们嵌入到页面中 我使用循环将视频放入页面中 由于某种原因 我在浏览器中看不到任何视频 它覆盖了提到的空间 但不渲染任何东
  • QSqlQuery size() 总是返回-1

    QSqlQuery query QString queryText SELECT FROM section query exec queryText qDebug lt lt query size always 1 while query
  • ViewPager Fragments 未在 onCreate 中启动

    我似乎在更新 ViewPager 中使用的片段时遇到问题 无论我尝试使用 onCreate onCreateView 还是 onResume 以下是我在 MainFragment 中设置 ViewPager 的方法 public View
  • 绘制多条徒手折线或曲线图 - 添加撤消功能

    我正在尝试创建一个具有撤消和重做功能的简单绘图应用程序 我假设您可以将要绘制的内容添加到列表中 并调用该列表来绘制所有内容 然后撤消应该只是删除最后添加的项目并再次重新绘制所有内容 问题是 如何将绘制的内容添加到列表中并使用该列表撤消 我正