C# 需要建议-如何编写绘图程序

C# 需要建议-如何编写绘图程序,c#,gdi+,C#,Gdi+,我想写一个程序,使用户能够绘制诸如圆、三角形、矩形等几何形状 我还希望能够拖放或调整/移动以前绘制的形状 我想画一个面板里面的形状。这似乎合理吗 在我画了一个圆之后,它就变成了位图的一部分。当然,我会将圆的细节保存在其他对象中 但我不明白的是如何实现以下内容: 当鼠标位于圆上方时,选择圆,然后使用某个键可使用户调整/移动圆的大小 我怎么知道鼠标在圆圈上方 是否需要检查鼠标坐标与所有圆形像素坐标 我在寻找更简单的解决方案 使用WPF图形和多媒体 Windows演示基金会(WPF)包括支持高质量的

我想写一个程序,使用户能够绘制诸如圆、三角形、矩形等几何形状

我还希望能够拖放或调整/移动以前绘制的形状

  • 我想画一个面板里面的形状。这似乎合理吗
  • 在我画了一个圆之后,它就变成了位图的一部分。当然,我会将圆的细节保存在其他对象中
  • 但我不明白的是如何实现以下内容:

    当鼠标位于圆上方时,选择圆,然后使用某个键可使用户调整/移动圆的大小

    我怎么知道鼠标在圆圈上方

    是否需要检查鼠标坐标与所有圆形像素坐标


    我在寻找更简单的解决方案

    使用WPF图形和多媒体

    Windows演示基金会(WPF)包括支持高质量的二维和三维图形、动画和媒体。图形平台的主要功能包括:

    矢量图形支持

    硬件加速

    分辨率和设备无关的图形

    最小屏幕重画和集成动画系统

    它拥有你所需要的一切——为什么要重新发明轮子

    您需要将对象本身保持为图形对象,以便它们能够响应鼠标悬停事件。一旦你把它们放到位图中,你就必须重新发明轮子

    比如说,

    以下是形状对象:

    以下是命中测试:


    您还可以利用硬件加速、分辨率和与设备无关的图形。我认为您自己无法轻松实现此功能:)

    您必须使用自定义控件并在此基础上绘制任何内容。每个形状都是具有某些属性的对象。创建一个名为
    IShape
    的接口,然后创建几个实现它的类,例如
    Rectangle
    Circle
    。当用户单击屏幕时,您必须按每个对象位置比较光标位置,然后执行某些操作并使屏幕无效。

    您应该使用绘制事件在自定义双缓冲控件内绘制形状

    例如:

    ///<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; } }
    }
    
    ///用于在其上绘图的空白控件。
    [默认事件(“绘制”)]
    [说明(“用于绘图的空白控件”)]
    [ToolboxBitmap(typeof(Canvas),“Images.Canvas.png”)]
    公共类画布:控件{
    ///创建一个新的画布控件。
    公共画布(){
    设置样式(ControlStyles.AllPaintingInWmPaint
    |ControlStyles.UserPaint
    |控件样式。不透明
    |ControlStyles.OptimizedDubleBuffer
    |ControlStyles.ResizerDraw,
    正确的);
    }
    ///获取或设置控件调整大小时是否应完全重新绘制。
    [说明(“获取或设置控件调整大小时是否应完全重新绘制。”)]
    [默认值(真)]
    [类别(“外观”)]
    公共新布尔ResizeRedraw{
    获取{return base.ResizeRedraw;}
    设置{base.ResizeRedraw=value;}
    }
    ///引发绘制事件。
    [SuppressMessage(“Microsoft.Reliability”,“CA2000:在失去作用域之前处理对象”,justify=“CA Bug”)]
    受保护的覆盖无效OnPaint(PaintEventArgs e){
    如果(e==null)抛出新的ArgumentNullException(“e”);
    if(显示设计消息和设计模式){
    使用(var笔刷=新的LinearGradientBrush(ClientRectangle、Color.AliceBlue、Color.DodgerBlue、LinearGradientMode.Vertical)){
    e、 图形。填充矩形(画笔、ClientRectangle);
    }
    使用(var字体=新字体(“Segoe UI”,18))
    使用(var format=new StringFormat{Alignment=StringAlignment.Center,LineAlignment=StringAlignment.Center}){
    e、 Graphics.DrawString(“画布控件”、字体、画笔、深蓝色、ClientRectangle、格式);
    }
    }否则
    基础漆(e);
    }
    ///获取是否在设计模式下绘制消息,而不是触发绘制事件。
    [可浏览(错误)]
    受保护的虚拟bool ShowDesignMessage{get{return true;}}
    }
    
    您应该处理画布的绘制事件,并使用
    e.Graphics
    绘制所有形状对象


    在鼠标事件中,您需要遍历所有形状并找到包含鼠标的最后一个形状。要检查圆是否包含鼠标,请使用毕达哥拉斯定理获得鼠标与圆中心之间的距离。

    假设有两个三角形,如下所示:

    要表示图片,可以将其存储在列表中:

    var picture = new List<Triangle> { 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;
            }
        }
    }
    
    foreach(picture.Reverse()中的变量三角形)
    {
    对于(int i=0;i<3;i++)
    {
    if(距离(鼠标、三角形、角[i])<5)
    {
    //拖动角点直到释放鼠标
    返回;
    }
    }
    }
    
    在Office、Visio、PaintShop和所有绘图软件包中,您都有Z顺序的概念

    你也需要这样的东西。我想知道如果你有撤销缓冲区,你是否会有大量的磁盘分页。需要考虑的事情。< /P> 撤消缓冲区很重要。除非你不想有撤销功能。也许你可以保存矢量数据,也许是位图

    此外,在正在绘制的对象下的缓冲区,图形卡仅在UI上(而不是在内存中)进行矢量运算的速度非常快。取决于您要查找的内容、需要处理的内容、需要多少(图形)对象。双重缓冲可能是好的,也可能是坏的

    对于循环命中测试:-

    isInCircle = (((cursorx-circlecentrex)*(cursorx-circlecentrex)+
      (cursory-circlecentrey)*(cursory-circlecentrey)) < circleradius)
    
    isInCircle=((光标x圆中心)*(光标x圆中心)+
    (粗略的保监会
    
    isInCircle = (((cursorx-circlecentrex)*(cursorx-circlecentrex)+
      (cursory-circlecentrey)*(cursory-circlecentrey)) < circleradius)