您的位置:首页 > 大数据 > 人工智能

Little Painter Step by Step-Day 7

2008-12-29 04:05 218 查看
第七天:(目标:Tool的抽象及ApplicationForm的重构)
昨天我们已经对Display部分进行了设计,我们构建了DisplayCommand继承结构,并对Canvas进行了抽象,接下来我们要对Tool进行抽象。
对于Tool,在之前的程序中我们有两种,Line和Circle,我们还能想到很多不同的Tool,如Rectangle,Ellipse等,对于所有的Tool,我们可以设计一个抽象的Tool,名为ATool,接下来,所有的Tool都可以从这个ATool继承,我们用一个String属性来区分这些Tool
public abstract class ATool
{
public abstract String ToolDesc { get; }
}
我们还需要一个ToolManager来管理Tool
public class ToolManager
{
public static event EventHandler<EventArgs> ActiveToolChanged;

public static ATool ActiveTool
{
get { return msActiveTool; }
set
{
if (msActiveTool != value)
{
msActiveTool = value;
OnActiveToolChanged(EventArgs.Empty);
}
}
}

public static ATool GetTool(String toolDesc)
{
foreach (ATool tool in msToolsCollection)
{
if (tool.ToolDesc == toolDesc)
return tool;
}
return null;
}

public static IEnumerable<ATool> Tools
{
get { return msToolsCollection; }
}

public static void HookupCanvas(ACanvas canvas)
{
InitializeTools(canvas);
}

private static void InitializeTools(ACanvas canvas)
{
msToolsCollection = new Collection<ATool>();
LineTool lineTool = new LineTool(canvas);
msToolsCollection.Add(lineTool);

msActiveTool = null;
}

private static void OnActiveToolChanged(EventArgs e)
{
if (ActiveToolChanged != null)
{
ActiveToolChanged(msActiveTool, e);
}
}

private static ATool msActiveTool;
private static Collection<ATool> msToolsCollection;
}
解释一下,ActiveTool是当前激活的Tool,我们使用该Tool进行绘画,类似于原来的CurrentDrawType。ToolManager中还有一个所有Tool的集合,我们会去初始化这些Tool。
接下来我们实现LineTool
public LineTool(ACanvas canvas)
{
mCanvas = canvas;
ToolManager.ActiveToolChanged += new EventHandler<EventArgs>(ToolManager_ActiveToolChanged);
}

public override String ToolDesc
{
get { return "Line"; }
}

void ToolManager_ActiveToolChanged(object sender, EventArgs e)
{
if (ToolManager.ActiveTool == this)
{
mCanvas.MouseClick += new System.Windows.Forms.MouseEventHandler(mCanvas_MouseClick);
mCanvas.MouseMove += new System.Windows.Forms.MouseEventHandler(mCanvas_MouseMove);
mCanvas.Paint += new System.Windows.Forms.PaintEventHandler(mCanvas_Paint);
}
else
{
mCanvas.MouseClick -= new System.Windows.Forms.MouseEventHandler(mCanvas_MouseClick);
mCanvas.MouseMove -= new System.Windows.Forms.MouseEventHandler(mCanvas_MouseMove);
mCanvas.Paint -= new System.Windows.Forms.PaintEventHandler(mCanvas_Paint);
}
}

void mCanvas_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (mClickNum == 1)
{
e.Graphics.DrawLine(Pens.DodgerBlue, mFirstClickPoint, mSecondClickPoint);
}
}

void mCanvas_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (mClickNum == 0) return;

mSecondClickPoint = e.Location;

mCanvas.Invalidate();
}

void mCanvas_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
mClickNum++;

if (mClickNum % 2 == 0)
{
// Confirm drawing
mSecondClickPoint = e.Location;
try
{
Pen p = new Pen(ColorManager.CurrentColor);
DrawLineCommand command = new DrawLineCommand(mCanvas, p, mFirstClickPoint, mSecondClickPoint);
DisplayManager.AddDisplayItem(command);
Core.UndoManager.Push(command);
mCanvas.Invalidate();
}
finally
{
mClickNum = 0;
}
}
else
{
mFirstClickPoint = e.Location;
}
}

