图形...几乎正在工作......缺少一条线? [英] Graphics ... almost working ... missing one line?

查看:45
本文介绍了图形...几乎正在工作......缺少一条线?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我之前曾问过一些关于某些图形的问题 - 第一个

是当我将动画画到一个图片框时它不会显示

表单已加载。


每个人都建议我停止使用自己的CreateGraphics

调用,并覆盖OnPaint调用来执行此操作。正如所建议的那样,我创建了一个

自定义控件drawBox。在其OnPaint上覆盖。因为我正在使用

手动双缓冲,因为人们建议它只是将
放入OnPaint并使用drawBox.Invalidate as

电话。最后我只需要更改/添加大约8行代码,而它只需要花费大约16个小时才能确定哪些代码。


然而,仍有一个恼人的问题。我可以让它显示

初始屏幕加载时就好了。但是我真正想要它做什么

正在播放开场动画。


如果我把这个电话设置为动画屏幕

play_animation();


作为Form1_Load的最后一行或我尝试的其他任何地方,那么

Form1不会'' t实际加载并显示任何内容,直到动画完成,然后弹出窗体,只显示动画的最终(结束)帧




我用旧的(坏的)方式做了同样的问题我做了图形,所以我认为

它不相关...但是FWIW,这里是关键部位我的新图形:


公共部分类Form1:表格

