The material I will discuss is mostly found in chapter 17. I strongly recommend that you read the chapter to reinforce the material in the lecture and for additional detail.
The following are some initial thoughts and ideas about graphics under .NET.
FCL supplies numerous graphics tools. The primary namespaces in which these tools are contained are System.Drawing and System.Drawing.Drawing2D. The tools supplied by .NET are relatively easy to use.
The graphics tools that FCL supplies are collectively called the .NET resource GDI+. GDI+ stands for an extension to the Graphical Device Interface. This is a device independent graphical API. This is probably not the interface for writers of game programs. It would be too slow. What do game programmers use?
The following are some of the principle classes in the GDI+.
Graphics contains methods to draw strings of characters, lines, rectangles, circles, and other shapes. Why have strings of characters in a Graphics class?
Pen is supplies to Graphics to draw figures.
Brush is used to fill in shapes.
Color a structure used to set the color components for Graphics methods.
Font used to define fonts.
Matrix used to specify transformations. E.g. rotation, translation, and scaling. If you have taken our graphics course, you know all about these.
Technically, the GDI+ is implemented as a set of classes in unmanaged C++. FCL supplies classes that are a wrapper for the GDI+. Keep in mind, should the case every come up, that you can call unmanaged code from managed code. This does take effort to do.
The coordinate system for graphics is a bit unusual. (.NET stays with a long established tradition.) The origin is in the upper left-hand corner. Positive "x" values are to the right of the origin and positive "y" values are below. The units default to being in picture units - pixels. This takes a little getting used to. I will draw a picture in class. See figure 17.2 in the text.
When you use graphics, they are completely your responsibility. This means that if the window you are using is minimized, resized, or overlaid, it is your responsibility to redraw the window. WHY? When such events occur, the CLR calls the OnPaint function in the object to redraw the screen. Each object that allows graphics to be performed on it and has a virtual OnPaint function that you may override.
The OnPaint function should not be called directly, but instead should be called using the Invalidate method. This will force the CLR to call the OnPaint method. This is important in that the CLR supplies information about the area in which you are drawing. So, if you call OnPaint directly, the CLR does not have a chance to supply this data.
There is also a Paint event for which you can install event handler. The Invalidate method will also cause this to be called.
You usually draw in a form, but you can draw in other places. For example, you can create "interesting" push buttons.
In order to perform graphical tasks, such as drawing graphs and color control, your code needs a graphics context. The graphics context provides information on your drawing space. It is managed by a Graphics object. A graphics context is available for every object that is derived from System.Windows.Form.Forms. There are two ways of getting the graphics context for an object. These are by overriding the OnPaint function for the object and by creating a Paint event handler for the object.
This guy will seem very familiar to people who know MFC. MFC is obsolete and no longer taught at Ramapo College. There is still legacy code in MFC and some folks who want their stuff to run on pre-XP operating systems may still used it. MFC also has an OnPaint function.
To override the OnPaint function in C#, you use the following definition in your class:
protected override void OnPaint( PaintEventArgs e )
The "override" means that this method will take the place of method with the same name and arguments in the parent class. We don't use this keyword in unmanaged C++ virtual functions.
You get the Graphics object (which you will use to manage most graphical operations) from the PaintEventArgs argument as follows:
Graphics graphicsOject = e.Graphics;
The graphicsOject may now be used to perform such tasks as drawing shapes.
In keeping with the event driven nature of .NET windows applications, most programmer who want to perform graphics will install an event handler for the Paint event. This has a nice side effect that .NET can still do whatever it wants when the OnPaint function is called. By adding an event handler, we just add an additional function to be called.
We install an Paint event handler in the same manner that we install other event handlers. Namely, we do it through the properties of the control for which we want this functionality. Select events and then paint. The Paint event handler will look like:
protected void MyEventHandler_Paint( object sender, PaintEventArgs e )
You get a Graphics object in the same manner that you did with the OnPaint class. Namely, you can get it from the e object.
Note: you must get a Graphics object each time that a Paint event occurs. The Graphics object can change between one call and another.
Color are represented in .NET as a 32 bit number. The color is called an ARGB value. Each letter stands for a different element of the color. These elements are one byte in length and are represented by numeric values from 0 to 255. The following are the meaning of the letters.
Question: Why does MS claim that .NET programmers have a choice of almost 17 million colors?
Note: if a graphic card does not support a particular color, the closest value will be selected. This can also happen if you select a lower Color Quality. You can do this under properties-settings for the screen.
Color is a struct that supports colors. It has static constants for predefined colors. This include all of the primary colors and quite a number of others. It also has the following methods and properties:
These are used to color the interior of various graphical shapes. In the Graphics class, you can specify a method that has the word "Fill" followed by the shape. This type of function requires a brush to fill the figure. There are a number of different brush classes that are derived from Brush. The one that we will usually use if Solid Brush. Other brushes can be used to add texture.
The following is an example from the text that demonstrates various aspects of colors, figures and brushes. ShowColors.cs. Don't worry about the details of this program. We will cover them later. The main thing here is I want to demonstrate colors.
Notes on program:
This is a very cool component that allows the user to select colors from a palette of available colors. It is very simple to use in a program and I will demonstrate by the text's example. ShowColorsComplex.cs
This material is important, but not very difficult. I would like you to read section 17.5 and understand the examples. Before leaving this topic, a couple quick notes:
Example of creating a font:
FontStyle style = FontStyle.Italic; Font myFont = new Font( "Arial", 12, style ); // style would be elements of the FontStyle enumeration. Example bold
We can use this font in our programs. Let's try it.
The Graphics class provides methods for drawing lines, rectangles, and ovals. It uses overloading to provide a variety of methods for each task. Each method requires a pen to make line drawings and a brush to make solid figures. If Draw is part of the function name, it means to draw lines. If fill is part of the function name, it means to fill in the figure using a brush.
For shapes, four int values are required. Two for the coordinates of the upper left-hand corner and two to specify the shape's width and height.
The following are methods to draw lines, rectangles, and ellipses. They all are void functions and keep in mind that there are a number of overloads.
The following is the text's example of drawing these figures. LinesRectanglesOvals.cs We will show how the positions of the lines are figured out. We do have to be a lot more aware of pixels in this work. Note that OnPaint is called on startup. Note that a size class is used to specify the size of the class. The Size class has Height and Width properties.
Question: How do we determine the origin and size of the area in which we will draw? This is essential is we are drawing graphs. This is using the ClientSize property of the object on which we are drawing.
In the interest of time, I will ask you to read this section. The only knowledge you need for this section is to realize that you specify a start angle and a sweep angle. Positive angles go clockwise with an angle of zero being the x - axis.
GUI+ contains a lot of classes with a lot of members. (What else is new. ;-) )There are also a number of classes to support the GUI+ classes. I can't present them all. You never know features that you need until you try and do a practical example. I will present a number of new features by picking practical tasks and seeing how we can implement them.
Write a program that will graph a set of points on the entire area of a form. Before we begin, let as discuss the aspects of graphing a set of points.
Note: we can use the graphics object on most controls. graph1.cs
Notes on the code. These duplication some of my preliminary comments.
Write a program to graph the SINE function for a range of values specified by the user. (The range is a start and end x value for the graph.) The graph you draw of the sine function should look smooth. Make sure your graph does not overlap the range text box.
This example will add an x and a y axis to the graphs. The following is the program: graph2.cs This takes a lot more than you might at first suppose. We will more than double the amount of code. The following is a list of the aspects of this problem. Don't be upset if you don't understand everything.
In class we saw that certain drawing operations can cause the screen to flicker. For example, increasing the size of a window. (I will show this with our example.) The flicker occurs due to conflicts between the draw and the refresh cycle. The solutions is to have the draw go to a memory image and then move the data to the screen. So, there is a single operation to drawing the screen. Most graphics cards support this double buffering.
So, with a few lines, you can be a hero.
//// Indicates that double buffering is to be
used.
this.SetStyle(
ControlStyles.DoubleBuffer,
true );
// When a control is painted, the
functions OnPaint and OnPaintBackground are
// normally called. This call indicates that the OnPaint function will
handle everything.
this.SetStyle(
ControlStyles.AllPaintingInWmPaint,
true );
// Indicates that the control paints itself.
this.SetStyle(
ControlStyles.UserPaint,
true );
The bottom line is that if you put in these three lines in the proper place, flickering will be gone. Why would you guess that the system defaults to having flickering?????
We will play with this in class and show how well it works with changing the size of an image.