目录
前言
大一下学期学习了C#,由于课程设计需要,所以写了这个小程序,主要用到的由C#中的委托,VisualStuidio的WinForm编程。此程序实现了简单的画笔,油漆桶,文字,形状(矩形和椭圆)工具等功能以及对工具属性的设置。
一、整体思路
1、界面
软件的主要界面由VisualStuidio窗体设计器交互式生成。
2、功能
画图功能由一个抽象类 Tools 来实现,Tools下派生了 Pencil, Line, Pattern, PaintBucket, Word的具体的工具类。
画图功能的实现是在Tools类中定义抽象方法 MouseDownDraw(object sender ,MouseEventArgs e), MouseUpDraw(object sender ,MouseEventArgs e), MouseMoveDraw(object sender ,MouseEventArgs e), MouseClickDraw(object sender ,MouseEventArgs e) ,并根据选定的Tool动态的更改界面上PictureBox的的MouseDown,MouseUp,MouseMove,MouseClick事件。
3、数据的传递
Tools和主窗体之间的数据传递:
由于Tools需要操作主窗体中的PictureBox对象,所以需要Tool在构造时需要将PictureBox的相关的一些对象。
- PictureBox pictureBox: 将主窗体的PictureBox对象传入Tool作为Tool的一个字段是为了在绘图完成后调用pictureBox.Image属性来更新屏幕上显示的内容。
- Bitmap bitmap:获取绘图后的Bitmap并设置给pictureBox的Image。
- Graphics g:保存bitmap对应的Graphic,用于调用绘图方法。
由于Tools具体的实现类所需的事件各不相同,所以我将具体的实现类所用到的事件保存在该具体实现类的一个字段中。
- protected List<string> eventName;用于保存所用到的事件名称。
- Dictionary<string, MouseEventHandler> eventMap;用于保存事件名称,和对应的EventHandler(object sender, EventArgs e)。
由于Tools实现类的的属性面板不同,所以每个实现类都有一个Panel panel字段来保存属性面板信息且必须实现抽象方法public abstract Panel GetAttrPanel();
Tools内部的数据传递:
由于在Tools.GetAttrPanel(),所有的控件没有保存在Tool的字段或属性中,所以控件虽然存在但是是不可访问的。而当更改一些该工具的一些属性(如颜色)时,一些控件需要做出相应的调整来和实际相匹配,所用我用一个字典 protected Dictionary<string, Control> attr 来保存控件对象,并在控件创建时(调用该 Tool 的 GetAttrPanel() 方法时)添加。下面为该方法的具体实现以及 attr 的具体引用。
- 方法实现
public override Panel GetAttrPanel()
{
if (panel != null)
return panel;
panel = new FlowLayoutPanel();
panel.BackColor = System.Drawing.SystemColors.AppWorkspace;
panel.AutoSize = true;
panel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
panel.Dock = DockStyle.Top;
attr.Add("panel",panel);
//color
Label color = new Label();
color.Text = "颜色";
panel.Controls.Add(color);
attr.Add("color", color);
//select_color
PictureBox select_color = new PictureBox();
select_color.Width = 30;
select_color.Height = 30;
select_color.Visible = true;
select_color.BackColor = Color.Black;
select_color.MouseDoubleClick += new MouseEventHandler(this.SelectColor);
panel.Controls.Add(select_color);
attr.Add("select_color", select_color);
//pixel
Label pixel = new Label();
pixel.Text = "粗细";
panel.Controls.Add(pixel);
attr.Add("pixel", pixel);
//set_pixel
NumericUpDown set_pixel = new NumericUpDown();
set_pixel.Width = 50;
set_pixel.Minimum = 1;
set_pixel.ValueChanged += new EventHandler(this.SetWidth);
set_pixel.ReadOnly = true;
panel.Controls.Add(set_pixel);
attr.Add("set_pixel", set_pixel);
set_pixel.Value = 5;
return panel;
}
- 具体引用
//设置Pencil颜色
private void SelectColor(object sender, MouseEventArgs e)
{
if (colorDialog.ShowDialog() == DialogResult.OK)
{
this.attr["select_color"].BackColor=colorDialog.Color;
this.pen.Color=colorDialog.Color;
}
}
//设置Pencil宽度
private void SetWidth(object sender, EventArgs e)
{
NumericUpDown width = attr["set_pixel"] as NumericUpDown;
pen.Width = (int)width.Value;
}
二、具体实现
窗体功能代码
Form1.cs
在事件的操作上,在切换工具时,由于我没有移除原本Tool的EventHandler,所以后选择的Tool的EventHandler会覆盖先前的,再次切回原来的Tool,因为事件中已经有了这个Tool的所有EventHandler所以无法添加,就表现为只能显示出第二个选择的工具的绘图效果。所以我才在每个Tool中保存了对应的事件名称,和对应的EventHandler对象用于减去先选择的Tool的委托。
using palette.Tools;
using pallet.Tools.Word;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace palette
{
public partial class Form1 : Form
{
private static Graphics g;//PictureBox的背景图片的Graphics
private static Bitmap orignalBitmap = (Bitmap)Image.FromFile("o.jpg");//一个空白图片用于初始化画布
private static List<Tool> Tools;//保存Tool的实例
private Panel attr_panel;//属性面板
private Tool SelectTool;//当前选择的工具
public Form1()
{
Tools = new List<Tool>();
InitializeComponent();
//初始化画布
Bitmap bmp = null;
bmp = new Bitmap(orignalBitmap, this.pictureBox1.Width, this.pictureBox1.Height);
this.pictureBox1.Image = bmp;
//添加工具到ListView
g = Graphics.FromImage(this.pictureBox1.Image);
Tools.Add(new Pencil(g,this.pictureBox1));
Tools.Add(new Line(g, this.pictureBox1));
Tools.Add(new pallet.Tools.PatternTool(g, this.pictureBox1));
Tools.Add(new PaintBucket(g,this.pictureBox1));
Tools.Add(new Word(g,this.pictureBox1));
foreach (Tool tool in Tools)
{
tool.Bitmap = bmp;
this.ToolBox.Items.Add(tool);
}
}
//工具选择
private void ToolBox_SelectedIndexChanged(object sender, EventArgs e)
{
//移除原有的属性栏和事件
if (this.SelectTool != null)
{
this.MPanel.Controls.Remove(this.SelectTool.GetAttrPanel());
this.ClearAllEvents(SelectTool);
}
//设置新的属性栏和事件
Tool tool = this.ToolBox.SelectedItem as Tool;
if (tool != null)
{
this.attr_panel=tool.GetAttrPanel();
this.MPanel.Controls.Add(this.attr_panel);
this.AddEventsToPBX(tool);
SelectTool=tool;
}
this.UpdateSBar();
this.UpdatePBxLocation();
}
//文件操作
private void 打开ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "jpg文件|*.jpg|png文件|*.png|jpeg文件|*.jpeg|bmp文件|*bmp";
if (ofd.ShowDialog() == DialogResult.OK)
{
Bitmap bmp = new Bitmap(ofd.FileName);
this.pictureBox1.Image = bmp;
g =Graphics.FromImage(bmp);
}
}
}
private void 保存ToolStripMenuItem_Click(object sender, EventArgs e)
{
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "jpg文件|*.jpg|png文件|*.png|jpeg文件|*.jpeg|bmp文件|*bmp";
if (sfd.ShowDialog() == DialogResult.OK)
{
this.pictureBox1.Image.Save(sfd.FileName);
}
}
}
//编辑菜单
private void 设置画板大小ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBox.Show("设置后会清空原图像,您确定继续吗?", "警告", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
using (SetSize ss = new SetSize(this.pictureBox1.Width,this.pictureBox1.Height))
{
if (ss.ShowDialog() == DialogResult.OK)
{