private ACanvas mCanvas;
private Int32 mClickNum;
private Point mFirstClickPoint = new Point(0, 0);
private Point mSecondClickPoint = new Point(0, 0);
}
我们通过Canvas去初始化Tool,并在Tool中注册Mouse事件,这样就能画preview以及创建command了。
当我们按下菜单栏中的Line按钮时,我们需要触发一个Command使得LineTool成为当前的ActiveTool,因此我们需要一个ToolActivateCommand
public class ToolActivateCommand : AUndoableCommand
{
private String mActivateToolDesc;
private String mOldActivateToolDesc;

public ToolActivateCommand(String activateToolDesc)
{
mActivateToolDesc = activateToolDesc;
}

public override void Execute(Object parameter)
{
if (ToolManager.ActiveTool == null)
mOldActivateToolDesc = null;
else
mOldActivateToolDesc = ToolManager.ActiveTool.ToolDesc;
ToolManager.ActiveTool = ToolManager.GetTool(mActivateToolDesc);
}

public override bool CanExecute(object parameter)
{
return mActivateToolDesc != mOldActivateToolDesc;
}

public override void Undo()
{
ToolManager.ActiveTool = ToolManager.GetTool(mOldActivateToolDesc);
}

public override void Redo()
{
ToolManager.ActiveTool = ToolManager.GetTool(mActivateToolDesc);
}
}
这样,Tool部分的重构就完成了,接下来我们需要对ApplicationForm进行改造,我们需要去掉以前的Canvas以及属性的Panel,去掉DrawType和DrawInfomation,事实上,我们仅仅需要一个Form加上菜单和状态栏。
接下来我们要往ApplicationForm中添加Canvas,为了便于以后对Canvas进行控制(比如移动,缩放),我们把Canvas放入一个UserControl中,命名为WorkingArea。
public class WorkingArea : UserControl
{
public WorkingArea()
{
InitializeComponent();
}

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
InitializeCanvas();
ToolManager.HookupCanvas(mCanvas);
}

private void InitializeCanvas()
{
mCanvas = new Canvas();
mCanvas.BackColor = Color.White;
mCanvas.Size = new Size(800, 600);
mCanvas.Location = new Point((this.ClientSize.Width - mCanvas.Width) / 2, (this.ClientSize.Height - mCanvas.Height) / 2);
this.Controls.Add(mCanvas);
}

private ACanvas mCanvas;

private void InitializeComponent()
{
this.SuspendLayout();
//
// WorkingArea
//
this.BackColor = System.Drawing.SystemColors.AppWorkspace;
this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.Name = "WorkingArea";
this.Size = new System.Drawing.Size(146, 146);
this.ResumeLayout(false);

}
}
在WorkingArea中我们对Canvas进行了居中处理
然后我们就可以把WorkingArea放入ApplicationForm中,Dock设置为Fill,ApplicationForm的代码简化如下:
public partial class ApplicationForm : Form
{
public ApplicationForm()
{
InitializeComponent();
}

private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}

private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
}

private void lineToolStripMenuItem_Click(object sender, EventArgs e)
{
ToolActivateCommand command = new ToolActivateCommand("Line");
command.Execute(null);
}

private void circleToolStripMenuItem_Click(object sender, EventArgs e)
{
}
}
在lineToolStripMenuItem的click响应中,我们仅仅构建一个ToolActivateCommand,剩下的东西,全部交予后台处理,这样就完成了重构。
现在我们来考虑一下,如果我们需要添加一个RectangleTool,我们需要做些什么呢?我们仅仅需要添加一个DrawRectangleCommand,一个RectangleTool,在ToolManager中添加对RectangleTool的初始化,以及在菜单栏中对Rectangle项的click事件响应中,执行一个ToolActivateCommand即可。非常easy!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: