C# 使用WinForm绘制实时二维图形

C# 使用WinForm绘制实时二维图形,c#,winforms,2d,C#,Winforms,2d,名为floorLayout的表存储了对象的原始坐标。这些细节显示在带有基本2D形状的2D图片框中。 上表的一个子表实时更新对象的新坐标,这些新位置也应被提取到2D形状中(通过改变颜色、位置等) 我对图形肯定是新手。下面是我想澄清的几个问题 我需要一个有线程的后台工作人员来处理2D图形的更新吗 对于这种情况,还有其他方法吗 在有帮助的评论后编辑: 有一张桌子,上面有基本的座位安排细节。 座位号、座位类型(用日蚀或正方形表示)、原始座位位置 当座位被占用或预订时,必须更改图片框颜色中的参考形状 座位

名为floorLayout的表存储了对象的原始坐标。这些细节显示在带有基本2D形状的2D图片框中。 上表的一个子表实时更新对象的新坐标,这些新位置也应被提取到2D形状中(通过改变颜色、位置等)

我对图形肯定是新手。下面是我想澄清的几个问题

  • 我需要一个有线程的后台工作人员来处理2D图形的更新吗
  • 对于这种情况,还有其他方法吗
  • 在有帮助的评论后编辑:

    有一张桌子,上面有基本的座位安排细节。 座位号、座位类型(用日蚀或正方形表示)、原始座位位置

    当座位被占用或预订时,必须更改图片框颜色中的参考形状

    座位可以在不同的区域。然而,有时某个座椅可以与另一个座椅连接。当一个座椅与另一个座椅耦合时,其当前位置成为其耦合座椅的位置(位置保持原始)。两个座位的颜色都变了

    分离时,副座椅位置会变回其原始位置,颜色也会发生变化

    这意味着对于每个DML事务,lsyout都有影响。这就是我希望在不影响性能的情况下管理的内容

    该应用程序分为三个部分。设置(登录是设置的一部分)、座位分配、图形布局。尽管是C#,但该模型采用3层分层体系结构,以实现未来的web扩展性(如果需要)。另外,将服务和数据访问分开提供了很大的自由度,便于管理

    在这种情况下,我有什么选择

    Edit2:[2014/07/01]

    在为我的原始问题尝试基于位图的绘图时,我遇到了一个与线程相关的问题。我在这里发帖,因为它实际上与我与能干的回答者的讨论有关

        public void bgWorker_DoWork(object sender, DoWorkEventArgs d)
        {
            //insert seats (sql call via service/access interaces) to table
             AddSeats();
        }
    
        //this doesn't get fired----<<<-------
        public void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs p)
        {
            txtFlag.Text = p.ProgressPercentage.ToString();
    
        }
    
        //this works fine
        public void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs r)
        {
            if (r.Cancelled)
            {
                txtFlag.Text = "Failed";
            }
            else
            {
                //load datagridview using datatable returned by select query
                GetSeats();
                //draw the bitmap with a loop over the datagridview  
                DrawFuntion();
    
                //bgWorker.ReportProgress(prog, txtFlag.Text);
            }
        }
    
    public void bgWorker\u DoWork(对象发送方,DoWorkEventArgs d)
    {
    //将席位(通过服务/访问交互进行sql调用)插入表
    AddSeats();
    }
    
    //这不会被解雇----考虑到我们聊天的结果,我会选择以下布局:

    考虑到当前项目的时间限制,请将其保存在Winforms中,但请记住WPF,以便将来进行修订

    绘制1000个座位并不是一个大问题,但为了保持GUI的响应性,应该由后台工作人员这样做:

    e.Graphics.DrawImage(bmp_Display,  Point.Empty);
    
     foreach (Seat s in SeatList)
     {
        e.Graphics.FillRectangle(seatBrushes[s.State], 
                                 new Rectangle(s.Location, s.Size);
        e.Graphics.DrawString(s.Name, seatFont, Brushes.Black, s.Location);                        
     }
    
    bmp_Display = (Bitmap)bmp_Buffer.Clone();
    
    创建两个
    位图
    属性:

    public Bitmap bmp_Display { get; set; }
    public Bitmap bmp_Buffer { get; set; }
    
    在显示面板的
    Paint
    事件中,您只需将bmp\u显示转储到面板上,如下所示:

    e.Graphics.DrawImage(bmp_Display,  Point.Empty);
    
     foreach (Seat s in SeatList)
     {
        e.Graphics.FillRectangle(seatBrushes[s.State], 
                                 new Rectangle(s.Location, s.Size);
        e.Graphics.DrawString(s.Name, seatFont, Brushes.Black, s.Location);                        
     }
    
    bmp_Display = (Bitmap)bmp_Buffer.Clone();
    
    这是一个单一的命令,将很快发生

    要创建更新的楼层平面图,背景线程将座椅绘制到bmp_缓冲区上,可能如下所示:

    e.Graphics.DrawImage(bmp_Display,  Point.Empty);
    
     foreach (Seat s in SeatList)
     {
        e.Graphics.FillRectangle(seatBrushes[s.State], 
                                 new Rectangle(s.Location, s.Size);
        e.Graphics.DrawString(s.Name, seatFont, Brushes.Black, s.Location);                        
     }
    
    bmp_Display = (Bitmap)bmp_Buffer.Clone();
    
    完成后,它会将其转储到bmp_显示屏上,如下所示:

    e.Graphics.DrawImage(bmp_Display,  Point.Empty);
    
     foreach (Seat s in SeatList)
     {
        e.Graphics.FillRectangle(seatBrushes[s.State], 
                                 new Rectangle(s.Location, s.Size);
        e.Graphics.DrawString(s.Name, seatFont, Brushes.Black, s.Location);                        
     }
    
    bmp_Display = (Bitmap)bmp_Buffer.Clone();
    
    要做到这一点,你应该确保线程的安全,也许是通过一个锁

    最后,显示面板是无效的

    细节将取决于您将使用的数据结构和业务逻辑。如果只传输更改,请使用它们更新数据结构,并仍然绘制所有更改。您可以使用带有表格和其他内容的平面布置图的漂亮图像初始化缓冲区
    位图

    如果需要,您可以创建一个助手应用程序作为楼层平面编辑器,该编辑器将创建座椅数据结构并将其映射到座椅的楼层平面坐标

    下面是后台工作人员如何更新位图的示例。注意错误处理实际上是不存在的;此外,只需要一个锁。你需要以某种方式获取数据。座椅类也只是一个假人

    List<SolidBrush> seatBrushes = new List<SolidBrush>() 
        { (SolidBrush)Brushes.Red, (SolidBrush)Brushes.Green  /*..*/ };
    
    public Bitmap bmp_Display { get; set; }
    public Bitmap bmp_Buffer { get; set; }
    
    public class Seat  
    {
        public string Name { get; set; }
        public int State { get; set; }
        public Point Location { get; set; } 
        //...
    }
    
    private void drawFloorplan(List<Seat> seats)
    {
        Graphics G = Graphics.FromImage(bmp_Buffer);
        Font sFont = new Font("Consolas", 8f);
        Size seatSize = new Size(32, 20);
        foreach (Seat s in seats)
        {
            G.FillRectangle(seatBrushes[s.State], new Rectangle(s.Location, seatSize));
            G.DrawString(s.Name, sFont, Brushes.Black, s.Location);
        }
        G.Dispose();
        sFont.Dispose();
    
    
    }
    
    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
    
            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                // get the seat data..
                List<Seat> seats = new List<Seat>();
    
                if (seats.Count > 0)
                {
                    drawFloorplan(seats);
                    try { bmp_Display = (Bitmap)bmp_Buffer.Clone(); } 
                    catch { /*!!just for testing!!*/ }
                    //lock(bmp_Display) { bmp = (Bitmap) bmp_Buffer.Clone(); }
                }
    
            }
        }
    
    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if ((e.Cancelled == true))
        { this.tbProgress.Text += "Cancelled!"; }
        else if (!(e.Error == null))
        { this.tbProgress.Text += ("Error: " + e.Error.Message); }
        else
        { panel1.Invalidate(); }
    }
    
    
    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        try { e.Graphics.DrawImage(bmp, Point.Empty); } catch {/*!!just for testing!!*/ }
        //lock (bmp){   e.Graphics.DrawImage(bmp, Point.Empty);    }
    
    }
    
    List座椅刷=新列表()
    {(SolidBrush)brush.Red,(SolidBrush)brush.Green/*../};
    公共位图bmp_显示{get;set;}
    公共位图bmp_缓冲区{get;set;}
    公务舱座位
    {
    公共字符串名称{get;set;}
    公共int状态{get;set;}
    公共点位置{get;set;}
    //...
    }
    私人休息室楼层平面图(列出座位)
    {
    Graphics G=Graphics.FromImage(bmp_缓冲区);
    Font sFont=新字体(“控制台”,8f);
    尺寸座椅尺寸=新尺寸(32,20);
    foreach(座椅中的座椅s)
    {
    G.填充矩形(座椅灯芯[s.State],新矩形(s.Location,seatSize));
    G.抽绳(s.名称、sFont、刷子、黑色、s.位置);
    }
    G.处置();
    sFont.Dispose();
    }
    私有void bw_DoWork(对象发送方,DoWorkEventArgs e)
    {
    BackgroundWorker worker=发件人作为BackgroundWorker;
    if((worker.CancellationPending==true))
    {
    e、 取消=真;
    }
    其他的
    {
    //获取座位数据。。
    列表席位=新列表();
    如果(座位数>0)
    {
    图纸平面图(座椅);
    请尝试{bmp_Display=(位图)bmp_Buffer.Clone();}
    捕获{/*!!仅用于测试!!*/}
    //锁定(bmp_显示){bmp=(位图)bmp_Buffer.Clone();}
    }
    }
    }
    私有void bw_RunWorkerCompleted(对象发送方,RunWorkerCompletedEventArgs e)
    {
    如果((e.Cancelled==true))
    {this.tbProgress.Text+=“取消!”;}
    else如果(!(e.Error==null))
    {this.tbProgress.Text+=(“错误:+e.Error.Message);}
    其他的
    {panel1.Invalidate();}
    }
    私有void panel 1_Paint(对象发送器,PaintEventArgs e)
    {
    尝试{e.Graphics.DrawImage(bmp,Point.Empty);}catch{/*!!仅用于测试!!*/}
    //锁(bmp){e.Graphics.DrawImage(bmp,Point.Empty);}
    }
    
    编辑2关于线程安全的几句话:TS的目的是确保没有两个线程试图同时访问同一对象。看着代码,人们可能会想知道它是如何发生的,因为它只是在上面