问题描述
我的代码需要一些帮助,因为我做错了什么!我试图以正确的方式修复和做所有事情,但我制造了更多问题......下面的代码是一个类似疼痛的程序的原型,我想在我知道我需要的一切并且原型完美运行后创建它。
我在这里分享了完整的代码,我还提供了一个下载我的完整项目/解决方案的链接,以便您可以运行它以查看发生了什么或进行修改以进行测试.
主要问题:
- 我想以该函数在绘图应用程序中的工作方式通过鼠标按钮创建矩形。这意味着当我按下鼠标时,我开始绘制矩形,当我在画布中移动鼠标时,矩形会动态改变其大小以适应。当我的鼠标抬起时,矩形就完成了。我相信你知道我在说什么,因为这就是几乎所有绘图软件绘制包括矩形在内的形状的方式。我可以对此进行编码,但我不确定我是否以正确的方式进行了编码,而且我也遇到了这个问题!在我绘制了一个矩形后,我再次单击以绘制另一个矩形后,前一个矩形消失/删除了..我需要在您的帮助下解决这个问题!
当我使用我的“图形”对象(在代码中注释)和 不是“e.Graphics”来绘制矩形这将绘制每个形状 厚/框架/更新..所以当我移动鼠标时,我会变成数百个 rect 连续抽奖..
-
我还有“画笔”工具,它可以像 Photoshop 中的画笔工具或基本 Windows 画图中的钢笔工具一样在画布上进行绘制。这部分工作正常!但是我从'MouseMove' 事件而不是从Paint 中绘制。我在教程视频中看到了这一点,但每个人都说“仅从疼痛事件中绘制”.. 当我尝试这样做时,画笔的功能已损坏。这意味着,它绘制直线而不是“追逐”鼠标光标并在其路径之后绘制。你能帮我澄清一下吗,这应该如何从 Paint 事件中工作?
-
当我单击“清除”按钮时出现了我的最后一个问题,这应该清除整个画布!这适用于我的画笔(仅当我从 MouseMove 绘制时)和像素工具(绘制单个像素)并正确清除画布。我从 Paint 事件绘制的矩形未清除。在我按下“新建”按钮以获取新的空画布后,矩形也会留在那里。这是个大问题!如果我从 Paint 事件中清除它就可以了,但这会导致 Paint 事件在无限循环中调用..
这是我的主要问题,我需要帮助! 我还有一些问题关于我从 MSDN 中无法正确理解的绘图/图形:
- 为什么我应该只从 Pain 事件中抽取?或者转出问题,如果我从 MouseMove 中绘制有什么问题?
- 什么是 OnPaint(),为什么它与 Paint() 不同?
- Invalidate().. 我知道这会调用 Paint 事件,但还有什么要知道的吗?为什么它与导致 Paint 事件重绘的任何其他方法不同?
我还想要求概述我的代码的每一行,以帮助了解我可以改进的地方,因为我在 C# 方面还很陌生!例如,当我尝试从“新建”按钮释放旧图片框时……我的所有线条都是必需的吗?还是我错过了什么?等等等等。或者我的代码逻辑很好,或者我应该以其他方式做吗?
提前感谢您抽出宝贵时间!如果您从上面的链接下载我的完整解决方案以更轻松地查看并运行/测试它,那将是最好的!我只想帮助解决我的问题并学习正确的编码方式!再次感谢,感谢您的帮助!
这里是我的代码,请注意pbx_canvas和bmp_canvas的区别! (我想在位图上绘制,因为我想稍后保存创建的图像)。
namespace NewFile___PixelDraw___Pen___Shapes
{
public partial class Form1 : Form
{
PictureBox pbx_usedColorBox;
PictureBox pbx_canvas;
Bitmap btm_canvas;
Graphics graphics;
Pen pen;
Button btn_activetool = new Button();
Point initialMouse = new Point(-1,-1);
Color chosenColor = Color.White;
bool mouseDown;
Rectangle rect;
public Form1()
{
InitializeComponent();
pen = new Pen(chosenColor,(float)num_drawWidth.Value)
{
StartCap = System.Drawing.drawing2d.LineCap.Round,EndCap = System.Drawing.drawing2d.LineCap.Round,};
tbx_canvasX.MaxLength = 5;
tbx_canvasY.MaxLength = 5;
pbx_usedColorBox = pbx_cBlack;
pnl_cPalette.BackColor = chosenColor;
pbx_cWhite.BorderStyle = BorderStyle.None;
}
// NEW
private void btn_new_Click(object sender,EventArgs e)
{
if (!string.IsNullOrEmpty(tbx_canvasX.Text) && !string.IsNullOrEmpty(tbx_canvasY.Text))
{
Point offset = new Point(7,19);
Point canvasSize = new Point(int.Parse(tbx_canvasX.Text),int.Parse(tbx_canvasY.Text));
Point canvasLocation = new Point((Size.Width / 2) - (canvasSize.X / 2) - offset.X,(Size.Height / 2) - (canvasSize.Y / 2) - offset.Y);
if (canvasSize.X != 0 && canvasSize.Y != 0)
{
if (pbx_canvas != null && btm_canvas != null)
{
pbx_canvas.MouseDown -= Pbx_canvas_MouseDown;
pbx_canvas.MouseMove -= Pbx_canvas_MouseMove;
pbx_canvas.MouseUp -= Pbx_canvas_MouseUp;
Controls.Remove(pbx_canvas);
pbx_canvas.dispose();
btm_canvas.dispose();
graphics.dispose();
}
pbx_canvas = new PictureBox
{
Size = new Size(canvasSize.X,canvasSize.Y),BorderStyle = BorderStyle.FixedSingle,Anchor = AnchorStyles.None,Location = canvasLocation,BackColor = Color.White,Cursor = Cursors.Cross,};
Controls.Add(pbx_canvas);
pbx_canvas.MouseDown += Pbx_canvas_MouseDown;
pbx_canvas.MouseMove += Pbx_canvas_MouseMove;
pbx_canvas.MouseUp += Pbx_canvas_MouseUp;
pbx_canvas.Paint += Pbx_canvas_Paint;
btm_canvas = new Bitmap(canvasSize.X,canvasSize.Y);
graphics = Graphics.FromImage(btm_canvas);
if (cbx_smoothing.Checked)
{
graphics.SmoothingMode = System.Drawing.drawing2d.SmoothingMode.AntiAlias;
}
}
else
{
tbx_canvasX.Text = "";
tbx_canvasY.Text = "";
}
}
}
// PAINT
private void Pbx_canvas_Paint(object sender,PaintEventArgs e)
{
Debug.WriteLine("IN PAINT EVENT!");
e.Graphics.DrawRectangle(pen,rect);
//graphics.DrawRectangle(pen,rect);
}
// M > DOWN
private void Pbx_canvas_MouseDown(object sender,MouseEventArgs e)
{
switch (btn_activetool.Name)
{
case "btn_toolBrush":
mouseDown = true;
initialMouse = e.Location;
break;
case "btn_toolRect":
mouseDown = true;
initialMouse = e.Location;
rect = new Rectangle(initialMouse,new Size(0,0));
break;
}
}
// M > MOVE
private void Pbx_canvas_MouseMove(object sender,MouseEventArgs e)
{
switch (btn_activetool.Name)
{
case "btn_toolBrush":
if (mouseDown == true && initialMouse.X != -1 && initialMouse.Y != -1)
{
graphics.DrawLine(pen,initialMouse,e.Location);
pbx_canvas.Image = btm_canvas;
initialMouse = e.Location;
}
break;
case "btn_toolRect":
if (mouseDown == true && initialMouse.X != -1 && initialMouse.Y != -1)
{
rect = new Rectangle(Math.Min(e.X,initialMouse.X),Math.Min(e.Y,initialMouse.Y),Math.Abs(e.X - initialMouse.X),Math.Abs(e.Y - initialMouse.Y));
pbx_canvas.Image = btm_canvas;
//Invalidate(rect);
}
break;
}
}
// M > UP
private void Pbx_canvas_MouseUp(object sender,MouseEventArgs e)
{
switch (btn_activetool.Name)
{
case "btn_toolPixel":
btm_canvas.SetPixel(e.X,e.Y,chosenColor);
pbx_canvas.Image = btm_canvas;
break;
case "btn_toolBrush":
mouseDown = false;
initialMouse.X = -1;
initialMouse.Y = -1;
break;
case "btn_toolRect":
mouseDown = false;
initialMouse.X = -1;
initialMouse.Y = -1;
break;
}
}
// PIXEL
private void btn_toolPixel_Click(object sender,EventArgs e)
{
btn_activetool.BackColor = Color.GhostWhite;
btn_activetool = (Button)sender;
btn_activetool.BackColor = Color.Beige;
}
// Brush
private void btn_toolBrush_Click(object sender,EventArgs e)
{
btn_activetool.BackColor = Color.GhostWhite;
btn_activetool = (Button)sender;
btn_activetool.BackColor = Color.Beige;
}
// RECT
private void btn_toolRect_Click(object sender,EventArgs e)
{
btn_activetool.BackColor = Color.GhostWhite;
btn_activetool = (Button)sender;
btn_activetool.BackColor = Color.Beige;
}
// CLEAR
private void btn_clear_Click(object sender,EventArgs e)
{
graphics.Clear(pbx_canvas.BackColor);
pbx_canvas.Image = null;
}
// Color change
private void G_pbx_colors_Click(object sender,EventArgs e)
{
PictureBox pbx_sender = (PictureBox)sender;
pbx_usedColorBox.BorderStyle = BorderStyle.FixedSingle;
pbx_sender.BorderStyle = BorderStyle.None;
pbx_usedColorBox = pbx_sender;
chosenColor = pbx_sender.BackColor;
pnl_cPalette.BackColor = chosenColor;
pen.Color = chosenColor;
}
// CanvasSize
private void G_tbx_canvasSize_Enter(object sender,EventArgs e)
{
BeginInvoke(new Action(() => (sender as TextBox).SelectAll()));
}
private void G_tbx_canvasSize_KeyPress(object sender,KeyPressEventArgs e)
{
e.Handled = !char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar);
}
// DrawWidth
private void num_drawWidth_ValueChanged(object sender,EventArgs e)
{
pen.Width = (float)num_drawWidth.Value;
}
}
}
解决方法
为什么我应该只从 Paint 事件中绘制?或者转出问题,如果我从 MouseMove 中绘制有什么问题?
你打算画什么?您无权访问控件的图形对象。如果需要,您可以绘制位图或其他缓冲区。但是在事件处理程序中绘制并不是一个好主意。您可能有多个输入事件,如果每个事件都导致代价高昂的绘制,而不是廉价的无效,应用程序将无法正常运行。
什么是 OnPaint(),为什么它与 Paint() 不同?
覆盖 OnPaint 或使用 Paint 事件通常无关紧要。
Invalidate().. 我知道这会调用 Paint 事件,但还有什么要知道的吗?为什么它与任何其他导致 Paint 事件重绘的方法不同?
这不会调用paint事件。它会将控件标记为稍后需要重绘。
提供的代码有很多问题,比如
- 在 switch 语句中使用字符串
- 所有逻辑似乎都在一个类中
- 您正在按钮单击处理程序中附加事件
- 据我所知,你没有处理任何东西
这是关于我将如何解决问题的高级概述。
当激活一个工具时,应该有一个状态机来跟踪使用的工具,以及如何为工具绘制形状。您似乎有一个原始状态机,但我建议使用类来模拟状态,以便每个工具彼此独立。
使用工具完成后,您应该将完成的形状绘制到表示完成图形的位图。此位图应在活动工具的图形之前绘制到屏幕上。这样,当您绘制下一个矩形时,前一个矩形会存储在位图中。
另一种方法是保留一个形状缓冲区,以便在使用工具时附加到其中。这样您就可以依次绘制每个形状,并且可以在创建后删除或更改它。