Little Painter Step by Step-Day 3
2008-12-25 04:12
253 查看
第三天:(目标:讨论Paint的机制,避免丢失每次画图的结果)
昨天的程序中,我们用两种方式来进行画图,一种是在MouseUp时通过创建Canvas的Graphics对象作图,一种是在Canvas的Paint事件中作图。其实这里面有个问题,如果我们没有写MouseMove和Paint中的代码,当完成了一条直线的作图后,我们发现如果将ApplicationForm最小化,然后还原,直线就没有了~~不过一旦在Paint中加了昨天的代码,我们会发现这条直线就一直存在,但是当画下一条直线时,上一条直线也消失了~~
为什么Canvas能绘图?
对于每个Windows的窗口(我们程序里使用的m_Canvas也可以看成一个Windows的窗口)它都会在消息循环里去响应Paint的消息,当它的一部分或者全部绘图区失效时,就会发出一个重绘的请求。失效会发生在如当窗口的一部分或全部被另一个窗口挡住,或者发生resize事件,又或者被最小化然后还原等等条件下,当窗口失效后,Windows会去重绘失效的那部分,最基本的情况,就是用窗口的背景色去刷新失效的部分,然后将其置为有效。
有了这个理解,我们就应该能明白为什么会有昨天的问题了。对于MouseUp里绘图的情况,当窗口最小化然后还原,我们没有在Paint事件中做任何处理,这样当系统重绘消息发出后,整个窗体就被刷白了,上次绘图的结果就没有被保存
对于第二种情况,基于同样的原理,画第二条线时,第一条线的信息并没有被保存下来,接着系统以背景色刷新,然后执行画线(第二条)命令,这样就只有一条线能被显示在Canvas上。
所以,我们需要做的是,保存所有的画图信息,并在Paint时间被触发时,重绘保存的图形
我们需要一个添加一个类,这个类能保存绘图时所需的全部信息,命名为DrawInformation。
class DrawInformation
{
public DrawInformation(Point startPoint, Point endPoint, DrawType currentDrawType)
{
m_StartPoint = startPoint;
m_EndPoint = endPoint;
m_CurrentDrawType = currentDrawType;
}
public Point StartPoint
{
get { return m_StartPoint; }
}
public Point EndPoint
{
get { return m_EndPoint; }
}
public DrawType CurrentDrawType
{
get { return m_CurrentDrawType; }
}
private Point m_StartPoint;
private Point m_EndPoint;
private DrawType m_CurrentDrawType;
}
对于整个程序,我们需要添加一个List<DrawInformation>,当每次mouseup时,创建一个DrawInfomation对象,并添加到该list中,当Paint事件触发后,我们遍历每个DrawInformation对象,根据不同的DrawType重绘已经画过的图形。更改后的代码如下:
private void m_Canvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_StartPoint = e.Location;
}
}
private void m_Canvas_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_EndPoint = e.Location;
DrawInformation info = new DrawInformation(m_StartPoint, m_EndPoint, m_CurrentDrawType);
m_DrawInfoList.Add(info);
m_Canvas.Invalidate();
}
}
private void m_Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_EndPoint = e.Location;
m_Canvas.Invalidate();
}
}
private void m_Canvas_Paint(object sender, PaintEventArgs e)
{
// Draw the current preview
switch (m_CurrentDrawType)
{
case DrawType.Line:
e.Graphics.DrawLine(Pens.Black, m_StartPoint, m_EndPoint);
break;
case DrawType.Circle:
Int32 width = Math.Abs(m_EndPoint.X - m_StartPoint.X);
Int32 height = Math.Abs(m_EndPoint.Y - m_StartPoint.Y);
Int32 diameter = width <= height ? width : height;
e.Graphics.DrawEllipse(Pens.Black,
new Rectangle(m_StartPoint, new Size(diameter, diameter)));
break;
default:
break;
}
// Draw the exist items
foreach (DrawInformation info in m_DrawInfoList)
{
switch (info.CurrentDrawType)
{
case DrawType.Line:
e.Graphics.DrawLine(Pens.Black, info.StartPoint, info.EndPoint);
break;
case DrawType.Circle:
Int32 width = Math.Abs(info.EndPoint.X - info.StartPoint.X);
Int32 height = Math.Abs(info.EndPoint.Y - info.StartPoint.Y);
Int32 diameter = width <= height ? width : height;
e.Graphics.DrawEllipse(Pens.Black,
new Rectangle(info.StartPoint, new Size(diameter, diameter)));
break;
default:
break;
}
}
}
private Point m_StartPoint;
private Point m_EndPoint;
private DrawType m_CurrentDrawType;
private List<DrawInformation> m_DrawInfoList = new List<DrawInformation>();
这样,所有的图形都被不会因为重绘而消失了,但是现在我们的仅能画简单的线和圆,明天我们会尝试设置更多的参数,绘制各种各样的线和圆,并把所绘的图形保存为图片。
昨天的程序中,我们用两种方式来进行画图,一种是在MouseUp时通过创建Canvas的Graphics对象作图,一种是在Canvas的Paint事件中作图。其实这里面有个问题,如果我们没有写MouseMove和Paint中的代码,当完成了一条直线的作图后,我们发现如果将ApplicationForm最小化,然后还原,直线就没有了~~不过一旦在Paint中加了昨天的代码,我们会发现这条直线就一直存在,但是当画下一条直线时,上一条直线也消失了~~
为什么Canvas能绘图?
对于每个Windows的窗口(我们程序里使用的m_Canvas也可以看成一个Windows的窗口)它都会在消息循环里去响应Paint的消息,当它的一部分或者全部绘图区失效时,就会发出一个重绘的请求。失效会发生在如当窗口的一部分或全部被另一个窗口挡住,或者发生resize事件,又或者被最小化然后还原等等条件下,当窗口失效后,Windows会去重绘失效的那部分,最基本的情况,就是用窗口的背景色去刷新失效的部分,然后将其置为有效。
有了这个理解,我们就应该能明白为什么会有昨天的问题了。对于MouseUp里绘图的情况,当窗口最小化然后还原,我们没有在Paint事件中做任何处理,这样当系统重绘消息发出后,整个窗体就被刷白了,上次绘图的结果就没有被保存
对于第二种情况,基于同样的原理,画第二条线时,第一条线的信息并没有被保存下来,接着系统以背景色刷新,然后执行画线(第二条)命令,这样就只有一条线能被显示在Canvas上。
所以,我们需要做的是,保存所有的画图信息,并在Paint时间被触发时,重绘保存的图形
我们需要一个添加一个类,这个类能保存绘图时所需的全部信息,命名为DrawInformation。
class DrawInformation
{
public DrawInformation(Point startPoint, Point endPoint, DrawType currentDrawType)
{
m_StartPoint = startPoint;
m_EndPoint = endPoint;
m_CurrentDrawType = currentDrawType;
}
public Point StartPoint
{
get { return m_StartPoint; }
}
public Point EndPoint
{
get { return m_EndPoint; }
}
public DrawType CurrentDrawType
{
get { return m_CurrentDrawType; }
}
private Point m_StartPoint;
private Point m_EndPoint;
private DrawType m_CurrentDrawType;
}
对于整个程序,我们需要添加一个List<DrawInformation>,当每次mouseup时,创建一个DrawInfomation对象,并添加到该list中,当Paint事件触发后,我们遍历每个DrawInformation对象,根据不同的DrawType重绘已经画过的图形。更改后的代码如下:
private void m_Canvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_StartPoint = e.Location;
}
}
private void m_Canvas_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_EndPoint = e.Location;
DrawInformation info = new DrawInformation(m_StartPoint, m_EndPoint, m_CurrentDrawType);
m_DrawInfoList.Add(info);
m_Canvas.Invalidate();
}
}
private void m_Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_EndPoint = e.Location;
m_Canvas.Invalidate();
}
}
private void m_Canvas_Paint(object sender, PaintEventArgs e)
{
// Draw the current preview
switch (m_CurrentDrawType)
{
case DrawType.Line:
e.Graphics.DrawLine(Pens.Black, m_StartPoint, m_EndPoint);
break;
case DrawType.Circle:
Int32 width = Math.Abs(m_EndPoint.X - m_StartPoint.X);
Int32 height = Math.Abs(m_EndPoint.Y - m_StartPoint.Y);
Int32 diameter = width <= height ? width : height;
e.Graphics.DrawEllipse(Pens.Black,
new Rectangle(m_StartPoint, new Size(diameter, diameter)));
break;
default:
break;
}
// Draw the exist items
foreach (DrawInformation info in m_DrawInfoList)
{
switch (info.CurrentDrawType)
{
case DrawType.Line:
e.Graphics.DrawLine(Pens.Black, info.StartPoint, info.EndPoint);
break;
case DrawType.Circle:
Int32 width = Math.Abs(info.EndPoint.X - info.StartPoint.X);
Int32 height = Math.Abs(info.EndPoint.Y - info.StartPoint.Y);
Int32 diameter = width <= height ? width : height;
e.Graphics.DrawEllipse(Pens.Black,
new Rectangle(info.StartPoint, new Size(diameter, diameter)));
break;
default:
break;
}
}
}
private Point m_StartPoint;
private Point m_EndPoint;
private DrawType m_CurrentDrawType;
private List<DrawInformation> m_DrawInfoList = new List<DrawInformation>();
这样,所有的图形都被不会因为重绘而消失了,但是现在我们的仅能画简单的线和圆,明天我们会尝试设置更多的参数,绘制各种各样的线和圆,并把所绘的图形保存为图片。
相关文章推荐
- Little Painter Step by Step-Day 1
- Little Painter Step by Step-Day 6
- Little Painter Step by Step-Day 7
- Little Painter Step by Step-Day 5
- Little Painter Step by Step-Day 9
- Little Painter Step by Step-Day 2
- Little Painter Step by Step-Day 4
- Little Painter Step by Step-Day 8
- 【ASP.NET Step by Step】之十一至十五 Custom Formatting Based Upon Data
- ActionScript 3.0 Step By Step系列(一):工欲其善,先利其器(Flex Builder) 推荐
- 关于ActiveX的一些step by step文章
- ALE STEP BY STEP
- Enable IMGFS Step by Step
- Step by Step 创建一个 Web Service
- ActionScript 3.0 Step By Step系列(一):工欲其善,先利其器(Flex Builder)
- Study Android, Step by Step(六) 记事本应用程序范例
- C#step by step 视频教程(4)常量
- LINQ to SQL快速上手 step by step
- SQL Server BI Step by Step SSIS 4 ---合并数据1
- iptables入门教程--设置静态防火墙( step by step)