第一范文网 - 专业文章范例文档资料分享平台

C#WinForm编程(3).第33章使用GDI+绘图

来源:用户分享 时间:2025/6/28 1:38:36 本文由loading 分享 下载这篇文档手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:xxxxxxx或QQ:xxxxxx 处理(尽可能给您提供完整文档),感谢您的支持与谅解。

Sender, CancelEventArgs e) { this.LoadFile(fileOpenDialog.FileName); } protected void menuFileOpen_Click(object sender, EventArgs e) { fileOpenDialog.ShowDialog(); } protected void menuFileExit_Click(object sender, EventArgs e) { this.Close(); } 下面介绍LoadFile()方法。这个方法处理文件的打开和读取操作,并确保引发Paint事件,使用新文件重新绘图: private void LoadFile(string FileName) { StreamReader sr = new StreamReader(FileName); string nextLine; documentLines.Clear(); nLines = 0; TextLineInformation nextLineInfo; while ( (nextLine = sr.ReadLine()) != null) { nextLineInfo = new TextLineInformation(); nextLineInfo.Text = nextLine; documentLines.Add(nextLineInfo); ++nLines; } sr.Close(); documentHasData = (nLines>0) ? true : false; CalculateLineWidths(); CalculateDocumentSize(); this.Text = standardTitle + \this.Invalidate(); } 这个功能的大部分都是标准的文件读取过程(参见第25章)。注意在读取文件时,给documentLines ArrayList添加文本行,使这个数组最终按顺序包含每行文本的信息。在读取文件后,设置documentHasDat标记,指定是否要显示信息。下一个任务是确定要显示内容的位置,之后确定显示文件的客户区域有多大-- 即用于设置滚动条的文档大小。最后,设置标题栏文本,并调用Invalidate()。 Invalidate()是Microsoft提供的一个非常重要的方法,所以下一节介绍这个方法的应用,之后介绍CalculateLineWidths()和CalculateDocument Size()方法的代码。

33.15.1 Invalidate()方法

Invalidate()是System.Windows.Forms.Form的一个成员,它把客户窗口区域标记为无效,因此在需要重新绘制时,它可以确保引发Paint事件。Invalidate()有两个重载方法:可以给它传送一个矩形,指定(使用页面坐标)需要重新绘制哪个窗口区域,如果不提供任何参数,它就把整个客户区域标记为无效。

如果知道需要绘制某些内容,为什么不调用OnPaint()或直接完成绘制任务的其他方法?一般情况下,最好不要直接调用绘图例程,如果代码要完成某些绘图任务,一般应调用Invalidate()。其原因如下所示:

● 绘图总是GDI+应用程序执行的一种处理器密集型的任务。在其他工作的中间进行绘图会妨碍其他工作的进行。在前面的示例中,如果在LoadFile()方法中直接调用一个方法来完成绘图,LoadFile()方法就将在绘图工作完成后才能返回。在这段时间里,应用程序不会响应其他事件。另一方面,通过调用Invalidate(),在从LoadFile返回之前,就可以让Windows引发一个Paint事件。接着Windows就可以检查等待处理的事件了。其内部的工作方式是事件被当作消息队列中一个消息。Windows会定期检查该队列,如果其中有事件,Windows就选择它,并调用相应的事件处理程序。现在Paint事件是队列中的唯一事件,所以OnPaint()会被立即调用。但是,在一个比较复杂的应用程序中,可能会有其他事件,其中一些的优先权比OnPaint()高。特别是如果用户已决定退出应用程序,该事件就会用消息WM_QUIT来标记。

● 如果有一个比较复杂的多线程应用程序,就会希望用一个线程处理所有的绘图操作。使用Invalidate()可以把所有的绘图操作传递到消息队列中,这有助于确保无论其他线程请求什么绘图操作,都由同一个线程完成所有的绘图操作(无论什么线程负责消息队列,都是由线程Application.Run()处理绘图操作)。

● 还有一个与性能有关的原因。假定在某一时刻有几个不同的屏幕绘制请求,也许代码仅能修改文档,以确保显示更新的文档,而同时用户刚刚移开另一个覆盖部分客户区域的窗口。调用Invalidate(),可以让Windows注意到发生的事件。Windows就会在需要时合并Paint事件,合并无效的区域,这样绘图操作就只执行一次。

