我想编写一个程序,让用户可以绘制圆形,三角形,矩形等几何形状。
我希望能够拖放或调整/移动以前绘制的形状。
但我不明白的是如何实现以下内容:
当鼠标悬停在圆圈上时,会选择圆圈,然后使用某个键可以让用户调整大小/移动它。
我怎么知道鼠标在圆圈上?
我是否需要检查鼠标坐标与所有圆形像素坐标?
我正在寻找更简单的解决方案。
答案 0 :(得分:3)
使用WPF图形和多媒体。 http://msdn.microsoft.com/en-us/library/ms752061.aspx
Windows Presentation Foundation(WPF)包括对高质量2-D和3-D图形,动画和媒体的支持。图形平台的主要功能包括:
矢量图形支持。
硬件加速。
分辨率和与设备无关的图形。
最小屏幕重绘和集成动画系统。
它拥有您需要的一切 - 为什么要重新发明轮子?
您需要将对象本身保留为图形对象,以便它们可以响应鼠标悬停事件。一旦你将它们放入位图,那么你将不得不重新发明轮子。
例如,
这是形状对象:
http://msdn.microsoft.com/en-us/library/ms747393.aspx#shapes
这是热门测试:
http://msdn.microsoft.com/en-us/library/ms752097.aspx
您还可以获得硬件加速,分辨率广告设备无关图形的优势。我认为你不能轻易地实现这一点:)
答案 1 :(得分:2)
假设您有两个这样的三角形:
var red = new Triangle(new Point(10, 10), new Point(30, 20), new Point(20, 50));
var blue = new Triangle(new Point(0, 10), new Point(20, 20), new Point(10, 30));
要表示图片,您可以将它们存储在列表中:
var picture = new List<Triangle> { red, blue };
绘制图片时,您枚举列表并单独绘制每个三角形。由于red
位于列表中的blue
之前,因此蓝色三角形会覆盖红色三角形。
foreach (var triangle in picture)
{
DrawTriangle(graphics, triangle);
}
为了让用户修改图片,您可以检测按下鼠标左键的坐标。然后,您按照尊敬的顺序枚举列表,并检查三角形的最近角落。
foreach (var triangle in picture.Reverse())
{
for (int i = 0; i < 3; i++)
{
if (Distance(mouse, triangle.Corner[i]) < 5)
{
// drag corner until mouse is released
return;
}
}
}
答案 2 :(得分:2)
在Office,Visio,PaintShop,所有绘图包中,您都拥有Z-Order的概念。
你也需要这样的东西。我想知道如果你有撤消缓冲区,你会有很多分页到磁盘。值得考虑的事情。
撤消缓冲区非常重要。除非你没有使用撤消功能。也许你保存矢量数据可能是位图。
还可以在正在绘制的对象下缓冲,无论如何,只有UI(不在内存中)才能真正快速地执行向量处理。取决于您要查找的内容,您需要使用的内容,您想要的对象数量(绘图)。双缓冲可能是好还是坏。
对于圈子命中测试: -
isInCircle = (((cursorx-circlecentrex)*(cursorx-circlecentrex)+
(cursory-circlecentrey)*(cursory-circlecentrey)) < circleradius)
在平面上使用像素坐标。
但是要遍历你的z顺序。您可以通过Tab-bing或Shift-Tab-bing或父对象内的对象层次来选择SOme图形包。并首先针对鼠标点击测试顶级Z-order。
如果支持在屏幕上移动圆圈并在靠近边缘时自动滚动编辑区域,则需要考虑许多坐标,计时器和缓冲区。国家管理真的很难。
考虑坐标变换(特别是如果编辑区域可缩放/可滚动,您需要对齐网格,子像素精度或其他功能等)。
修改
修复了isInCircle代码段及其格式。
答案 3 :(得分:1)
您必须使用自定义控件并在其上绘制任何内容。每个形状都是具有某些属性的对象。创建一个名为IShape
的接口,然后创建几个实现它的类,例如Rectangle
和Circle
。当用户点击屏幕时,您必须将光标位置与每个对象位置进行比较,然后执行某些操作并使屏幕无效。
答案 4 :(得分:0)
您应该使用Paint事件在自定义双缓冲控件中绘制形状。
例如:
///<summary>A blank control for drawing on.</summary>
[DefaultEvent("Paint")]
[Description("A blank control for drawing on.")]
[ToolboxBitmap(typeof(Canvas), "Images.Canvas.png")]
public class Canvas : Control {
///<summary>Creates a new Canvas control.</summary>
public Canvas() {
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint
| ControlStyles.Opaque
| ControlStyles.OptimizedDoubleBuffer
| ControlStyles.ResizeRedraw,
true);
}
///<summary>Gets or sets whether the control should completely repaint when it is resized.</summary>
[Description("Gets or sets whether the control should completely repaint when it is resized.")]
[DefaultValue(true)]
[Category("Appearance")]
public new bool ResizeRedraw {
get { return base.ResizeRedraw; }
set { base.ResizeRedraw = value; }
}
///<summary>Raises the Paint event.</summary>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "CA Bug")]
protected override void OnPaint(PaintEventArgs e) {
if (e == null) throw new ArgumentNullException("e");
if (ShowDesignMessage && DesignMode) {
using (var brush = new LinearGradientBrush(ClientRectangle, Color.AliceBlue, Color.DodgerBlue, LinearGradientMode.Vertical)) {
e.Graphics.FillRectangle(brush, ClientRectangle);
}
using (var font = new Font("Segoe UI", 18))
using (var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }) {
e.Graphics.DrawString("Canvas Control", font, Brushes.DarkBlue, ClientRectangle, format);
}
} else
base.OnPaint(e);
}
///<summary>Gets whether to paint a message in design mode instead of firing the Paint event.</summary>
[Browsable(false)]
protected virtual bool ShowDesignMessage { get { return true; } }
}
您应该使用e.Graphics
处理画布'paint事件并绘制所有形状对象。
在鼠标事件中,您需要遍历所有形状并找到包含鼠标的最后一个形状。要检查圆圈是否包含鼠标,请使用毕达哥拉斯定理来获得鼠标与圆心之间的距离。