첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

■ Undo/Redo 사용하기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip


Polyline.cs

 

 

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Xml.Serialization;

 

namespace TestProject

{

    /// <summary>

    /// 폴리라인

    /// </summary>

    public class Polyline

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Field

 

        /// <summary>

        /// 색상

        /// </summary>

        [XmlIgnore]

        public Color Color = Color.Black;

 

        /// <summary>

        /// 두께

        /// </summary>

        public int Thickness = 1;

 

        /// <summary>

        /// 대시 스타일

        /// </summary>

        public DashStyle DashStyle = DashStyle.Solid;

 

        /// <summary>

        /// 포인트 리스트

        /// </summary>

        public List<Point> PointList = new List<Point>();

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region ARGB - ARGB

 

        /// <summary>

        /// ARGB

        /// </summary>

        public int ARGB

        {

            get

            {

                return Color.ToArgb();

            }

            set

            {

                Color = Color.FromArgb(value);

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 그리기 - Draw(graphics)

 

        /// <summary>

        /// 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        public void Draw(Graphics graphics)

        {

            using(Pen pen = new Pen(Color, Thickness))

            {

                pen.DashStyle = DashStyle;

 

                if(DashStyle == DashStyle.Custom)

                {

                    pen.DashPattern = new float[] { 10, 2 };

                }

 

                graphics.DrawLines(pen, PointList.ToArray());

            }

        }

 

        #endregion

    }

}

 

 

MainForm.cs

 

 

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.IO;

using System.Windows.Forms;

using System.Xml.Serialization;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 폴리라인 리스트

        /// </summary>

        private List<Polyline> polylineList = new List<Polyline>();

 

        /// <summary>

        /// 신규 폴리라인

        /// </summary>

        private Polyline newPolyline = null;

 

        /// <summary>

        /// 그리기 색상

        /// </summary>

        private Color drawingColor = Color.Black;

 

        /// <summary>

        /// 그리기 두께

        /// </summary>

        private int drawingThickness = 1;

 

        /// <summary>

        /// 그리기 대시 스타일

        /// </summary>

        private DashStyle drawingDashStyle = DashStyle.Solid;

 

 

        /// <summary>

        /// 실행 취소 리스트

        /// </summary>

        private Stack<string> undoList = new Stack<string>();

 

        /// <summary>

        /// 다시 실행하기 리스트

        /// </summary>

        private Stack<string> redoList = new Stack<string>();

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            this.newMenuItem.Click          += newMenuItem_Click;

            this.openMenuItem.Click         += openMenuItem_Click;

            this.saveAsMenuItem.Click       += saveAsMenuItem_Click;

            this.exitMenuItem.Click         += exitMenuItem_Click;

            this.undoMenuItem.Click         += undoMenuItem_Click;

            this.redoMenuItem.Click         += redoMenuItem_Click;

            this.colorBlackMenuItem.Click   += colorMenuItem_Click;

            this.colorRedMenuItem.Click     += colorMenuItem_Click;

            this.colorGreenMenuItem.Click   += colorMenuItem_Click;

            this.colorBlueMenuItem.Click    += colorMenuItem_Click;

            this.colorOrangeMenuItem.Click  += colorMenuItem_Click;

            this.colorYellowMenuItem.Click  += colorMenuItem_Click;

            this.colorLimeMenuItem.Click    += colorMenuItem_Click;

            this.thickness1MenuItem.Click   += thicknessMenuItem_Click;

            this.thickness2MenuItem.Click   += thicknessMenuItem_Click;

            this.thickness3MenuItem.Click   += thicknessMenuItem_Click;

            this.thickness4MenuItem.Click   += thicknessMenuItem_Click;

            this.thickness5MenuItem.Click   += thicknessMenuItem_Click;

            this.styleSolidMenuItem.Click   += styleMenuItem_Click;

            this.styleDashMenuItem.Click    += styleMenuItem_Click;

            this.styleDotMenuItem.Click     += styleMenuItem_Click;

            this.styleCustomMenuItem.Click  += styleMenuItem_Click;

            this.canvasPictureBox.MouseDown += canvasPictureBox_MouseDown;

            this.canvasPictureBox.MouseMove += canvasPictureBox_MouseMove;

