名为floorLayout的表具有存储的对象的原始坐标。这些细节显示在具有基本2D形状的2D图片框中。 上表的从属实时更新对象的新坐标,这些新位置也应该更新为2D形状(通过改变颜色,位置等)。
我当然是图形新手。以下是我想澄清的几个问题。
在有用的评论后编辑:
有一张桌子,里面有基本的座位计划细节。 座位号,座位类型(由日食或方形表示),原始座位位置
当座位被占用或保留时,必须更改图片框颜色中的参考形状。
座位可以在不同的部分。然而,有时某个座位可以与另一个座位耦合。当座位与另一个座位耦合时,其当前位置将成为其座位的位置(位置保持原始状态)。两个座位'颜色变化。
分离时,辅助座椅位置会变回原来的位置并且颜色会发生变化。
这意味着对于每个DML交易,座位lsyout都会产生影响。这就是我想要管理而不影响性能的。
该应用程序有三个部分。设置(登录是设置的一部分),座位分配,图形布局。虽然它位于C#中,但该模型采用3层分层体系结构,以便将来进行Web扩展(如果需要)。此外,单独提供服务和数据访问可以提供很多自由,易于管理。
鉴于这种情况,我的选择是什么?
EDIT2: [2014年7月1日]
在尝试基于Bitmap的绘图时,我遇到了与我的线程相关的问题。我在这里发帖,因为它与我对有能力的回答者的讨论有关。
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);
}
}
一个主要问题: 1.这是否真的有意义我使用bgworker来插入数据库,当它完成时我正在调用loadgridview并绘制方法? 我实际上认为,最好在bgworker中调用draw方法,但我无法弄清楚如何(逻辑上和功能上流动) 3.当我尝试在DoWork()中运行DrawFunction()时,它只是抛出了最大的交叉线程错误:不允许访问使用不同线程创建的表单中的UI控件。
我怎样才能理解这一点?
答案 0 :(得分:2)
考虑到我们聊天的结果,这是我想要的布局:
考虑到当前项目的时间限制,请将其保留在Winforms中,但请记住WPF以便将来进行修订。
绘制一千个席位不是一个大问题,但为了保持GUI响应,它应该由这样的后台工作者完成:
创建两个Bitmap
属性:
public Bitmap bmp_Display { get; set; }
public Bitmap bmp_Buffer { get; set; }
在显示面板的Paint
事件中,您只需将bmp_Display转储到面板上,如下所示:
e.Graphics.DrawImage(bmp_Display, Point.Empty);
这是一个单一命令,会很快发生。
要创建更新的平面布局,背景线程将座位绘制到bmp_Buffer上,可能是这样的:
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上,如下所示:
bmp_Display = (Bitmap)bmp_Buffer.Clone();
要做到这一点,你应该确保线程安全,也许是锁定。
最后,显示面板为invalidated
。
详细信息取决于您将使用的数据结构和业务逻辑。如果您只传输更改,请使用它们更新数据结构并仍然全部绘制。您可以使用表格和其他内容的平面图初始化缓冲区Bitmap
。
如果需要,您可以创建一个帮助应用程序作为平面布局编辑器,它将创建座位数据结构并将其映射到座位的平面布局坐标。
以下是后台工作者如何更新位图的示例。请注意,错误处理几乎不存在;也只需要其中一个锁。你需要以某种方式得到数据。 Seat类也只是一个假人。
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); }
}
编辑2 关于线程安全的几句话:TS的目的是确保没有两个线程同时尝试访问同一个对象。查看代码可能会想知道它是如何发生的,因为只有在完成后,BW线程才会触发Invalidate。但是线程更复杂。 它们总是如此!例如,当BW线程将其结果转储到显示位图时,系统intself触发无效时会出现问题。 (只要系统看到更新屏幕的原因,系统就可以自由执行此操作。)
为了避免这种情况,其中一个同时发生的线程应该在使用资源时阻止其访问。我建议使用do_Work方法。您可能从未在测试中遇到问题。我的测试台每秒运行30次(!),几分钟后就完成了。 try - catch
只能防止崩溃。对于生产代码,应使用lock
来避免整个情况。
小心lock
:&#39;锁定块&#39;如果使用得太慷慨,就会造成死锁..