{

静态公共drawBox pictureBoxn;
< br $> b $ b ....

公共类drawBox:控制

{

public drawBox()

{

this.Height = 600;

this.Width = 600;

}

protected override void OnPaint(Pa intEventArgs pe)

{

base.OnPaint(pe);

myBuffer.Render();


}

}


我在Form1_Load中调用以下内容:


pictureBoxn = new drawBox() ;

currentContext = BufferedGraphicsManager.Current;

myBuffer =

currentContext.Allocate(pictureBoxn.CreateGraphics(),

pictureBoxn.DisplayRectangle);

this.Controls.Add(pictureBoxn);


canvas = myBuffer.Graphics;

canvas .SmoothingMode =

System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

// canvas.Clear(Color.White);

screenwidth = pictureBoxn.Width;

screenheight = pictureBoxn.Height;

tracks = new Bitmap((int)screenwidth,(int)screenheight);

backgroundcanvas = Graphics.FromImage(tracks);

backgroundcanvas.SmoothingMode =

System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

backgroundcanvas.Cl ear(Color.Red);

pictureBoxn.Paint + = new

System.Windows.Forms.PaintEventHandler(this.pictur eBoxn_Paint);


pictureBoxn.Refresh();

pictureBoxn.Show();

pictureBoxn.Update();

pictureBoxn.Invalidate ();

解决方案

彼得,


play_animation的代码是什么?你是否循环并且每次调用循环时重新获得
重绘?好像就是这样。

这里。


你真正想做的是创建一个计时器让你无效
$ b $每个刻度线上的b图片框(动画的帧速率将取决于计时器的频率
)。

-

- 尼古拉斯Paldino [.NET / C#MVP]

- mv*@spam.guard.caspershouse.com


" Peter Webb" < we ******** @ DIESPAMDIEoptusnet.com.auwrote in message

news:47 ******************** ***@news.optusnet.com.a u ...


>我之前曾询问过一些关于某些图形的问题 - 第一个
当我将动画绘制到一个图片框时,当
表格加载时它就不会显示。


每个人都建议我停止使用自己的动画CreateGraphics

调用,并覆盖OnPaint调用来执行此操作。正如所建议的那样,我创建了一个

自定义控件drawBox。在其OnPaint上覆盖。因为我是使用手动双缓冲的
,因为人们建议将myBuffer.Render()放入OnPaint并使用
实际上只是一个问题。
drawBox.Invalidate作为调用。最后我只需要更改/添加大约

8行代码,而且它只用了大约16个小时才能确定

哪些代码。


但是,还有一个烦人的问题。我可以让它显示

初始屏幕加载时就好了。但是我真正希望它能够播放开场动画。


如果我把这个电话设为动画屏幕

play_animation();


作为Form1_Load的最后一行或我尝试过的其他任何地方,

然后Form1没有'' t实际加载并显示任何内容,直到动画完成

,此时弹出的Form只显示动画的最终(结束)

帧。


我用旧的(坏的)方式做了同样的问题我做了图形,所以我认为

它不相关...但是FWIW,这里是关键部位我的新图形:


公共部分类Form1:表格

{

静态公共drawBox pictureBoxn;


...


公共类drawBox:控制

{

public drawBox()

{

this.Height = 600;

this.Widt h = 600;

}

protected override void OnPaint(PaintEventArgs pe)

{

base.OnPaint( pe)​​;

myBuffer.Render();


}

}


我在Form1_Load中调用以下内容:


pictureBoxn = new drawBox();

currentContext = BufferedGraphicsManager.Current;

myBuffer =

currentContext.Allocate(pictureBoxn.CreateGraphics(),

pictureBoxn.DisplayRectangle);

this.Controls.Add(pictureBoxn);


canvas = myBuffer.Graphics;

canvas.SmoothingMode =

System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

// canvas.Clear(Color.White);

screenwidth = pictureBoxn.Width;

screenheight = pictureBoxn.Height;

tracks = new Bitmap((int)screenwidth,(int)screenheight);

backgroundcanvas = Graphics.FromImage(tracks);

backgroundcanvas.SmoothingMode =

System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

backgroundcanvas.Clear(Color.Red);

pictureBoxn.Paint + = new

System.Windows.Forms.PaintEventHandler(this.pictur eBoxn_Paint);


pictureBoxn.Refresh();

pictureBoxn.Show( );

pictureBoxn.Update();

pictureBoxn.Invalidate();




Nicholas Paldino [.NET / C#MVP]" < mv*@spam.guard.caspershouse.com写在

消息新闻:39 ************************* ********* @ microsof t.com ...


Peter,


什么在play_animation的代码?你是否循环并且每次调用循环时重新获得
重绘?好像那是

的情况。


你真正想做的是创建一个计时器让你无效

每个刻度线上的图片框(动画的帧速率将取决于计时器的频率$ / b $ b。



是的。我正在循环浏览几十个屏幕上的对象,更新他们的

位置。这就是我的计划所做的,并且花费所有时间。在主要的
动画循环中,我仍然使用myBuffer.Render()命令,因为

drawBox.Invalidate()命令闪烁(即使它只是一个

myBuffer.Render())。


我之前被告知(我想你)在异步中使用计时器

线程。这在我的应用程序中似乎不是一个问题,因为大多数人都会想要运行它,因此有两个线程更复杂,我没有b / b
明白。


令人讨厌的是,这只是表格启动时的一个问题。

所有其他功能都非常出色。


我可以做一些快速和肮脏的初学者来制作表格

出现在动画开始之前(甚至完成!)?


或者如果我必须正确地做到这一点,你能否推荐我到一个可能让我开始的网页?

2007年12月8日星期六08:11:47 -0800,Peter Webb

< we ******** @ DIESPAMDIEoptusnet.com.auwrote:


是的。我正在循环浏览几十个屏幕上的对象,更新他们的

位置。



不要这样做。


这就是我的程序所做的事,并且花费了一直以来。



即便如此,也不要这样做。


在主动画循环中,我仍然使用myBuffer.Render()命令,因为

drawBox.Invalidate()命令闪烁(即使它只是一个

myBuffer.Render())。



闪烁是因为在调用OnPaint()方法之前,调用Invalidate()会导致整个控件首先被删除

。您可以通过几种方式避免这种情况,但最简单的自定义控件就是将
设置为控件的DoubleBuffered属性为true(注意这个

属性与你自己的缓冲无关...它导致操作系统

创建一个新的屏幕外缓冲区,用于在更新期间用于绘图>
控件)。


Flicker没有理由放弃标准的Windows绘图

模型。有更好的方法来解决它。


我之前被告知(我想你)在异步中使用计时器

线程。这在我的应用程序中似乎不是一个问题,因为大多数人都希望将b
运行它,所以有两个线程更复杂,我没有b / b $ b明白了。



这里的一些要点:


*您可能不需要不同的线程。

Forms.Timer类可能为您的动画提供足够的准确性。它提供了

a稍微简单的处理时间的方法。


*在这种情况下,第二个线程不应该那么复杂。

最棘手的部分是记住你需要使用Control.Invoke()来调用任何直接使用UI组件的方法。在这种情况下,'

可能只是对Invalidate()的调用,你可以像

这样完成(使用匿名方法):


myCustomControl.Invoke((MethodInvoker)委托()

{myCustomControl.Invalidate();});


注意:当使用第二个线程时,你_might_仍会遇到时间问题,因为虽然线程本身可能能够很好地管理自己的

时间,但是重绘东西(在
失效后失效和重绘)仍然必须通过Forms.Timer对象必须经过的正常窗口消息队列

。在编写一个常规的Windows应用程序时会有一些基本的

限制,无论_how_你是以正确还是错误的方式进行绘图。


我建议你首先尝试使用Forms.Timer类,因为它提供了很好的,简单的API,你没有跨线程的问题。除了
要求从主GUI线程无效(因此需要

来使用Invoke()),使用第二个线程也意味着你需要强加

某种线程同步。要么你需要基本上

双缓冲你的缓冲区,总是有一个用于处理的完成副本

控制更新和你实际绘制的第二个副本新的

数据,或者你需要使用lock()来保护使用缓冲区的代码所以

你没有两个线程试图同时操作

的数据结构。既不困难,但为什么要介绍复杂性

如果你不需要?


请注意,无论你做什么,你都是将被限制为某种东西

使用像这样的机制15-20 fps。试图频繁更新

可能导致动画不均匀,一些

帧显示的时间比
$更短或更长b $ b其他人。对于比这更快的动画,你只需要完全摆脱标准Windows应用程序模型的

,你不会用常规的.NET做
表格申请。


*大多数人想要平稳地运行它并不是真的。b $ b out。实际上,我遇到的每个用户都希望能够随时退出

应用程序。使用你的设计是不允许的,因为在动画完成之前,表单不会响应用户输入。


令人烦恼的是,这只是表格启动时的一个问题。

其他所有功能都非常出色。



我不确定我会使用出色地这个词吗?描述你的应用程序中的_everything

else_如何工作。您可能已经将动画设置为

animate,但除此之外,您的应用程序并不是非常好的

Windows应用程序。


正如您所承诺的那样,由于未能遵守

Windows中的标准绘图模型,您已经创建了新的,难以解决的问题。


是否有一些快速和肮脏的我可以做的启动表格

出现在动画开始之前(甚至完成!)?



好​​吧,一个选项是将动画代码移动到某个地方

你知道它会在表格绘制后发生。但请记住

这样做只会改变你的表格

不会画的特定场景。展示其他场景需要绘制形式_again_,但是不可能因为你在你身上被困在
中,这将是微不足道的。动画循环。


正确的修复,也是我唯一推荐的修复,就是修改你的代码,以便你的控件_only_自我绘制以响应WM_PAINT消息。

换句话说,在OnPaint()方法中,并且在那里只绘制数据的

_current_状态。用于动画的数据的更新

(在你的情况下基本上只是指你的屏幕外缓冲区)必须是在绘图代码之外完成的
。更新数据后,只需调用

Invalidate()即可进行重绘。


或者如果我必须正确执行此操作,你能把我推荐给一个可以让我开始的网页吗?



我在上一个帖子中发布了一些有用的链接到

MSDN网站上的页面。我相信其他人已经向Bob发布了一个或多个链接

鲍威尔的网站(我不能担保,但我见过其他的

人们高度推荐。


但大多数情况下,你真的需要专注于基础知识。这里有一个非常具体但很容易描述的范例:


*确保您的绘图代码正确响应OnPaint(),

渲染数据的当前状态,并且能够在任何

时间内完成。

*不要从你所在的地方抽取改变数据。致电

Invalidate()代替。


从此,其他一切都来自。


Pete


I previously asked about two problems I had with some graphics - the first
was that when I drew animation to a picturebox it wouldn''t display when the
Form loaded.

It was suggested to me by everyone that I stop using my own CreateGraphics
calls, and override an OnPaint call to do this. As suggested, I created a
custom control "drawBox" with an override on its OnPaint. Because I am using
manual double buffering, as people suggested it was really just a matter of
putting myBuffer.Render() into the OnPaint, and using drawBox.Invalidate as
the call. In the end I only had to change/add about 8 lines of code, and its
only taken about 16 hours to work out exactly which ones.

However, there is one annoying problem that remains. I can get it to show
the initial screen just fine when it loads. But what I really want it to do
is play the opening animation.

If I put the call to animate the screen

play_animation();

As the very last line of Form1_Load or anywhere else that I have tried, then
the Form1 doesn''t actually load and show anything until the animation is
finished, at which point the Form pops up showing only the final (end) frame
of the animation.

I had the same problem with the old (bad) way I did graphics, so I assume
its not related ... but FWIW, here are they key parts of my new graphics:

public partial class Form1 : Form
{
static public drawBox pictureBoxn;

....

public class drawBox : Control
{
public drawBox()
{
this.Height = 600;
this.Width = 600;
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
myBuffer.Render();

}
}

I call the following in Form1_Load:

pictureBoxn = new drawBox();
currentContext = BufferedGraphicsManager.Current;
myBuffer =
currentContext.Allocate(pictureBoxn.CreateGraphics (),
pictureBoxn.DisplayRectangle);
this.Controls.Add(pictureBoxn);

canvas = myBuffer.Graphics;
canvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// canvas.Clear(Color.White);
screenwidth = pictureBoxn.Width;
screenheight = pictureBoxn.Height;
tracks = new Bitmap((int)screenwidth, (int)screenheight);
backgroundcanvas = Graphics.FromImage(tracks);
backgroundcanvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
backgroundcanvas.Clear(Color.Red);
pictureBoxn.Paint += new
System.Windows.Forms.PaintEventHandler(this.pictur eBoxn_Paint);

pictureBoxn.Refresh();
pictureBoxn.Show();
pictureBoxn.Update();
pictureBoxn.Invalidate();

解决方案

Peter,

What is in the code for play_animation? Are you looping through and
repainting on every invocation of the loop? It seems like that is the case
here.

What you really want to do is create a timer which you invalidate your
picturebox on every tick (the frame rate of your animation will depend on
the frequency of the timer).
--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"Peter Webb" <we********@DIESPAMDIEoptusnet.com.auwrote in message
news:47***********************@news.optusnet.com.a u...

>I previously asked about two problems I had with some graphics - the first
was that when I drew animation to a picturebox it wouldn''t display when the
Form loaded.

It was suggested to me by everyone that I stop using my own CreateGraphics
calls, and override an OnPaint call to do this. As suggested, I created a
custom control "drawBox" with an override on its OnPaint. Because I am
using manual double buffering, as people suggested it was really just a
matter of putting myBuffer.Render() into the OnPaint, and using
drawBox.Invalidate as the call. In the end I only had to change/add about
8 lines of code, and its only taken about 16 hours to work out exactly
which ones.

However, there is one annoying problem that remains. I can get it to show
the initial screen just fine when it loads. But what I really want it to
do is play the opening animation.

If I put the call to animate the screen

play_animation();

As the very last line of Form1_Load or anywhere else that I have tried,
then the Form1 doesn''t actually load and show anything until the animation
is finished, at which point the Form pops up showing only the final (end)
frame of the animation.

I had the same problem with the old (bad) way I did graphics, so I assume
its not related ... but FWIW, here are they key parts of my new graphics:

public partial class Form1 : Form
{
static public drawBox pictureBoxn;

...

public class drawBox : Control
{
public drawBox()
{
this.Height = 600;
this.Width = 600;
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
myBuffer.Render();

}
}

I call the following in Form1_Load:

pictureBoxn = new drawBox();
currentContext = BufferedGraphicsManager.Current;
myBuffer =
currentContext.Allocate(pictureBoxn.CreateGraphics (),
pictureBoxn.DisplayRectangle);
this.Controls.Add(pictureBoxn);

canvas = myBuffer.Graphics;
canvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// canvas.Clear(Color.White);
screenwidth = pictureBoxn.Width;
screenheight = pictureBoxn.Height;
tracks = new Bitmap((int)screenwidth, (int)screenheight);
backgroundcanvas = Graphics.FromImage(tracks);
backgroundcanvas.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
backgroundcanvas.Clear(Color.Red);
pictureBoxn.Paint += new
System.Windows.Forms.PaintEventHandler(this.pictur eBoxn_Paint);

pictureBoxn.Refresh();
pictureBoxn.Show();
pictureBoxn.Update();
pictureBoxn.Invalidate();



"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.comwrote in
message news:39**********************************@microsof t.com...

Peter,

What is in the code for play_animation? Are you looping through and
repainting on every invocation of the loop? It seems like that is the
case here.

What you really want to do is create a timer which you invalidate your
picturebox on every tick (the frame rate of your animation will depend on
the frequency of the timer).

Yes. I am looping through dozens of on-screen objects, updating their
position. That''s what my program does, and spends all its time. In the main
animation loop, I still use a myBuffer.Render() command, as the
drawBox.Invalidate() command flickers (even though it just does a
myBuffer.Render() ).

I was told before (and I think by you) to use a timer in asynchronous
thread. It didn''t seem an issue in my app, as most people will want to run
it flat out, so having two threads was more complexity that I didn''t
understand.

The annoying thing is that it is just a problem at start up of the Form.
Everything else works brilliantly.

Is there some quick and dirty I can do for the start-up to make the form
appear before the animation starts (or even finishes!)?

Or if I have to do this properly, can you refer me to a web page that might
get me started?


On Sat, 08 Dec 2007 08:11:47 -0800, Peter Webb
<we********@DIESPAMDIEoptusnet.com.auwrote:

Yes. I am looping through dozens of on-screen objects, updating their
position.

Don''t do that.

That''s what my program does, and spends all its time.

Even so, don''t do that.

In the main animation loop, I still use a myBuffer.Render() command, as
the drawBox.Invalidate() command flickers (even though it just does a
myBuffer.Render() ).

The flicker is because calling Invalidate() causes the entire control to
be erased first, before the OnPaint() method is called. You can avoid
this in a couple of ways, but the simplest in a custom control is to just
set the DoubleBuffered property of your control to true (note that this
property has nothing to do with your own buffering...it causes the OS to
create a new off-screen buffer to be used for drawing during updates to
the control).

Flicker isn''t a good reason to abandon the standard Windows drawing
model. There are better ways to fix it.

I was told before (and I think by you) to use a timer in asynchronous
thread. It didn''t seem an issue in my app, as most people will want to
run it flat out, so having two threads was more complexity that I didn''t
understand.

Some points here:

* You might not need a different thread. It''s possible that the
Forms.Timer class provides enough accuracy for your animation. It offers
a slightly simpler way to deal with timing.

* A second thread should not be all that complicated in this case.
The trickiest part is remembering that you need to use Control.Invoke() to
call any methods that directly use the UI component. In this case, that''s
likely to just be the call to Invalidate(), which you can accomplish like
this (using an anonymous method):

myCustomControl.Invoke((MethodInvoker)delegate()
{ myCustomControl.Invalidate(); });

Note: when using a second thread, you _might_ still run into timing
issues, because while the thread itself might be able to manage its own
timing reasonably well, the redraw stuff (invalidation and redrawing after
invalidation) still has to go through the normal window message queue that
the Forms.Timer object would have had to. There are some basic
limitations when writing a regular Windows applications that will be
present no matter _how_ you do the drawing, right way or wrong way.

I would recommend you try for the Forms.Timer class first, as it provides
a nice, easy API where you don''t have cross-thread issues. In addition to
the requirement to invalidate from the main GUI thread (and thus the need
to use Invoke()), using a second thread will also mean you need to impose
some sort of thread synchronization. Either you''ll need to essentially
double-buffer your buffer, always having a finished copy used for handling
the control update and a second copy into which you actually draw the new
data, or you''ll need to use lock() to protect any code using the buffer so
that you don''t have two threads trying to manipulate the data structure at
the same time. Neither is difficult, but why introduce the complexities
if you don''t have to?

Note that no matter what you do, you''re going to be limited to something
like 15-20 fps using a mechanism like this. Trying to update any more
frequently than that is likely to lead to uneven animation, with some
frames being displayed much shorter or longer periods of time than
others. For faster animation than that, you simply need to get away from
the standard Windows application model completely, which you''re not going
to do with a regular .NET forms application.

* It''s not really true that most people "will want to run it flat
out". Practically every user I''ve met wants to be able to exit an
application whenever they want to. Using your design won''t allow that, as
the form will not respond to user input until the animation has completed.

The annoying thing is that it is just a problem at start up of the Form.
Everything else works brilliantly.

I''m not sure I''d use the term "brilliantly" to describe how _everything
else_ in your application works. You may have gotten the animation to
animate, but other than that your application isn''t a very well-behaved
Windows application.

As promised to you, by failing to adhere to the standard drawing model in
Windows, you''ve created new, difficult-to-solve problems.

Is there some quick and dirty I can do for the start-up to make the form
appear before the animation starts (or even finishes!)?

Well, one option would be to move your animation code to some place where
you know it will take place after the form has drawn. But keep in mind
that doing that will only change the specific scenarios in which your form
won''t draw. It will be trivial to demonstrate other scenarios in which
the form needs to be draw _again_, but won''t be able to because you''re
stuck in your animation loop.

The correct fix, and the only one I will recommend, is to fix your code so
that your control _only_ draws itself in response to WM_PAINT messages.
In other words, in the OnPaint() method, and in there drawing only the
_current_ state of the data. Updates of the data used for animation
(which in your case basically just means your off-screen buffer) must be
done outside of the drawing code. When the data is updated, simply call
Invalidate() to cause a redraw to happen.

Or if I have to do this properly, can you refer me to a web page that
might get me started?

I posted a number of useful links in the previous thread, to pages on the
MSDN web site. I believe someone else has posted one or more links to Bob
Powell''s web site as well (which I can''t vouch for, but I''ve seen other
people recommend it highly).

Mostly though, you really need to just focus on the basics. There''s a
very specific, but easily-described paradigm here:

* Make sure your drawing code responds correctly to OnPaint(),
rendering the current state of the data and being able to do so at any
time.
* Don''t draw from the same place where you change the data. Call
Invalidate() instead.

From that, everything else derives.

Pete


这篇关于图形...几乎正在工作......缺少一条线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