            this.canvasPictureBox.MouseUp   += canvasPictureBox_MouseUp;

            this.canvasPictureBox.Paint     += canvasPictureBox_Paint;

 

            #endregion

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 새로 그리기 메뉴 항목 클릭시 처리하기 - newMenuItem_Click(sender, e)

 

        /// <summary>

        /// 새로 그리기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void newMenuItem_Click(object sender, EventArgs e)

        {

            this.polylineList = new List<Polyline>();

 

            this.canvasPictureBox.Refresh();

 

            this.undoList = new Stack<string>();

            this.redoList = new Stack<string>();

 

            SaveSnapshot();

        }

 

        #endregion

        #region 파일 열기 메뉴 항목 클릭시 처리하기 - openMenuItem_Click(sender, e)

 

        /// <summary>

        /// 파일 열기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void openMenuItem_Click(object sender, EventArgs e)

        {

            if(this.openFileDialog.ShowDialog() == DialogResult.OK)

            {

                try

                {

                    XmlSerializer serializer = new XmlSerializer(this.polylineList.GetType());

 

                    using(FileStream stream = new FileStream(this.openFileDialog.FileName, FileMode.Open))

                    {

                        List<Polyline> newPolylineList = (List<Polyline>)serializer.Deserialize(stream);

 

                        this.polylineList = newPolylineList;

 

                        this.canvasPictureBox.Refresh();

 

                        this.undoList = new Stack<string>();

                        this.redoList = new Stack<string>();

 

                        SaveSnapshot();

                    }

                }

                catch(Exception exception)

                {

                    MessageBox.Show(exception.Message);

                }

            }

        }

 

        #endregion

        #region 다른 이름으로 저장하기 메뉴 항목 클릭시 처리하기 - saveAsMenuItem_Click(sender, e)

 

        /// <summary>

        /// 다른 이름으로 저장하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void saveAsMenuItem_Click(object sender, EventArgs e)

        {

            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)