● 最后,执行绘图的代码可能是应用程序中最复杂的代码部分,特别是当有一个比较专业化的用户界面时,就更是如此。需要长时间维护该代码的人员希望我们把所有的绘图代码都放在一个地方,且尽可能简单-- 如果程序的其他部分没有过多的路径进入该代码部分,维护就更容易。 其底线是最好把所有的绘图代码都放在OnPaint()例程中,或者在该方法中调用的其他方法。但是要维持一个平衡。如果要在屏幕上替换一个字符,最好不要影响到已经绘制好的其他内容,此时可能不需要使用Invalidate(),而只需编写一个独立的绘图例程。 注意: 在非常复杂的应用程序中,甚至可以编写一个完整的类,专门负责在屏幕上绘图。几年前MFC仍是GDI密集型应用程序的标准技术,MFC就遵循这个模式,使用一个C++类CView完成绘图操作。但即使是这样,这个类也有一个成员函数OnDraw(),用作大多数绘图请求的入口点。 33.15.2 计算项和文档的大小 下面返回CapsEditor示例,介绍在LoadFile()中调用的CalculateLineWidths()和Calculate DocumentSize()方法: private void CalculateLineWidths() { Graphics dc = this.CreateGraphics(); foreach (TextLineInformation nextLine in documentLines) { nextLine.Width = (uint)dc.MeasureString(nextLine.Text, mainFont).Width; } } 这个方法仅遍历已经读取的每行文本,使用Graphics.MeasureString()方法处理和存储字符串需要的水平间距。存储这个值是因为MeasureString()要进行大量的计算。如果没有把CapsEditor示例编写得非常简单,就不能很容易地计算出每一行的高度和位置,而这个方法也肯定需要按计算所有项的方式来实现。 知道屏幕上每个项的大小后,就可以计算每个项的位置,并计算出文档的大小。高度基本上是每行文本的高度乘以行数。宽度则需要计算,遍历每行文本,确定哪一行最长。对于高度和宽度,也可以在显示的文档周围设置一个较小的页边距,使应用程序看起来更吸引人。 下面是计算文档大小的方法: private void CalculateDocumentSize() { if (!documentHasData) { documentSize = new Size(100, 200); } else { documentSize.Height = (int)(nLines*lineHeight) + 2*(int)margin; uint maxLineLength = 0; foreach (TextLineInformation nextWord in documentLines) { uint tempLineLength = nextWord.Width + 2*margin; if (tempLineLength > maxLineLength) maxLineLength = tempLineLength; } maxLineLength += 2*margin; documentSize.Width = (int)maxLineLength; } this.AutoScrollMinSize = documentSize; } 这个方法首先检查是否有数据要显示。如果没有,就使用硬编码的文档大小,该大小足以显示很大的红色警告。如果要正确显示数据,就应使用MeasureString()确定警告信息到底有多大。

计算出文档大小后,就设置Form.AutoScrollMinSize属性,告诉Form实例文档有多大。完成后,后台就会发生一些有趣的事。在设置这个属性的过程中,客户区域会标记为无效,引发Paint事件。改变文档的大小就意味着需要添加或修改滚动条,需要重新绘制整个客户区域。为什么说这很有趣?如果回过头来看看LoadFile()的代码,就会发现在该方法中调用Invalidate()是多余的。在设置文档大小时,肯定要使客户区域无效。在LoadFile()方法中显式调用Invalidate(),说明了在一般情况下应如何完成任务。实际上在这个示例中,再次调用Invalidate()将重复请求Paint事件,这是不必要的。但是,这论证了前面论述的Invalidate()给Windows一个优化性能的机会。第二个Paint事件实际上并不会被引发:Windows发现队列中已经有一个Paint事件了,就会比较请求的无效区域,看看是否需要合并它们。在本例中,两个Paint事件都指定了整个客户区域,所以不需要合并,Windows会撤消第二个Paint请求。当然,这个过程会占用处理器一定的时间,但与某些绘图操作所占用的时间相比,它可以忽略不计。

33.15.3 OnPaint()

搜索更多关于: C#WinForm编程(3).第33章使用GDI+绘图 的文档
C#WinForm编程(3).第33章使用GDI+绘图.doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印
本文链接:https://www.diyifanwen.net/c1wlzn4u4zr3qhty4wk7w_11.html(转载请注明文章来源)
热门推荐
Copyright © 2012-2023 第一范文网 版权所有 免责声明 | 联系我们
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ:xxxxxx 邮箱:xxxxxx@qq.com
渝ICP备2023013149号
Top