Хобрук: Ваш путь к мастерству в программировании

Нарисуйте несколько полилиний или кривых от руки — добавление функции отмены

Я пытаюсь создать простое приложение для рисования с функциями отмены и повтора. Я предполагаю, что вы можете добавить то, что вы рисуете, в список и вызвать список, чтобы нарисовать все. Затем отмена должна просто удалить последний добавленный элемент и перерисовать все заново. Проблема в том, как мне добавить то, что я нарисовал, в список и использовать этот список для отмены?

Я использую метод перерисовки растрового изображения. Вот как я рисую:

    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.
    }
10.07.2016

  • Вам нужно собрать координаты, которые вы рисуете в List<T> . Код о том, как их собирать, см. в моих комментариях здесь! Также: Вы рисуете неправильно. Кажется, вы кэшируете объект Graphics. Это не верно! Нарисуй все в событии Paint! 11.07.2016
  • Да, без проблем. Если вы реализуете код, который я дал в (все) мои комментарии, на которые я ссылался все, что вам нужно, это добавить нажатие кнопки с помощью curves.Remove(curves.Last)); pnlMain.Invalidate(); 11.07.2016

Ответы:


1

Вам нужно хранить строки в файле List<List<Point>>. Каждый элемент списка содержит точки рисунка, которые вы рисуете с помощью кнопок «вниз», «переместить» и «вверх». Следующая строка, которую вы рисуете, будет храниться в следующем элементе списка. Каждая отмена удалит последний рисунок.

Поместите экземпляр этого элемента управления в свою форму, и он будет обрабатывать рисунок за вас. Также для выполнения отмены вызовите его метод Undo.

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(); }
    }
}

Примечание

  • Используя эту логику, вы можете просто реализовать повтор, используя другой файл List<List<Point>>. Достаточно скопировать последний элемент перед отменой в список повторов с помощью RedoBuffer.Add(Lines.Last());. Тогда для каждой команды повтора достаточно добавить последний элемент буфера повторов в Lines и удалить его из буфера повторов. Вы также должны очищать буфер повторения после каждого нажатия кнопки мыши.
  • Вы можете использовать либо DrawLines, либо DrawCurve в зависимости от ваших требований. DrawLines рисует ломаную линию, а DrawCurve рисует более плавную кривую.

  • Я предпочитаю инкапсулировать Lines.Count > 0 в свойстве, подобном bool CanUndo, и сделать его доступным извне.

  • Это просто пример, и вы можете просто расширить решение. Например, вместо List<List<Point>> вы можете создать класс Shape, содержащий List<Point>, LineWidth, LineColor и т. д., и выполнять задачу, используя List<Shape>.

10.07.2016
  • Это очень хороший класс! Не могли бы вы показать мне пример того, как я могу поговорить с классом и сказать ему рисовать прямоугольники или круги? 11.07.2016
  • Чтобы расширить пример, вы можете создать класс Shape, содержащий абстрактный метод Draw и некоторые производные классы, такие как Rectangle и Circle, реализующие метод и содержащие необходимые свойства для координат. Затем вы можете использовать List<Shape> для хранения объектов, которые хотите нарисовать. Чтобы нарисовать фигуру, у вас должен быть флажок, указывающий, какую фигуру вы рисуете, и, основываясь на этом флаге, добавьте нужную фигуру в этот список. Чтобы нарисовать их, в методе OnPaint вызовите метод Draw каждой фигуры. 11.07.2016
  • Это почти вся идея. Это просто, но нужно больше кода. Также это можно сделать по-разному, так что если вы начнете писать приложение, не стесняйтесь задавать вопросы, связанные с вашей реализацией. Надеюсь, этот ответ и комментарии помогут вам :) 11.07.2016
  • Чтобы констатировать очевидное: это неограниченная отмена :-) - см. здесь пример сохранения работы 11.07.2016
  • Новые материалы

    Шлюз с лицензией OSS, совместимый с Apollo Federation v2, появится в WunderGraph
    Сегодня мы рады сообщить, что мы сотрудничаем с поддерживаемой YC Tailor Technologies, Inc. для внедрения Apollo Federation v2. Реализация будет лицензирована MIT (Engine) и Apache 2.0..

    Это оно
    Ну, я официально уволился с работы! На этой неделе я буду лихорадочно выполнять последние требования Думающего , чтобы я мог сосредоточиться на поиске работы. Что именно это значит?..

    7 полезных библиотек JavaScript, которые вы должны использовать в своем следующем проекте
    Усильте свою разработку JavaScript Есть поговорка «Не нужно изобретать велосипед». Библиотеки — лучший тому пример. Это поможет вам написать сложные и трудоемкие функции простым способом...

    Базовое руководство по переносу концепций обучения в глубокое обучение
    Обзор По мере того, как машинное обучение становится все более мощным и продвинутым, модели, обеспечивающие эту расширенную возможность, становятся все больше и начинают требовать огромного..

    C в C.R.U.D с использованием React-Redux
    Если вы использовали React, возможно, вы знакомы с головной болью, связанной с обратным потоком данных. Передача состояния реквизитам от родительских компонентов к дочерним компонентам может..

    5 обязательных элементов современного инструмента конвейера данных
    В цифровом мире предприятия используют конвейеры данных для перемещения, преобразования и хранения огромных объемов данных. Эти конвейеры составляют основу бизнес-аналитики и играют..

    Случай использования npm3 вместо npm2 для разработки библиотеки
    Некоторое время назад я создал библиотеку на NodeJS, чтобы упростить рендеринг на стороне сервера и клиента. Он использует React и React Router для отображения соответствующего HTML на веб-сайте...