One of the many advantages of C# is that GUI development is supported by the Framework Class Library (FCL). Every aspect of our C# application—the domain classes/objects, persistence of the state of these objects to a file, GUI manipulation of these objects, and so on—can be conveniently programmed using FCL types.
The fundamental approach to graphical user interface programming with virtually any programming language, C# or otherwise, is to assemble graphical building blocks generically called components (and, in C#, often referred to specifically as controls). We assemble them in specific ways to provide the "look," or presentation, that we desire for an application, and then program their behind-the-scenes logic to enable them to do useful things. Users interact with GUI objects—buttons, text fields, lists, and so on—to provide information to the system and/or to obtain information from the system, in order to achieve some worthwhile goal: in other words, to fulfill the use cases that we identified in Chapter 9.
Instantiating objects: for example, when a student user logs on to the system, we'll instantiate a Student object as an abstraction of that user.
Invoking their methods, as we do when we invoke the ValidatePassword method on a Student object to ensure that the password a user has typed is valid.
Changing their states, by modifying attribute values and/or creating new links between the objects; for example, when a student successfully enrolls in a course, we'll form a link between the appropriate Student and Section objects.
In fact, the GUI itself consists of objects! All C# GUI components are created as objects, and hence
Are described by classes
Are instantiated via the new operator, using an appropriate constructor
Have fields (which are typically private), and properties/methods (which are typically public) that we access via dot notation
Communicate via messages
Participate in inheritance hierarchies
Are referenced by reference variables, when we wish to maintain named "handles" on them
Maintain handles on other objects—GUI as well as non-GUI objects
Collaborate with other objects—GUI as well as non-GUI objects—to accomplish the mission of the overall system
Therefore, all of the techniques that you've learned about creating and communicating with objects in general throughout this book will apply to GUI objects in particular. Through user interactions with the SRS GUI, the GUI's objects will be requested to perform services, which in many cases lead them to collaborate behind the scenes with the domain objects—Students, Professors, Courses, Sections, and so on—to carry out a particular user-requested service such as registering for a course.
Just as we often use collections to organize object references (e.g., an ArrayList to organize Student references), we assemble C# GUIs by organizing collections of components using a special type of GUI object known as a container.
We may depict the relationship between containers, components, and objects via a UML diagram, as shown in Figure 16-1.
We learn from this diagram that
Every container is a component (by virtue of inheritance), but conversely, every component is not necessarily a container.
Since a container can contain components, and a container is a component, then a container may contain other containers. In fact, this is the way that we build up complex GUIs—by embedding components in containers, a technique that we'll explore in depth in this chapter.
By way of analogy, a container is like a directory on your computer system, and the objects placed in it are like items placed within that directory.
Just as any one file has only one "home" directory, any one object can be placed in only one container.
Conversely, a container may contain many objects—including other containers—just as a directory may contain many files and/or other directories.
The C# GUI model is an event-driven system, wherein each visual object can generate a number of events based on a user's interaction with that object. For example, clicking a graphical button, pressing the Enter key on the keyboard after typing in a text field, or clicking an item within a list to select it all automatically generate events.
Events are the system's way of telling us that the user has interacted with the GUI in some fashion. Events are informational in nature; as application developers, we can choose to either program a response to an event or to ignore it as we see fit. We'll discuss events and event handling in more detail later in this chapter.
The GUI classes and support classes that we'll discuss in this chapter can be found in the System.Windows.Forms and System.Drawing namespaces of the .NET Framework Class Library. These C# namespaces provide an extensive array of ready-made GUI classes.
The System.Windows.Forms namespace contains predefined GUI classes that represent buttons, labels, menus, windows, panels, and many other things besides. We can extend the existing GUI classes to create our own custom GUI classes.
The System.Windows.Forms namespace also contains a variety of delegate types. A delegate is a C# class that can be used to respond to events generated by a GUI object. We'll discuss delegates when we discuss C# event handling later in this chapter.
The System.Drawing namespace contains support classes that can be used for such operations as changing a font, adding color to a GUI object, or defining a geometrical construct such as a point or a rectangle.
Before we dive into the details of the classes defined by the GUI libraries of the .NET FCL, however, let's take a step back and talk about some fundamental concepts of proper GUI design.
A technique known as separating the model from the view is an important design approach when developing a graphically oriented application. This concept relates to the Model-View-Controller (MVC) paradigm, which was popularized as a formal concept with the Smalltalk language, but is equally applicable to all OO languages, including C#.
MVC is a way of thinking of an application as being subdivided into three parts, known as the model, the view, and the controller:
The model embodies the abstract domain knowledge of the application: that is, the objects/classes that represent the real-world items/issues that the users of an application are familiar with, often referred to as the business logic or domain of the application. Up until this point in the book, we've been focusing almost exclusively on these so-called domain classes: Person, Student, Professor, Course, Section, and the like.
The view is the way in which we present this knowledge to the user—typically, although not exclusively, via a graphical user interface. Note that there can be many different views of the same model, in either the same or different applications. For example, with respect to the SRS GUI, we could represent the Students enrolled in a particular Section as a list of their names and student ID numbers; or as photographs; or in diagram form, as shown in Figures 16-2 through 16-4.
The controller is the automatic mechanism by which the user interface is displayed, and by which events are communicated back and forth between the model and the view; changes to the model are reflected in the view, and user interactions with the view trigger changes to the model. In the case of C#, this is handled by the .NET runtime in conjunction with the underlying windowing mechanism of your particular computer system.
As OO developers, we must
Design and program the view(s); in C#, this is accomplished through the use of the Framework Class Library (FCL) GUI namespaces that provide the graphical user interface building blocks we'll focus our attention on for much of this chapter.
Understand how the controller works, in order to take advantage of the mechanism for connecting the model and view together. This involves learning C#'s approach to event handling, which we'll discuss later in this chapter as well.
When designing and programming an application, if we take care to cleanly separate and insulate the code for the model from the code for the view, then it becomes much easier to
Add or change a view, if need be, without disturbing the underlying model: That, in essence, is what we'll be doing when we add a GUI view to the SRS application later in this chapter. As you'll see, the domain classes that we've already programmed will remain virtually unchanged by our addition of a GUI; and, those changes that we do make to the domain classes will be to enhance their abstractions, not to introduce GUI features.
Provide multiple alternative views of the same underlying model: Sometimes, we provide different views for different categories of user; for example, a professor using the SRS may see different windows and options than a student would see.
Give a single user the ability to switch among multiple views: A familiar example of this can be found within the Microsoft Windows operating system. With Windows, users are able to view the contents of a folder as icons, or as a detailed list, or as an HTML page, or even as a DOS directory listing, as shown in Figures 16-5 through 16-8.
Regardless of the view chosen, however, the underlying model in our Windows example is the same: we have a directory titled "Different Views" on our file system, and it contains four files: two text documents, a GIF image, and a PowerPoint presentation, as reflected in the UML diagram in Figure 16-9.
Our model should, in essence, be "view free": just as we can change the appearance of a sofa by adding a slipcover without changing its underlying structure or general functionality, so too should we be able to change the appearance of an application without changing its underlying structure.
One way to help ensure that the model and view are logically separated is to develop the model first, without regard for the view. In theory, if we do a proper job of developing the model, based on the analysis techniques of Part Two, then virtually any view relevant to the original goals (use cases) set forth for the system should be attainable. We'll demonstrate this concept by retroactively adding a GUI to the model that we automated in Chapter 14.
We'll learn about building C# GUIs in three stages:
First, we'll learn how to describe the desired "look" and behavior of our GUI by preparing a concept of operations document.
Secondly, we'll learn about the various classes and techniques used in producing the visual appearance of a GUI.
Finally, we'll learn how to provide behavior to our GUI through event handling.