            {

                XmlSerializer serializer = new XmlSerializer(this.polylineList.GetType());

 

                using(StreamWriter writer = new StreamWriter(this.saveFileDialog.FileName))

                {

                    serializer.Serialize(writer, this.polylineList);

 

                    writer.Close();

                }

            }

        }

 

        #endregion

        #region 종료 메뉴 항목 클릭시 처리하기 - exitMenuItem_Click(sender, e)

 

        /// <summary>

        /// 종료 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void exitMenuItem_Click(object sender, EventArgs e)

        {

            Close();

        }

 

        #endregion

        #region 실행 취소 메뉴 항목 클릭시 처리하기 - undoMenuItem_Click(sender, e)

 

        /// <summary>

        /// 실행 취소 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void undoMenuItem_Click(object sender, EventArgs e)

        {

            this.redoList.Push(this.undoList.Pop());

 

            RestoreTopUndoItem();

 

            EnableUndo();

        }

 

        #endregion

        #region 다시 실행하기 메뉴 항목 클릭시 처리하기 - redoMenuItem_Click(sender, e)

 

        /// <summary>

        /// 다시 실행하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void redoMenuItem_Click(object sender, EventArgs e)

        {

            this.undoList.Push(this.redoList.Pop());

 

            RestoreTopUndoItem();

 

            EnableUndo();

        }

 

        #endregion

 

        #region 색상 메뉴 항목 클릭시 처리하기 - colorMenuItem_Click(sender, e)

 

        /// <summary>

        /// 색상 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void colorMenuItem_Click(object sender, EventArgs e)

        {

            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

 

            this.colorButton.Image = menuItem.Image;

 

            this.drawingColor = menuItem.ForeColor;

        }

 

        #endregion

        #region 두께 메뉴 항목 클릭시 처리하기 - thicknessMenuItem_Click(sender, e)

 

        /// <summary>

        /// 두께 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void thicknessMenuItem_Click(object sender, EventArgs e)

        {

            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

 

            this.thicknessButton.Image = menuItem.Image;

 

            this.drawingThickness = int.Parse(menuItem.Text);

        }

 

        #endregion

        #region 스타일 메뉴 항목 클릭시 처리하기 - styleMenuItem_Click(sender, e)

 

        /// <summary>

        /// 스타일 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void styleMenuItem_Click(object sender, EventArgs e)

        {

            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

 

            this.styleButton.Image = menuItem.Image;

 

            switch(menuItem.Text)

            {

                case "Solid"  : this.drawingDashStyle = DashStyle.Solid;  break;

                case "Dash"   : this.drawingDashStyle = DashStyle.Dash;   break;

                case "Dot"    : this.drawingDashStyle = DashStyle.Dot;    break;

                case "Custom" : this.drawingDashStyle = DashStyle.Custom; break;

            }

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 DOWN 처리하기 - canvasPictureBox_MouseDown(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 DOWN 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_MouseDown(object sender, MouseEventArgs e)

        {

            this.newPolyline = new Polyline();

 

            this.polylineList.Add(this.newPolyline);

 

            this.newPolyline.Color     = this.drawingColor;

            this.newPolyline.Thickness = this.drawingThickness;

            this.newPolyline.DashStyle = this.drawingDashStyle;

 

            this.newPolyline.PointList.Add(e.Location);

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 이동시 처리하기 - canvasPictureBox_MouseMove(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 이동시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_MouseMove(object sender, MouseEventArgs e)

        {

            if(this.newPolyline == null)

            {

                return;

            }

 

            this.newPolyline.PointList.Add(e.Location);

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 UP 처리하기 - canvasPictureBox_MouseUp(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 UP 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_MouseUp(object sender, MouseEventArgs e)

        {

            if(this.newPolyline == null)

            {

                return;

            }

 

            if(this.newPolyline.PointList.Count < 2)

            {

                this.polylineList.RemoveAt(this.polylineList.Count - 1);

            }

 

            this.newPolyline = null;

 

            this.canvasPictureBox.Refresh();

 

            SaveSnapshot();

        }

 

        #endregion

        #region 캔버스 픽처 박스 페인트시 처리하기 - canvasPictureBox_Paint(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 페인트시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_Paint(object sender, PaintEventArgs e)

        {

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

 

            e.Graphics.Clear(this.canvasPictureBox.BackColor);

 

            foreach(Polyline polyline in this.polylineList)

            {

                polyline.Draw(e.Graphics);

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 스냅샷 저장하기 - SaveSnapshot()

 

        /// <summary>

        /// 스냅샷 저장하기

        /// </summary>

        private void SaveSnapshot()

        {

            this.undoList.Push(GetSnapshot());

 

            if(this.redoList.Count > 0)

            {

                this.redoList = new Stack<string>();

            }

 

            EnableUndo();

        }

 

        #endregion

        #region 실행 취소 활성화 하기 - EnableUndo()

 

        /// <summary>

        /// 실행 취소 활성화 하기

        /// </summary>

        private void EnableUndo()

        {

            this.undoMenuItem.Enabled = (this.undoList.Count > 0);

            this.redoMenuItem.Enabled = (this.redoList.Count > 0);

        }

 

        #endregion

        #region 스냅샷 구하기 - GetSnapshot()

 

        /// <summary>

        /// 스냅샷 구하기

        /// </summary>

        /// <returns>스냅샷</returns>

        private string GetSnapshot()

        {

            XmlSerializer serializer = new XmlSerializer(this.polylineList.GetType());

 

            using(StringWriter writer = new StringWriter())

            {

                serializer.Serialize(writer, this.polylineList);

 

                return writer.ToString();

            }

        }

 

        #endregion

        #region 최상위 실행 취소 항목 복구하기 - RestoreTopUndoItem()

 

        /// <summary>

        /// 최상위 실행 취소 항목 복구하기

        /// </summary>

        private void RestoreTopUndoItem()

        {

            if(this.undoList.Count == 0)

            {

                this.polylineList = new List<Polyline>();

            }

            else

            {

                XmlSerializer serializer = new XmlSerializer(this.polylineList.GetType());

 

                using(StringReader reader = new StringReader(this.undoList.Peek()))

                {

                    List<Polyline> newPolylineList = (List<Polyline>)serializer.Deserialize(reader);

 

                    this.polylineList = newPolylineList;

                }

            }

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker
TAG

댓글을 달아 주세요