效果图
已经实现了画矩形和画线条,还有撤销和重做功能。
Form1为上一篇新建的画布界面
在Form1中添加个menuStrip
添加一个窗体Form2,
把它的IsMdiContainer属性设置为true。
添加一个menuStrip控件
public partial class Form2 : Form
{
Form1 docForm;
public Form2()
{
InitializeComponent();
NewFile();
Application.Idle += this.OnIdle;
}
/// <summary>
/// 空闲事件时,随时更新重做和撤销的状态
/// </summary>
private void OnIdle(object sender, EventArgs e)
{
Form1 currActiveDocForm = this.ActiveMdiChild as Form1;
if (currActiveDocForm != null)
{
currActiveDocForm.UpdateUI();
}
}
public void NewFile()
{
docForm = new Form1();
docForm.MdiParent = this;
docForm.WindowState = FormWindowState.Maximized;
docForm.Show();
}
private void 新建ToolStripMenuItem_Click(object sender, EventArgs e)
{
NewFile();
}
}
当前图像不想要的时候可以直接开始新建一个画图。
接下来开始为画图做准备,有点小复杂。
先添加好接口
命令接口 ICommod
数据库接口 Idatabase
画图方法接口 IMyDraw
public interface ICommod
{
}
interface Idatabase
{
}
interface IMyDraw
{
/// <summary>
/// 画矩形的方法
/// </summary>
/// <param name="rec"></param>
/// <param name="_pen"></param>
void DrawRectangle(Rectangle rec, Pen _pen);
/// <summary>
/// 画直线的方法
/// </summary>
/// <param name="postion"></param>
/// <param name="_pen"></param>
void DrawHorLine(PointF[] postion, Pen _pen);
}
下一步,添加一个所有图像的父类 Images
用来存储图像的位置点和画图方法
/// <summary>
/// 图像的父类
/// </summary>
class Images
{
#region 图像点集合
//线段
private Dictionary<string, PointF[]> lines = new Dictionary<string, PointF[]>();
public Dictionary<string, PointF[]> Lines
{
get
{
return lines;
}
set
{
lines = value;
}
}
//矩形
private Dictionary<string, Rectangle> rectangles = new Dictionary<string, Rectangle>();
public Dictionary<string, Rectangle> Rectangles
{
get
{
return rectangles;
}
set
{
rectangles = value;
}
}
#endregion
public int Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
private int _id;
public Pen _pen;
/// <summary>
/// 添加图像时,对应ID增加,确保精确识别每一个图像
/// </summary>
public Images()
{
_id++;
}
/// <summary>
/// 开始画图
/// </summary>
/// <param name="gd"></param>
public void Draw(IMyDraw gd)
{
if (Lines.Keys.Count > 0)
{
foreach (string id in lines.Keys)
{
gd.DrawHorLine(lines[id], _pen);
}
}
if (Rectangles.Keys.Count > 0)
{
foreach (string id in rectangles.Keys)
{
gd.DrawRectangle(rectangles[id], _pen);
}
}
}
}
添加画图类 MyDraw实现IMyDraw接口
class MyDraw : IMyDraw
{
private Graphics _g = null;
public Graphics graphics
{
get { return _g; }
set { _g = value; }
}
private Pen _pen = new Pen(Color.Black);
public Pen pen
{
get { return _pen; }
set { _pen = value; }
}
private Brush _brush = new SolidBrush(Color.Red);
public Brush brush
{
get { return _brush; }
set { _brush = value; }
}
private Presenter _presenter = null;
public Presenter presenter
{
get { return _presenter; }
}
/// <summary>
/// 构造器
/// </summary>
/// <param name="presenter"></param>
public MyDraw(Presenter presenter)
{
_presenter = presenter;
}
#region 画图的方法
public void DrawRectangle(Rectangle _rec, Pen mypen)
{
//画四边框
_g.DrawRectangle(mypen == null ? _pen : mypen, _rec);
}
public void DrawHorLine(PointF[] postion, Pen mypen)
{
//画线
_g.DrawLine(mypen == null ? _pen : mypen, postion[0], postion[1]);
}
#endregion
}
添加存储数据的数据库类database实现Idatabase接口
class database : Idatabase
{
private Presenter _presenter = null;
public database(Presenter _pre)
{
_presenter = _pre;
Clear();
}
private List<Images> lsitUndoRedo = new List<Images>();
private List<Images> lsit = new List<Images>();
/// <summary>
/// 图像集合
/// </summary>
public List<Images> Lsit
{
get
{
return lsit;
}
}
/// <summary>
/// 撤销图像集合
/// </summary>
internal List<Images> LsitUndoRedo
{
get
{
return lsitUndoRedo;
}
set
{
lsitUndoRedo = value;
}
}
public void Addimage1(Images image)
{
lsitUndoRedo.Add(image);
}
public void Removeimage1(Images image)
{
lsitUndoRedo.Remove(image);
}
public void Removeimage1(int index)
{
lsitUndoRedo.RemoveAt(index);
}
public Images Lastimage1()
{
return lsitUndoRedo.Last();
}
public Images Firstimage1()
{
return lsitUndoRedo.First();
}
public Images Lastimage()
{
return lsit.Last();
}
public Images Firstimage()
{
return lsit.First();
}
public int index(Images image)
{
return lsit.FindIndex(t => t == image);
}
public bool contains(Images image)
{
return lsit.Contains(image);
}
public void SetImage(int _index, Images image)
{
lsit[_index] = image;
}
public void Addimage(Images image)
{
lsit.Add(image);
}
public void Removeimage(Images image)
{
lsit.Remove(image);
}
public void Removeimage(int index)
{
lsit.RemoveAt(index);
}
public void Clear()
{
lsit.Clear();
}
}
添加命令类CommodArgS和Commod
internal abstract class Commod : ICommod
{ /// <summary>
/// 命令管理器
/// </summary>
protected CommodArgS _mgr = null;
internal CommodArgS cmdMgr
{
get { return _mgr; }
set { _mgr = value; }
}
/// <summary>
/// 初始化
/// </summary>
public virtual void Initialize()
{
}
/// <summary>
/// 结束
/// </summary>
public virtual void Terminate()
{
}
/// <summary>
/// 撤销
/// </summary>
public virtual void Undo()
{
}
/// <summary>
/// 重做
/// </summary>
public virtual void Redo()
{
}
/// <summary>
/// 完成
/// </summary>
public virtual void Finish()
{
}
/// <summary>
/// 取消
/// </summary>
public virtual void Cancel()
{
}
/// <summary>
/// Mouse Down
/// </summary>
public virtual EventResult OnMouseDown(MouseEventArgs e)
{
return EventResult.Unhandled;
}
/// <summary>
/// Mouse Up
/// </summary>
public virtual EventResult OnMouseUp(MouseEventArgs e)
{
return EventResult.Unhandled;
}
/// <summary>
/// Mouse Move
/// </summary>
public virtual EventResult OnMouseMove(MouseEventArgs e)
{
return EventResult.Unhandled;
}
/// <summary>
/// Mouse Wheel
/// </summary>
public virtual EventResult OnMouseWheel(MouseEventArgs e)
{
return EventResult.Unhandled;
}
/// <summary>
/// Key Down
/// </summary>
public virtual EventResult OnKeyDown(KeyEventArgs e)
{
return EventResult.Unhandled;
}
/// <summary>
/// Key Up
/// </summary>
public virtual EventResult OnKeyUp(KeyEventArgs e)
{
return EventResult.Unhandled;
}
/// <summary>
/// Paint
/// </summary>
public virtual void OnPaint(Graphics g)
{
}
/// <summary>
/// 事件处理结果
/// </summary>
public class EventResult
{
public EventResultStatus status = EventResultStatus.Invalid;
public object data = null;
/// <summary>
/// 未处理事件
/// </summary>
public static EventResult Unhandled
{
get
{
EventResult eRet = new EventResult();
eRet.status = EventResultStatus.Unhandled;
return eRet;
}
}
/// <summary>
/// 处理了事件
/// </summary>
public static EventResult Handled
{
get
{
EventResult eRet = new EventResult();
eRet.status = EventResultStatus.Handled;
return eRet;
}
}
}
/// <summary>
/// 事件结果状态
/// </summary>
public enum EventResultStatus
{
// 无效
Invalid = 0,
// 处理了
Handled = 1,
// 未处理
Unhandled = 2,
}
}
/// <summary>
/// 命令窗口
/// </summary>
class CommodArgS
{
/// <summary>
/// 构造函数
/// </summary>
public CommodArgS(Presenter presenter)
{
_presenter = presenter;
}
/// <summary>
/// Presenter
/// </summary>
private Presenter _presenter = null;
public Presenter presenter
{
get { return _presenter; }
}
/// <summary>
/// 当前命令
/// </summary>
private Commod _currentCmd = null;
public Commod CurrentCmd
{
get { return _currentCmd; }
}
/// <summary>
/// 命令完成事件
/// </summary>
public delegate void CommandEvent(Commod cmd);//定义事件的处理方法的方式:传入一个命令,返回值为void
public event CommandEvent commandFinished;
public event CommandEvent commandCanceled;
/// <summary>
/// 命令列表:撤销
/// </summary>
private List<Commod> _undoCmds = new List<Commod>();
/// <summary>
/// 命令列表:重做
/// </summary>
private List<Commod> _redoCmds = new List<Commod>();
/// <summary>
/// 判断是否可以撤销
/// </summary>
internal bool canUndo
{
get { return UndoCmds.Count > 0; }
}
/// <summary>
/// 判断是否可以重做
/// </summary>
internal bool canRedo
{
get { return _redoCmds.Count > 0; }
}
internal List<Commod> UndoCmds
{
get
{
return _undoCmds;
}
set
{
_undoCmds = value;
}
}
/// <summary>
/// 设置当前的执行命令
/// </summary>
public void DoCommand(Commod cmd)
{
//如果当前命令不为空,说明命令没有执行完,就不再执行
if (_currentCmd != null)
{
return;
}
_currentCmd = cmd;//设置当前执行的命令
_currentCmd.cmdMgr = this;//命令的命令窗口是这里
_currentCmd.Initialize();
}
/// <summary>
/// 绘制
/// </summary>
public void OnPaint(Graphics g)
{
if (_currentCmd != null)
{
_currentCmd.OnPaint(g);
}
}
/// <summary>
/// Mouse Down
/// </summary>
public void OnMouseDown(MouseEventArgs e)
{
if (_currentCmd != null)
{
//调用每个画图命令的不同的按下方法,一般统一为按下记录按下的位置,弹起记录弹起的位置
_currentCmd.OnMouseDown(e);
}
}
/// <summary>
/// Mouse Up
/// </summary>
public void OnMouseUp(MouseEventArgs e)
{
if (_currentCmd != null)
{
_currentCmd.OnMouseUp(e);
}
}
/// <summary>
/// Mouse Move
/// </summary>
public void OnMouseMove(MouseEventArgs e)
{
if (_currentCmd != null)
{
_currentCmd.OnMouseMove(e);
}
} /// <summary>
/// 完成当前命令
/// </summary>
public void FinishCurrentCommand()
{
if (_currentCmd != null)
{
_currentCmd.Finish();
if (_currentCmd is UndoCmd)
{
this.Undo();
}
else if (_currentCmd is RedoCmd)
{
this.Redo();
}
else
{
UndoCmds.Add(_currentCmd);
_redoCmds.Clear();
}
commandFinished.Invoke(_currentCmd);
_currentCmd = null;
}
}
/// <summary>
/// 撤销
/// </summary>
private void Undo()
{
if (UndoCmds.Count == 0)
{
return;
}
Commod cmd = UndoCmds[UndoCmds.Count - 1];
UndoCmds.RemoveAt(UndoCmds.Count - 1);
cmd.Undo();
_redoCmds.Add(cmd);
}
/// <summary>
/// 重做
/// </summary>
private void Redo()
{
if (_redoCmds.Count == 0)
{
return;
}
Commod cmd = _redoCmds[_redoCmds.Count - 1];
_redoCmds.RemoveAt(_redoCmds.Count - 1);
cmd.Redo();
UndoCmds.Add(cmd);
}
}
添加撤销和重做的命令RedoCmd,UndoCmd
/// <summary>
/// 撤销命令
/// </summary>
class UndoCmd : Commod
{
public override void Initialize()
{
base.Initialize();
_mgr.FinishCurrentCommand();
}
}
/// <summary>
/// 重做命令
/// </summary>
class RedoCmd : Commod
{
public override void Initialize()
{
base.Initialize();
_mgr.FinishCurrentCommand();
}
}
Presenter类代码添加
class Presenter : IParents
{
/// <summary>
/// 画布
/// </summary>
private IMyCanvas _canvas = null;
/// <summary>
/// 鼠标标识
/// </summary>
private MousePoint _point = null;
/// <summary>
/// 存储当前界面的图像
/// </summary>
private Bitmap _bufferBitmap = null;
private bool _bufferBitmapToRedraw = true;
/// <summary>
/// 命令管理器
/// </summary>
private CommodArgS _cmdsMgr = null;
public MyDraw worldDraw
{
get { return _Draw; }
}
/// <summary>
/// 所有画图的方法,图像内部直接调用。可以直接画图
/// </summary>
private MyDraw _Draw = null;
/// <summary>
/// 存储图元
/// </summary>
private database _db = null;
public database Db
{
get
{
return _db;
}
}
internal bool canUndo
{
get { return CmdsMgr.canUndo; }
}
internal bool canRedo
{
get { return CmdsMgr.canRedo; }
}
public IMyCanvas canvas
{
get
{
return _canvas;
}
}
internal CommodArgS CmdsMgr
{
get
{
return _cmdsMgr;
}
set
{
_cmdsMgr = value;
}
}
public Presenter(IMyCanvas canvas)
{
_canvas = canvas;
//new一个画图的方法对象,传入主导对象目前未发现有何作用
_Draw = new MyDraw(this);
//命令窗口。新建
CmdsMgr = new CommodArgS(this);
//构造函数就定义好了鼠标的标识
_point = new MousePoint(this);
//所有数据的集合
_db = new database(this);
CmdsMgr.commandFinished += this.OnCommandFinished;
CmdsMgr.commandCanceled += this.OnCommandCanceled;
}
/// <summary>
/// 设置当前的执行命令
/// </summary>
public void OnCommand(ICommod cmd)
{
CmdsMgr.DoCommand(cmd as Commod);
}
/// <summary>
/// 命令完成,对图像数据库进行处理
/// </summary>
public void OnCommandFinished(Commod cmd)
{
if (cmd is RedoCmd) //重做
{
_db.Addimage(_db.Lastimage1());
_db.Removeimage1(_db.Lastimage1());
}
else if (cmd is UndoCmd)//撤销
{
_db.Addimage1(_db.Lastimage());
_db.Removeimage(_db.Lastimage());
}
this.RepaintCanvas(true);
}
/// <summary>
/// 命令取消
/// </summary>
public void OnCommandCanceled(Commod cmd)
{
this.RepaintCanvas(false);
}
/// <summary>
/// 将图像存入数据集合
/// </summary>
/// <param name="_image"></param>
public void SaveImage(Images _image)
{
if (!_db.contains(_image))
{
_db.Addimage(_image);
}
}
public void DrawEntity(Graphics graphics, Images _image)
{
_Draw.graphics = graphics;
_image.Draw(_Draw);
}
/// <summary>
/// 鼠标在画布上按下时,调用主导的按下方法
/// </summary>
/// <param name="e"></param>
public void OnMouseDown(MouseEventArgs e)
{
//如果当前有要执行的绘图命令
if (CmdsMgr.CurrentCmd != null)
{
//调用绘图的按下方法
CmdsMgr.OnMouseDown(e);
RepaintCanvas(true);
}
}
/// <summary>
/// 鼠标在画布上挪动时,触发主导的鼠标移动事件
/// </summary>
/// <param name="e"></param>
public void OnMouseMove(MouseEventArgs e)
{
_point.OnMouseMove(e);
if (CmdsMgr.CurrentCmd != null)
{
CmdsMgr.OnMouseMove(e);
RepaintCanvas(true);
}
}
public void OnMouseUp(MouseEventArgs e)
{
if (CmdsMgr.CurrentCmd != null)
{
CmdsMgr.OnMouseUp(e);
RepaintCanvas();
}
}
public void OnKeyDown(KeyEventArgs e)
{
}
public void OnKeyUp(KeyEventArgs e)
{
}
public void OnMouseDoubleClick(MouseEventArgs e)
{
}
public void OnResize(EventArgs e)
{
RepaintCanvas(true);
}
public void OnMouseWheel(MouseEventArgs e)
{
}
internal void RepaintCanvas(bool bufferBitmapToRedraw = false)
{
if (bufferBitmapToRedraw)
_bufferBitmapToRedraw = true;//对图像进行了更改,需要进行刷新图像区域
_canvas.Repaint();
}
/// <summary>
/// 主要绘图的方法
/// </summary>
/// <param name="e"></param>
public void OnPaintCanvas(PaintEventArgs e)
{
int canvasWidth = (int)_canvas.width;//获取画布宽
int canvasHeight = (int)_canvas.height;//获取画布高
Rectangle clipRectangle = e.ClipRectangle;
Rectangle canvasRectangle = new Rectangle(0, 0, canvasWidth, canvasHeight);//画图的区域的大小
if (_bufferBitmap == null)//当前界面的图像为空
{
clipRectangle = canvasRectangle;//绘画区域就是画布区域
_bufferBitmap = new Bitmap(canvasWidth, canvasHeight);
_bufferBitmapToRedraw = true;
}
if (_bufferBitmapToRedraw)
{
_bufferBitmapToRedraw = false;
Graphics graphics = Graphics.FromImage(_bufferBitmap);//获取图像的画图对象
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;//设置绘画的质量。抗拒齿等
// 绘制背景
graphics.Clear(Color.FromArgb(255, 255, 255));//先清除所有的图像
//draw的意义是,图像调用画图的方法,直接画图
_Draw.graphics = graphics;//
foreach (Images _image in _db.Lsit) //所有的图形
{
//图像调用画图的方法,直接画图
_image.Draw(_Draw);
}
}
// 双缓冲:将图片绘制到画布
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
e.Graphics.DrawImage(_bufferBitmap, clipRectangle, clipRectangle, GraphicsUnit.Pixel);
// 命令管理器绘制
//随时绘制图像
CmdsMgr.OnPaint(e.Graphics);
//画出鼠标点
_point.OnPaint(e.Graphics);
}
}
接下来可以准备画图了
添加画直线的命令HorLine
class HorLine : Commod
{
private Images _rectangle = null;
/// <summary>
/// 步骤
/// </summary>
private enum Step
{
Step1 = 1,
Step2 = 2,
}
private Presenter _presenter = null;
public HorLine(Presenter presenter)
{
_presenter = presenter;
}
private Step _step = Step.Step1;
// 起点+终点
private PointF _point1st = new PointF(0, 0);
private PointF _point2nd = new PointF(0, 0);
/// <summary>
/// 初始化
/// </summary>
public override void Initialize()
{
base.Initialize();
_step = Step.Step1;
}
public void UpdatePoint()
{
_rectangle = new Images();
if (!_rectangle.Lines.ContainsKey(_rectangle.Id.ToString()))
{
_rectangle.Lines.Add(_rectangle.Id.ToString(), new PointF[] { _point1st, _point2nd });
}
}
public override EventResult OnMouseDown(MouseEventArgs e)
{
switch (_step)
{
case Step.Step1: //鼠标第一次按下时,记录点击的坐标位置
if (e.Button == MouseButtons.Left)
{
_step = Step.Step2;
_point1st = e.Location;
}
break;
case Step.Step2:
if (e.Button == MouseButtons.Left)
{
//_point2nd = e.Location;
//this.UpdatePoint();
//_mgr.FinishCurrentCommand();
}
break;
}
return EventResult.Handled;
}
public override EventResult OnMouseUp(MouseEventArgs e)
{
if (_rectangle != null)
{
_rectangle._pen = new Pen(this._presenter.worldDraw.pen.Color, this._presenter.worldDraw.pen.Width);
_presenter.SaveImage(_rectangle);//鼠标弹起,将图像保存到图像集合里
}
_mgr.FinishCurrentCommand();
return EventResult.Handled;
}
public override EventResult OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Middle)
{
return EventResult.Handled;
}
if (_step == Step.Step2)//第二步画图,
{
_point2nd = e.Location;//随时记录移动的位置
this.UpdatePoint();
}
return EventResult.Handled;
}
public override void OnPaint(Graphics g)
{
//随时画出自己想画的图像
if (_rectangle != null)
{
this._presenter.DrawEntity(g, _rectangle);
}
}
}
画矩形的命令RectangleLine
class RectangleLine : Commod
{
private Images _rectangle = null;
/// <summary>
/// 步骤
/// </summary>
private enum Step
{
Step1 = 1,
Step2 = 2,
}
private Presenter _presenter = null;
public RectangleLine(Presenter presenter)
{
_presenter = presenter;
}
private Step _step = Step.Step1;
// 起点+终点
private PointF _point1st = new PointF(0, 0);
private PointF _point2nd = new PointF(0, 0);
/// <summary>
/// 初始化
/// </summary>
public override void Initialize()
{
base.Initialize();
_step = Step.Step1;
}
public void UpdatePoint()
{
_rectangle = new Images();
if (!_rectangle.Rectangles.ContainsKey(_rectangle.Id.ToString()))
_rectangle.Rectangles.Add(_rectangle.Id.ToString(), new Rectangle(new Point((int)_point1st.X, (int)_point1st.Y), new Size((int)(_point2nd.X - _point1st.X), (int)(_point2nd.Y - _point1st.Y))));
}
public override EventResult OnMouseDown(MouseEventArgs e)
{
switch (_step)
{
case Step.Step1:
if (e.Button == MouseButtons.Left)
{
_step = Step.Step2;
_point1st = e.Location;
}
break;
case Step.Step2:
if (e.Button == MouseButtons.Left)
{
//_point2nd = e.Location;
//this.UpdatePoint();
//_mgr.FinishCurrentCommand();
}
break;
}
return EventResult.Handled;
}
public override EventResult OnMouseUp(MouseEventArgs e)
{
if (_rectangle != null)
{
_rectangle._pen = new Pen(this._presenter.worldDraw.pen.Color, this._presenter.worldDraw.pen.Width);
_presenter.SaveImage(_rectangle);
}
_mgr.FinishCurrentCommand();
return EventResult.Handled;
}
public override EventResult OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Middle)
{
return EventResult.Handled;
}
if (_step == Step.Step2)
{
_point2nd = e.Location;
this.UpdatePoint();
}
return EventResult.Handled;
}
public override void OnPaint(Graphics g)
{
if (_rectangle != null)
{
this._presenter.DrawEntity(g, _rectangle);
}
}
}
Form1 完整代码
public partial class Form1 : Form
{
private Presenter _presenter = null;
private CanavsControl _canvas = null;//画布
public Form1()
{
InitializeComponent();
_canvas = new CanavsControl();
_presenter = new Presenter(_canvas);
_canvas.Dock = DockStyle.Fill;
_canvas.SetPresenter(_presenter);
this.Controls.Add(_canvas);
}
/// <summary>
/// 实时更新撤销和重做按钮的状态
/// </summary>
internal void UpdateUI()
{
撤销ToolStripMenuItem.Enabled = _presenter.canUndo;
重做ToolStripMenuItem.Enabled = _presenter.canRedo;
}
private void 线条ToolStripMenuItem_Click(object sender, EventArgs e)
{
HorLine _hor = new HorLine(_presenter);
_presenter.OnCommand(_hor);
}
private void 矩形ToolStripMenuItem_Click(object sender, EventArgs e)
{
RectangleLine _rec = new RectangleLine(_presenter);
_presenter.OnCommand(_rec);
}
private void 撤销ToolStripMenuItem_Click(object sender, EventArgs e)
{
UndoCmd red = new UndoCmd();
_presenter.OnCommand(red);
}
private void 重做ToolStripMenuItem_Click(object sender, EventArgs e)
{
RedoCmd red = new RedoCmd();
_presenter.OnCommand(red);
}
}