当我们需要使用Window的窗体进行绘图的时候可能会对绘制图的进行放大或缩小的操作,利用PictureBox控件默认是实现不了这个功能的,但是我们可以通过对一个一般的UserControl操作来达到这个目的,而不一定使用PictureBox,这里,我使用了Form或UserControl的基类ScrollableControl进行了继承并在其上绘图,因为这个控件足够使用了.
我这里提到了如下的几个绘图的问题:
1:当我们在Windows里绘图的时候所用的坐标系统和我们在数学里使用的坐标系是不一样的,我们需要对其进行转换以适合我们在数学坐标系里的习惯.
2:当我们所需的画布大于当前窗口大小的时候需要滚动条的支持.
3:我们可能随时对所绘的图进行放大或缩小.
4:我们在绘图的时候不能出现闪动而使使用者感到讨厌,我们需要良好的显示效果,给人以快速的感觉
5:我们可能需要绘制出数学函数图像.或是给了一堆的要绘图的坐标绘制出图像.
如果您对上面的问题感兴趣,那么请参考下面的代码示例,如果对示例有问题也请指正以共同提高,谢谢!
定义绘图使用的控件:
///
<summary>
///
显示由Graphics绘制的图像
///
</summary>
internal
class
ImageBox : ScrollableControl
{
private
Color m_ColorCoordinate;
private
PointF[] m_Points;
private
float
m_Scale;
private
SizeF m_ImageSize;
///
<summary>
///
构造函数
///
</summary>
public
ImageBox()
{
this
.SetStyle(ControlStyles.AllPaintingInWmPaint
|
ControlStyles.UserPaint
|
ControlStyles.OptimizedDoubleBuffer
|
ControlStyles.ResizeRedraw,
true
);
m_ColorCoordinate
=
Color.Red;
this
.m_Scale
=
1
;
}
///
<summary>
///
画布的大小
///
</summary>
public
SizeF ImageSize
{
get
{
return
m_ImageSize;
}
set
{
this
.m_ImageSize
=
value;
this
.AutoScrollMinSize
=
Size.Ceiling(
new
SizeF(
this
.m_ImageSize.Width
*
this
.m_Scale,
this
.m_ImageSize.Height
*
this
.m_Scale));
}
}
///
<summary>
///
坐标系的X,Y两轴的颜色
///
</summary>
public
Color ColorCoordinate
{
get
{
return
this
.m_ColorCoordinate;
}
set
{
this
.m_ColorCoordinate
=
value;
this
.Invalidate(
this
.ClientRectangle,
false
);
}
}
///
<summary>
///
重写以进行绘制
///
</summary>
///
<param name="e"></param>
protected
override
void
OnPaint(PaintEventArgs e)
{
base
.OnPaint(e);
this
.drawMathImage(e.Graphics,
this
.getStartPoint(),
this
.m_Scale);
}
///
<summary>
///
得到Sin函数值坐标点
///
</summary>
///
<returns></returns>
public
void
DrawSin()
{
List
<
PointF
>
list
=
new
List
<
PointF
>
();
for
(
int
i
=
-
100
*
(
int
)Math.PI; i
<
100
*
(
int
)Math.PI; i
++
)
{
float
x
=
i
/
100.0f
;
float
y
=
(
float
)Math.Sin((
double
)x);
list.Add(
new
PointF(x
*
100
, y
*
100
));
}
m_Points
=
list.ToArray();
this
.Invalidate();
}
///
<summary>
///
得到抛物线函数值坐标点
///
</summary>
///
<returns></returns>
public
void
DrawPow()
{
List
<
PointF
>
list
=
new
List
<
PointF
>
();
for
(
int
i
=
-
100
; i
<
(
int
)
100
; i
++
)
{
float
x
=
i
/
100f;
float
y
=
(
float
)Math.Pow((
double
)x,
2
);
list.Add(
new
PointF(x
*
100
, y
*
100
));
}
m_Points
=
list.ToArray();
this
.Invalidate();
}
public
void
ClearFunImage()
{
this
.m_Points
=
null
;
this
.Invalidate();
}
///
<summary>
///
放大图像的倍数
///
</summary>
///
<param name="scale"></param>
public
void
ScaleImage(
float
scale)
{
float
tmp
=
scale;
if
(tmp
>
0
)
{
this
.m_Scale
=
tmp;
SizeF size
=
new
SizeF(
this
.m_ImageSize.Width
*
this
.m_Scale,
this
.m_ImageSize.Height
*
this
.m_Scale);
base
.AutoScrollMinSize
=
Size.Ceiling(size);
this
.Invalidate();
}
}
///
<summary>
///
由当前的滚动信息得到开始点以用来计算绘图原点信息
///
</summary>
///
<returns></returns>
private
PointF getStartPoint()
{
SizeF size
=
new
SizeF(
this
.m_ImageSize.Width
*
this
.m_Scale,
this
.m_ImageSize.Height
*
this
.m_Scale);
PointF point
=
this
.AutoScrollPosition;
if
(size.Width
>
this
.ClientRectangle.Width
&&
size.Height
<
this
.ClientRectangle.Height)
{
point.Y
+=
(
this
.ClientRectangle.Height
-
size.Height)
/
2.0f
;
}
else
if
(size.Width
<
this
.ClientRectangle.Width
&&
size.Height
>
this
.ClientRectangle.Height)
{
point.X
+=
(
this
.ClientRectangle.Width
-
size.Width)
/
2.0f
;
}
else
if
(size.Width
<=
this
.ClientRectangle.Width
&&
size.Height
<=
this
.ClientRectangle.Height)
{
point.Y
+=
(
this
.ClientRectangle.Height
-
size.Height)
/
2.0f
;
point.X
+=
(
this
.ClientRectangle.Width
-
size.Width)
/
2.0f
;
}
return
point;
}
///
<summary>
///
使用指定的Graphics对象进行绘图
///
</summary>
///
<param name="g">
用来绘图的Graphics对象
</param>
///
<param name="startPoint">
绘图的时候坐标的偏移量
</param>
private
void
drawMathImage(Graphics g, PointF start,
float
scale)
{
//
建立坐标中点坐标
float
tmpOX
=
this
.AutoScrollMinSize.Width
/
2.0f
;
float
tmpOY
=
this
.AutoScrollMinSize.Height
/
2.0f
;
GraphicsState state
=
g.Save();
using
(Matrix matrix
=
new
Matrix(
1
,
0
,
0
,
-
1
,
0
,
0
))
{
g.Transform
=
matrix;
g.PageUnit
=
GraphicsUnit.Pixel;
g.TranslateTransform(tmpOX
+
start.X,
-
tmpOY
-
start.Y, MatrixOrder.Prepend);
g.ScaleTransform(
this
.m_Scale,
this
.m_Scale, MatrixOrder.Prepend);
float
maxX
=
tmpOX
/
scale;
float
maxY
=
tmpOY
/
scale;
//
绘制坐标轴线
using
(Pen pen
=
new
Pen(
this
.m_ColorCoordinate,
1
))
{
g.DrawLine(pen,
-
maxX,
0.0f
, maxX,
0.0f
);
g.DrawLine(pen,
0.0f
,
-
maxY,
0.0f
, maxY);
g.DrawLine(pen, maxX,
0.0f
, maxX
-
2.0f
,
2.0f
);
g.DrawLine(pen, maxX,
0.0f
, maxX
-
2.0f
,
-
2.0f
);
g.DrawLine(pen,
0.0f
, maxY,
2.0f
, maxY
-
2.0f
);
g.DrawLine(pen,
0.0f
, maxY,
-
2.0f
, maxY
-
2.0f
);
}
//
绘制函数图像
if
(m_Points
!=
null
&&
m_Points.Length
>
1
)
{
g.DrawCurve(SystemPens.ControlText, m_Points);
}
}
g.Restore(state);
}
///
<summary>
///
重写以切换Sin,Pow函数图像进行绘图测试
///
</summary>
///
<param name="e"></param>
protected
override
void
OnMouseDown(MouseEventArgs e)
{
base
.OnMouseDown(e);
if
(e.Button
==
MouseButtons.Right)
{
this
.ClearFunImage();
return
;
}
switch
(e.Clicks)
{
case
1
:
this
.DrawSin();
break
;
case
2
:
this
.DrawPow();
break
;
}
}
protected
override
bool
ProcessDialogKey(Keys keyData)
{
if
(keyData
==
Keys.Add)
{
this
.ScaleImage(
this
.m_Scale
+
this
.m_Scale
*
0.02f
);
}
else
if
(keyData
==
Keys.Subtract)
{
this
.ScaleImage(
this
.m_Scale
-
this
.m_Scale
*
0.02f
);
}
return
base
.ProcessDialogKey(keyData);
}
}
然后我们可以定义一个窗体用来承载这个控件以使控件展现它的行为:
public
class
testForm:Form
{
public
testForm()
{
//
InitializeComponent();
ImageBox box
=
new
ImageBox();
box.Dock
=
DockStyle.Fill;
box.ImageSize
=
new
Size(
200
,
150
);
this
.Controls.Add(box);
}
}
到这里我们可以运行testForm来通过点击使ImageBox显示出Sin函数或抛物线的数学图像.我们也可以通过键盘上的"+"及"-"来放大或缩小我们绘制的图,并在适当的时候通过滚动条来滚动显示图像.