1. Trang chủ
  2. » Công Nghệ Thông Tin

gdi programming with c sharp phần 10 ppsx

78 445 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 78
Dung lượng 2,12 MB

Nội dung

[ Team LiB ] This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. 13.1 Understanding the Rendering Process In previous chapters of this book, you learned how to draw graphics shapes, curves, and images. In all of these cases, the Graphics object is responsible for the drawing. When we're drawing graphics objects from within a menu or button click event handler, a call to the Invalidate method becomes imperative. If we don't call this method, the form will not paint itself, but if we write the same code on a form's OnPaint or paint event handler, there is no need to invalidate the form. In this section we will find out why that's so. 13.1.1 Understanding the Paint Event Paint event functionality is defined in the System.Windows.Forms.Control class, which is the base class for Windows Forms controls such as Label, ListBox, DataGrid, and TreeView. A paint event is fired when a control is redrawn. The Form class itself is inherited from the Control class. Figure 13.1 shows the Form class hierarchy. Figure 13.1. The Form class hierarchy The PaintEventArgs class provides data for the paint event. It provides two read-only properties: ClipRectangle and Graphics.ClipRectangle indicates the rectangle in which to paint, and the Graphics property indicates the Graphics object associated with the paint event of a particular control (including the form itself). Always be careful when you're dealing with the paint event because it is unpredictable and called automatically. The Control class also provides OnPaint methods, which can be overridden in the derived classes to fire the paint event. The signature of the OnPaint method is defined as follows: protected virtual void OnPaint( PaintEventArgs e); This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. As this definition shows, OnPaint takes a PaintEventArgs object as its only argument. The Graphics property of PaintEventArgs is used to get the Graphics object associated with a control—including the form. 13.1.2 Adding a Paint Event Handler to a Form Adding a paint event handler for any Control-derived class is pretty simple. We write an event handler that has two parameters, of types object and PaintEventArgs: private void MyPaintEventHandler(object sender, System.Windows.Forms.PaintEventArgs args) { } We can give the event handler whatever name we want. After implementing this event handler, we use the parameter args (which is a PaintEventArgs object) to get the Graphics object for the control. The following code delegates the event handler for the Paint event: this.Paint += new System.Windows.Forms.PaintEventHandler (this.MyPaintEventHandler); The following code gives the paint event handler for a form: private void MyPaintEventHandler(object sender, System.Windows.Forms.PaintEventArgs args) { // Write your code here } Now we can use the PaintEventArgs object to get the Graphics object associated with the form and use the Graphics object's methods and properties to draw and fill lines, curves, shapes, text, and images. Let's draw a rectangle, an ellipse, and some text on the form, as shown in Listing 13.1. Listing 13.1 Using the paint event handler to draw private void MyPaintEventHandler(object sender, System.Windows.Forms.PaintEventArgs args) { // Drawing a rectangle args.Graphics.DrawRectangle( new Pen(Color.Blue, 3), new Rectangle(10, 10, 50, 50)); // Drawing an ellipse args.Graphics.FillEllipse( Brushes.Red, new Rectangle(60, 60, 100, 100)); This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. // Drawing text args.Graphics.DrawString( "Text", new Font("Verdana", 14), new SolidBrush(Color.Green), 200, 200) ; } Figure 13.2 shows the output from Listing 13.1. Now if the form is covered by another window and the focus returns to the form, the code on the paint event handler will repaint the form. Figure 13.2. Drawing on a form 13.1.3 Adding a Paint Event Handler to Windows Controls As mentioned earlier, the paint event handler can be added to any Windows control that is inherited from the Control class, such as Button, ListBox, or DataGrid. In other words, each Windows control can have a paint event handler and a Graphics object, which represents the control as a drawing canvas. That means we can use a button or a list box as a drawing canvas. Let's add DataGrid and Button controls to a form. We will use the button and the data grid as our drawing canvases. Listing 13.2 adds the paint event methods of our Button1 and DataGrid1 controls. Listing 13.2 Adding a paint event handler for Windows controls // Adding a button's Paint event handler this.button1.Paint += new System.Windows.Forms.PaintEventHandler This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. (this.TheButtonPaintEventHandler); // Adding a data grid's Paint event handler this.dataGrid1.Paint += new System.Windows.Forms.PaintEventHandler (this.TheDataGridPaintEventHandler); Listing 13.3 gives the code for the Button and DataGrid paint event handlers. This code is useful when we need to draw graphics shapes on a control itself. For example, a column of a data grid can be used to display images or graphics shapes. In our example we draw an ellipse on these controls, instead of drawing on a form. The PaintEventArgs.Graphics object represents the Graphics object associated with a particular control. Once you have the Graphics object of a control, you are free to call its draw and fill methods. Listing 13.3 Drawing on Windows controls private void TheButtonPaintEventHandler(object sender, System.Windows.Forms.PaintEventArgs btnArgs) { btnArgs.Graphics.FillEllipse( Brushes.Blue, 10, 10, 100, 100); } private void TheDataGridPaintEventHandler(object sender, System.Windows.Forms.PaintEventArgs dtGridArgs) { dtGridArgs.Graphics.FillEllipse( Brushes.Blue, 10, 10, 100, 100); } Figure 13.3 shows the output of Listing 13.3. As you can see, a button or a data grid can function as a drawing canvas. The top left-hand corner of a control is the (0, 0) coordinate of the canvas associated with that control. Figure 13.3. Drawing on Windows controls This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. At this stage it is worth pointing out another big advantage that GDI+ has over GDI: the flexibility to have a Graphics object associated with a control. 13.1.4 Overriding the OnPaint Method of a Form We have already seen this in previous chapters. We can override the OnPaint method by defining it as follows: protected override void OnPaint( PaintEventArgs args) { // Add your drawing code here } Then we can use the Graphics property of PaintEventArgs to draw lines, shapes, text, and images. Listing 13.4 draws a few graphics shapes and text on our form's OnPaint method. To test this code, create a Windows application and add the code to it. Listing 13.4 Using OnPaint to draw protected override void OnPaint( PaintEventArgs args ) { // Get the Graphics object from // PaintEventArgs Graphics g = args.Graphics; // Draw rectangle g.DrawRectangle( new Pen(Color.Blue, 3), new Rectangle(10, 10, 50, 50)); // Fill ellipse g.FillEllipse( Brushes.Red, new Rectangle(60, 60, 100, 100)); // Draw text g.DrawString("Text", new Font("Verdana", 14), new SolidBrush(Color.Green), 200, 200) ; } 13.1.5 Using Visual Studio .NET to Add the Paint Event Handler If you are using Visual Studio .NET, the easiest way to add a paint event handler is to use the Properties windows of a form or control and add a paint event handler. We have seen examples of this in previous chapters. This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. 13.1.6 Disposing of Graphics Objects It is usually good programming practice to dispose of objects when you're finished using them. But it may not always be the best practice. A Graphics object must always be disposed of if it was created via the CreateGraphics method or other "CreateFrom" methods. If we use a Graphics object on a paint event or the OnPaint method from the PaintEventArgs.Graphics property, we do not have to dispose of it. Note Do not dispose of Graphics objects associated with Windows controls such as Button, ListBox, or DataGrid. If you create objects such as pens and brushes, always dispose of them. Although it is acceptable practice to rely on the garbage collector, doing so may often be at the expense of application performance. Garbage collection can be a costly affair because the garbage collector checks the memory for objects that haven't been disposed of, and this process absorbs processor time. However, the Dispose method of an object tells the garbage collector that the object is finished and ready to be disposed of. Calling the Dispose method eliminates the need to have the garbage collector check memory, and thus saves processor time. In Web pages, it is always good practice to dispose of objects as soon as they are done being used. 13.1.7 The OnPaintBackground Method The OnPaintBackground method paints the background of a control. This method is usually overridden in the derived classes to handle the event without attaching a delegate. Calling the OnPaintBackground method calls OnPaintBackground of the base class automatically, so we do not need to call it explicitly. 13.1.8 Scope and Type of Variables and Performance One of the best programming practices is the efficient use of variables and their scope. Before adding a new variable to a program, think for a second and ask yourself, "Do I really need this variable?" If you need a variable, do you really need it right now? The scope of variables and use of complex calculations can easily degrade the performance of your applications. Using global scope for pens, brushes, paths, and other objects may be useful instead of defining variables in the OnPaint or OnPaintBackground methods. Let's look at a practical example: Listing 13.5 is written on a form's paint event handler, which creates pens and brushes, and draws rectangles and polygons. Listing 13.5 Variables defined in the form's paint event handler This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { // Create brushes and pens HatchBrush hatchBrush = new HatchBrush(HatchStyle.HorizontalBrick, Color.Red, Color.Blue); Pen redPen = new Pen(Color.Red, 2); Pen hatchPen = new Pen(hatchBrush, 4); SolidBrush brush = new SolidBrush(Color.Green); // Create points for curve PointF p1 = new PointF(40.0F, 50.0F); PointF p2 = new PointF(60.0F, 70.0F); PointF p3 = new PointF(80.0F, 34.0F); PointF p4 = new PointF(120.0F, 180.0F); PointF p5 = new PointF(200.0F, 150.0F); PointF[] ptsArray ={ p1, p2, p3, p4, p5 }; float x = 5.0F, y = 5.0F; float width = this.ClientRectangle.Width - 100; float height = this.ClientRectangle.Height - 100; Point pt1 = new Point(40, 30); Point pt2 = new Point(80, 100); Color [] lnColors = {Color.Black, Color.Red}; LinearGradientBrush lgBrush = new LinearGradientBrush (pt1, pt2, Color.Red, Color.Green); lgBrush.LinearColors = lnColors; lgBrush.GammaCorrection = true; // Draw objects e.Graphics.DrawPolygon(redPen, ptsArray); e.Graphics.DrawRectangle(hatchPen, x, y, width, height); e.Graphics.FillRectangle(lgBrush, 200, 200, 200, 200); // Dispose of objects lgBrush.Dispose(); brush.Dispose(); hatchPen.Dispose(); redPen.Dispose(); hatchBrush.Dispose(); } In this example we define many variables, all of local scope. Throughout the application, the redPen, hatchBrush, hatchPen, brush, and other variables remain the same. Programmatically, it doesn't matter whether we define these variables locally or globally; the choice depends entirely on the application. It may be better to have variables defined with a global scope. If you repaint the form frequently, defining these variables globally may improve performance because time will not be wasted on re-creating the objects for each pass. On the other hand, defining objects globally may consume more resources (memory). It is also good to avoid lengthy calculations in frequently called routines. Here's an example: Listing 13.6 draws a line in a loop. As you can see, int x and int y are defined inside the loop. Listing 13.6 Defining variables inside a loop This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. for (int i = 0; i < 10000; i++) { Pen bluePen = new Pen(Color.Blue); int x = 100; int y = 100; g.DrawLine(bluePen, 0, 0, x, y); } We can easily replace the code in Listing 13.6 with Listing 13.7, which is more efficient. If a code statement does the same thing every time a control reaches it inside a loop, it is a good idea to move that statement outside the loop to save processing cycles. Listing 13.7 Defining variables outside a loop Pen bluePen = new Pen(Color.Blue); int x = 100; int y = 100; for (int i = 0; i < 10000; i++) { g.DrawLine(bluePen, 0, 0, x, y); } Sometimes using a floating point data type instead of an integer may affect the quality of a drawing, even though floating point data is costly in terms of resources. A well-designed and well-coded application also plays a vital role in performance. For example, replacing multiple if statements with a single case statement may improve performance. [ Team LiB ] This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. [ Team LiB ] 13.2 Double Buffering and Flicker-Free Drawing Do you remember the Web drawing method in Chapter 12? Drawing on the Web works differently from drawing in Windows Forms. On the Web we have many limitations, one of which is no pixelwise drawing support in the Web browser. So our approach in Chapter 12 was to convert our graphics objects into a temporary bitmap image and view the image in a Web browser. Double buffering is a similar concept. You may have seen one of the frequently asked questions on GDI+ discussion forums: "How do we create flicker-free drawings"? The double buffering technique is used to provide faster, smoother drawings by reducing flicker. In this technique, all objects are drawn on an off-screen canvas with the help of a temporary image and a Graphics object. The image is then copied to the control. If the drawing operation is small and includes drawing only simple objects such as rectangles or lines, there is no need for double buffering (it may even degrade performance). If there are many calculations or drawn elements, performance and appearance may be greatly improved through the use of double buffering. To prove the point, let's write an example. Listing 13.8 gives the code for a drawing method that draws several lines. Listing 13.8 The DrawLines method private void DrawLines(Graphics g) { float width = ClientRectangle.Width; float height = ClientRectangle.Height; float partX = width / 1000; float partY = height / 1000; for (int i = 0; i < 1000; i++) { g.DrawLine(Pens.Blue, 0, height - (partY * i), partX * i, 0); g.DrawLine(Pens.Green, 0, height - (partY * i), (width) - partX * i, 0); g.DrawLine(Pens.Red, 0, partY * i, (width) - partX * i, 0); } } To test our application, we will call it from a button click. The code for a button click event handler is given in Listing 13.9. Listing 13.9 Calling the DrawLines method // Create a Graphics object for "this" Graphics g = this.CreateGraphics(); This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks. [...]... ResizeRedraw The control is redrawn when it is resized Selectable The control can receive focus StandardClick The control implements standard click behavior StandardDoubleClick The control implements standard double-click behavior When using this option, you must also set StandardClick to true SupportsTransparentBackColor The control accepts a Color object with alpha transparency for the background color The... device contexts There is no concept of device context in managed code, but to maintain GDI interoperability, the Graphics class's GetHdc method is used to create a device context for a Graphics object (a surface) GetHdc returns an IntPtr object In Listing 14.2, first we create aGraphics object by using CreateGraphics and we draw a few graphics items From this Graphics object we create a Bitmap object,... "winspool.drv" , CharSet=CharSet.Unicode, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)] public static extern long EndDocPrinter(IntPtr hPrinter); [ DllImport( "winspool.drv", CharSet=CharSet.Unicode,ExactSpelling=true, CallingConvention=CallingConvention.StdCall )] public static extern long ClosePrinter(IntPtr hPrinter); } public class App { public static void Main() { System.IntPtr... CharSet=CharSet.Unicode,ExactSpelling=false, CallingConvention=CallingConvention.StdCall )] public static extern long StartDocPrinter(IntPtr hPrinter, int Level, ref DOCINFO pDocInfo); [ DllImport( "winspool.drv", CharSet=CharSet.Unicode,ExactSpelling=true, CallingConvention=CallingConvention.StdCall)] This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it Thanks public static extern... Dispose of objects blackPen.Dispose(); g.Dispose(); Listing 13.12 draws the same graphics objects The only difference is that this code uses a graphics path Listing 13.12 Using a graphics path to draw graphics objects Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); // Create a black pen Pen blackPen = new Pen(Color.Black, 2); // Create a graphics path GraphicsPath path = new GraphicsPath();... g1.FillRectangle(Brushes.Blue, 30, 30, 70, 70); g1.FillEllipse(new HatchBrush (HatchStyle.DashedDownwardDiagonal, Color.Red, Color.Green), 110, 110, 100 , 100 ); Bitmap curBitmap = new Bitmap( this.ClientRectangle.Width, this.ClientRectangle.Height, g1); g2 = Graphics.FromImage(curBitmap); IntPtr hdc1 = g1.GetHdc(); IntPtr hdc2 = g2.GetHdc(); BitBlt(hdc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height,... the device context During the operation when your application uses the HDC, the GDI+ should not draw anything on the Graphics object until the Graphics.ReleaseHdc method is called Every GetHdc call must be followed by a call toReleaseHdc on a Graphics object, as in the following code snippet: IntPtr hdc1 = g1.GetHdc(); // Do something with hdc1 g1.ReleaseHdc(hdc1); g2 = Graphics.FromImage(curBitmap);... IntPtr hdc1 = g1.GetHdc(); IntPtr hdc2 = g2.GetHdc(); BitBlt(hdc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, hdc1, 0, 0, 13369376); g2.DrawRectangle(Pens.Red, 40, 40, 200, 200); g1.ReleaseHdc(hdc1); g2.ReleaseHdc(hdc2); If we make a GDI+ call after GetHdc, the system will throw an "object busy" exception For example, in the preceding code snippet we make a DrawRectangle call after... scale the image, which decreases performance For example, the code e.Graphics.DrawImage(image, 10, 10; can be replaced with the following code: e.Graphics.DrawImage(image, 10, 10, image.Width, image.Height); This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it Thanks [ Team LiB ] This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com... g.Clear(this.BackColor); // Create a Bitmap object with the size of the form Bitmap curBitmap = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); // Create a temporary Graphics object from the bitmap This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it Thanks Graphics g1 = Graphics.FromImage(curBitmap); // Draw lines on the temporary Graphics object . graphics objects Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); // Create a black pen Pen blackPen = new Pen(Color.Black, 2); // Create a graphics path GraphicsPath path = new GraphicsPath(); path.AddLine(50,. behavior. StandardDoubleClick The control implements standard double-click behavior. When using this option, you must also set StandardClick to true. SupportsTransparentBackColor The control accepts a Color object. garbage collector, doing so may often be at the expense of application performance. Garbage collection can be a costly affair because the garbage collector checks the memory for objects that

Ngày đăng: 12/08/2014, 19:20

TỪ KHÓA LIÊN QUAN