Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 140 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
140
Dung lượng
4,88 MB
Nội dung
8849CH26.qxd 10/10/07 11:46 AM Page 952 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 26 s INTRODUCING WINDOWS WORKFLOW FOUNDATION w fr ee -e bo o ks - w nl oa d o rg If you are interested in learning more about building custom activities, the NET Framework 3.5 SDK documentation provides a number of interesting examples, including the construction of a “Send E-mail Activity.” For more details, simply browse the Custom Activities samples found under the WF Samples node of the provided documentation (see Figure 26-29) w Figure 26-29 The NET Framework 3.5 SDK documentation provides numerous workflow examples w 952 Summary Windows Workflow Foundation (WF) is an API that was released with NET 3.0 In essence, WF allows you to model an application’s internal business processes directly within the application itself Beyond simply modeling the overall workflow, however, WF provides a complete runtime engine and several services that round out this API’s overall functionality (transaction services, persistence and tracking services, etc.) While this introductory chapter did not examine these services in any great detail, remember that a production-level WF application will most certainly make use of these facilities When building a workflow-enabled application, Visual Studio 2008 provides several designer tools, including a workflow designer, configuration using the Properties window, and (most important) the Windows Workflow Toolbox Here, you will find numerous built-in activities that constitute the overall composition of a particular workflow Once you have modeled your workflow, you are then able to execute the workflow instance using the WorkflowRuntime type, using your host of choice 8849CH27.qxd 10/16/07 11:59 AM Page 953 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com PART w w w fr ee -e bo o ks - w nl oa d o rg Desktop User Interfaces 8849CH27.qxd 10/16/07 11:59 AM Page 954 w w w fr ee -e bo o ks - w nl oa d o rg Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 8849CH27.qxd 10/16/07 11:59 AM Page 955 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 27 Programming with Windows Forms S w w w fr ee -e bo o ks - w nl oa d o rg ince the release of the NET platform (circa 2001), the base class libraries have included a particular API named Windows Forms (represented by the System.Windows.Forms.dll assembly) As you may know, the Windows Forms toolkit provides the types necessary to build desktop graphical user interfaces (GUIs), create custom controls, manage resources (string tables, icons, etc.), and perform other GUI-centric programming tasks In addition, a separate API named GDI+ (bundled within the System.Drawing.dll assembly) provides additional types that allow programmers to generate 2D graphics, interact with networked printers, and manipulate image data The Windows Forms (and GDI+) APIs are still alive and well with the release of NET 3.5, and will exist within the base class library for quite some time (arguably forever, in fact) However, since the release of NET 3.0, Microsoft shipped a brand new GUI toolkit called Windows Presentation Foundation (WPF) As you will see beginning in the next chapter, WPF provides a massive amount of horsepower that can be used to build bleeding-edge user interfaces The point of this chapter, however, is to provide a tour of the traditional Windows Forms API for one simple reason: many GUI applications simply might not require the horsepower offered by WPF In fact, for many UI applications, WPF can be overkill Furthermore, there are many existing Windows Forms applications scattered throughout the NET universe that will need to be maintained Given these points, in this chapter you will come to understand the Windows Forms programming model, work with the integrated designers of Visual Studio 2008, experiment with numerous Windows Forms controls, and receive an overview of graphics programming using GDI+ To pull this information together in a cohesive whole, we wrap things up by creating a (semicapable) painting application s Note Earlier editions of this text included three (fairly lengthy) chapters dedicated to the Windows Forms API Given that WPF is poised to become the preferred toolkit for NET GUI development, this edition has consolidated Windows Forms/GDI+ coverage to this single chapter However, those who have purchased this book can download the previous Windows Forms/GDI+ chapters in PDF format from the Apress website for free The Windows Forms Namespaces The Windows Forms API consists of hundreds of types (classes, interfaces, structures, enums, and delegates) that are organized within various namespaces of the System.Windows.Forms.dll assembly Figure 27-1 shows these namespaces displayed through the Visual Studio 2008 object browser 955 8849CH27.qxd 10/16/07 11:59 AM Page 956 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com rg CHAPTER 27 s PROGRAMMING WITH WINDOWS FORMS oa d o Figure 27-1 The Windows Forms namespaces of System.Windows.Forms.dll w nl By far and away, the most important namespace is System.Windows.Forms From a high level, the types within the System.Windows.Forms namespace can be grouped into the following broad categories: ks - • Core infrastructure: These are types that represent the core operations of a Windows Forms program (Form, Application, etc.) and various types to facilitate interoperability with legacy ActiveX controls ee -e bo o • Controls: These are types used to create rich UIs (Button, MenuStrip, ProgressBar, DataGridView, etc.), all of which derive from the Control base class Controls are configurable at design time and are visible (by default) at runtime .fr • Components: These are types that not derive from the Control base class but still provide visual features to a Windows Forms program (ToolTip, ErrorProvider, etc.) Many components (such as the Timer and BackgroundWorker) are not visible at runtime, but can be configured visually at design time w w • Common dialog boxes: Windows Forms provides a number of canned dialog boxes for common operations (OpenFileDialog, PrintDialog, ColorDialog, etc.) As you would hope, you can certainly build your own custom dialog boxes if the standard dialog boxes not suit your needs w 956 Given that the total number of types within System.Windows.Forms is well over 100 strong, it would be redundant (not to mention a terrible waste of paper) to list every member of the Windows Forms family As you work through this chapter, you will gain a firm foundation upon which to build However, be sure to check out the NET Framework 3.5 SDK documentation for further details Building a Simple Windows Forms Application (IDE-Free) As you would expect, modern NET IDEs (such as Visual Studio 2008, C# 2008 Express, or SharpDevelop) provide numerous form designers, visual editors, and integrated code generation tools (aka wizards) to facilitate the construction of a Windows Forms application While these tools are extremely useful, they can also hinder the process of learning Windows Forms, as these same tools tend to generate a good deal of boilerplate code that can obscure the core object model 8849CH27.qxd 10/16/07 11:59 AM Page 957 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 27 s PROGRAMMING WITH WINDOWS FORMS Given this, our first Windows Forms example will be created using a no-frills text editor and the C# command-line compiler (see Chapter for the details of working with csc.exe) To begin, create a folder named SimpleWinFormsApp (I’d suggest creating this directly off your C drive), open a Visual Studio 2008 command prompt, and using your text editor of choice, create a file named SimpleWFApp.cs Author the following code within your new file, and save it in the SimpleWinFormsApp folder // The minimum required namespaces using System; using System.Windows.Forms; o oa d nl w // This is our main window class MainWindow : Form {} ks - } rg namespace SimpleWFApp { // This is our application object class Program { static void Main() { Application.Run(new MainWindow()); } } s Note w w fr csc /target:winexe *.cs ee -e bo o This code represents the absolute simplest Windows Forms application At bare minimum, we need a class type that extends the Form base class and a Main() method to call the static Application.Run() method (more details on Form and Application later in this chapter) You can compile this application using the following command set (recall from Chapter that the default response file [csc.rsp] automatically references numerous NET assemblies, including System Windows.Forms.dll and System.Drawing.dll): w Technically speaking, you can build a Windows application at the command line using the /target:exe option; however, if you do, you will find that a command window will be looming in the background (and it will stay there until you shut down the main window) When you specify /target:winexe, your executable runs as a native Windows Forms application (without the looming command window) If you were to run your application, you would find you have a resizable, minimizable, maximizable, and closable topmost window (see Figure 27-2) Figure 27-2 A very simple Windows Forms application 957 8849CH27.qxd 10/16/07 11:59 AM Page 958 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 27 s PROGRAMMING WITH WINDOWS FORMS Granted, our current application is not terribly exciting, but it does illustrate how simple a Windows Forms application can be To spruce things up a bit, let’s add a custom constructor to our MainWindow type, which allows the caller to set various properties on the window to be displayed For example: oa d } o // Inherited method to center the form on the screen CenterToScreen(); rg // This is our main window class MainWindow : Form { public MainWindow(string title, int height, int width) { // Set various properties from our parent classes Text = title; Width = width; Height = height; } nl We can now update the call to Application.Run() as follows: ks - w static void Main() { Application.Run(new MainWindow("My Window", 200, 300)); } ee -e bo o While this is a step in the right direction, any window worth its salt will require various user interface elements (menu systems, status bars, buttons, etc.) to allow for input To understand how a Form-derived type can contain such elements, you must understand the role of the Controls property and the underlying controls collection .fr Populating the Controls Collection w w The System.Windows.Forms.Control base class (which is the inheritance chain of the Form type) defines a property named Controls This property wraps a custom collection nested in the Control class named ControlsCollection This collection (as the name suggests) references each UI element maintained by the derived type Like other containers, this type supports a number of methods to insert, remove, and find a given UI widget (see Table 27-1) w 958 Table 27-1 ControlCollection Members Member Meaning in Life Add() AddRange() Used to insert a new Control-derived type (or array of types) in the collection Clear() Removes all entries in the collection Count Returns the number of items in the collection GetEnumerator() Returns the IEnumerator interface for this collection Remove() RemoveAt() Used to remove a control from the collection When you wish to populate the UI of a Form-derived type, you will typically follow a very predictable series of steps: 8849CH27.qxd 10/16/07 11:59 AM Page 959 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 27 s PROGRAMMING WITH WINDOWS FORMS • Define a member variable of a given UI element within the Form derived class • Configure the look and feel of the UI element • Add the UI element to the form’s ControlsCollection container via a call to Controls.Add() Assume you wish to update your MainWindow class to support a File ® Exit menu system Here are the relevant updates, with code analysis to follow: o ee -e bo o ks - w private void BuildMenuSystem() { // Add the File menu item to the main menu mnuFile.Text = "&File"; mnuMainMenu.Items.Add(mnuFile); nl oa d public MainWindow(string title, int height, int width) { // Method to create our menu system BuildMenuSystem(); } rg class MainWindow : Form { // Members for a simple menu system private MenuStrip mnuMainMenu = new MenuStrip(); private ToolStripMenuItem mnuFile = new ToolStripMenuItem(); private ToolStripMenuItem mnuFileExit = new ToolStripMenuItem(); // Now add the Exit menu to the File menu mnuFileExit.Text = "E&xit"; mnuFile.DropDownItems.Add(mnuFileExit); mnuFileExit.Click += new System.EventHandler(this.mnuFileExit_Click); w w } w fr // Finally, set the menu for this Form Controls.Add(this.mnuMainMenu); MainMenuStrip = this.mnuMainMenu; // Handler for the File | Exit event private void mnuFileExit_Click(object sender, EventArgs e) { Application.Exit(); } } First off, notice that the MainWindow type now maintains three new member variables The MenuStrip type represents the entirety of the menu system, where a given ToolStripMenuItem represents any given topmost menu item (e.g., File) or submenu item (e.g., Exit) supported by the host s Note If you have programmed with earlier versions of Windows Forms (1.0 or 1.1), you may recall that the MainMenu type was used to hold any number of MenuItem objects The MenuStrip control (introduced with NET 2.0) is similar to MainMenu; however, MenuStrip is able to contain controls beyond “normal menu items” (combo boxes, text boxes, etc.) 959 8849CH27.qxd 10/16/07 11:59 AM Page 960 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 27 s PROGRAMMING WITH WINDOWS FORMS ee -e bo o ks - w nl oa d o rg The menu system is configured within our BuildMenuSystem() helper function Notice that the text of each ToolStripMenuItem is controlled via the Text property, each of which has been assigned a string literal containing an embedded ampersand symbol As you may already know, this syntax sets the Alt key shortcut, thus selecting Alt+F will activate the File menu, while selecting Alt+X will activate the Exit menu Also notice that the File ToolStripMenuItem object (mnuFile) adds subitems via the DropDownItems property The MenuStrip object itself adds a topmost menu item via the Items property Once the menu system has been established, it is then added to the controls collection (via the Controls property), after which we assign our MenuStrip object to the inherited MainMenuStrip property While this step may seem redundant, having a specific property such as MainMenuStrip makes it possible to dynamically establish which menu system to show a user, perhaps due to user preferences or security settings The only other point of interest is the fact that we are handling the Click event of the File ® Exit menu, in order to capture when the user selects this submenu The Click event works in conjunction with a standard delegate type named System.EventHandler This event can only call methods that take a System.Object as the first parameter and a System.EventArgs as the second Here, our delegate target (mnuFileExit_Click) has been implemented to terminate the entire Windows application using the static Application.Exit() method Once this application has been recompiled and exeFigure 27-3 A simple window, with a cuted, you will now find your simple window sports a simple menu system custom menu system (see Figure 27-3) The Role of System.EventArgs and System.EventHandler w w fr System.EventHandler is one of many delegate types used within the Windows Forms (and ASP.NET) APIs during the event-handling process As you have seen, this delegate can only point to methods where the first argument is of type System.Object, which is a reference to the type that sent the event For example, if we were to update the implementation of the mnuFileExit_Click() method as follows: w 960 private void mnuFileExit_Click(object sender, EventArgs e) { MessageBox.Show(string.Format("{0} sent this event", sender.ToString())); Application.Exit(); } we would be able to verify that the mnuFileExit type sent the event, as the string "E&xit sent this event" is displayed within the message box You may be wondering what purpose the second argument, System.EventArgs, serves In reality, the System.EventArgs type brings little to the table, as it simply extends Object and provides practically nothing by way of addition functionality: public class EventArgs { public static readonly EventArgs Empty; static EventArgs(); public EventArgs(); } 8849CH27.qxd 10/16/07 11:59 AM Page 961 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 27 s PROGRAMMING WITH WINDOWS FORMS The SimpleWinFormsApp project can be found under the Chapter 27 subdirectory nl oa d s Source Code o rg This type is, however, very useful in the overall scheme of NET event handling, in that it is the parent to many (very useful) derived types For example, the MouseEventArgs type extends EventArgs to provide details regarding the current state of the mouse KeyEventArgs also extends EventArgs to provide details of the state of the keyboard (such as which key was pressed), PaintEventArgs extends EventArgs to yield graphically relevant data, and so forth You will see numerous EventArgs descendents (and the delegates that make use of them) not when working with Windows Forms, but with the WPF and ASP.NET APIs as well In any case, while we could most certainly continue to build more and more functionality into our MainWindow (status bars, dialog boxes, etc.) using a simple text editor, we will eventually end up with hand cramps, as we have to manually author all the grungy control configuration logic Thankfully, Visual Studio 2008 provides numerous integrated designers that take care of these details on our behalf As we use these tools during the remainder of this chapter, always remember that they are authoring everyday C# code There is nothing “magical” about them whatsoever w The Visual Studio Windows Forms Project Template w w w fr ee -e bo o ks - When you wish to leverage the Windows Forms designer tools of Visual Studio 2008, your first step is to select the Windows Application project template via the File ® New Project menu option To get comfortable with the core Windows Forms designer tools, create a new application named SimpleVSWinFormsApp (see Figure 27-4) Figure 27-4 The Visual Studio Windows Forms project template 961 8849CH29.qxd 10/16/07 12:17 PM Page 1077 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS FillListBox(); } private void FillListBox() { // Add items to the list box lstVideoGameConsoles.Items.Add("Microsoft XBox 360"); lstVideoGameConsoles.Items.Add("Sony Playstation 3"); lstVideoGameConsoles.Items.Add("Nintendo Wii"); lstVideoGameConsoles.Items.Add("Sony PSP"); lstVideoGameConsoles.Items.Add("Nintendo DS"); } } w nl oa d o rg One thing that might strike you as odd is that in the XAML description of the ListBox, we made use of types to populate the items; however, here we have made use of string types when calling the Add() method The short explanation is that when using XAML, types are more convenient in that they are defined within the http://schemas.microsoft.com/ winfx/2006/xaml/presentation XML namespace, and therefore we have a direct reference to them Under the hood, ToString() is called on each type, so the end result is identical If you truly wanted to use a System.String to fill the ListBox (or ComboBox) type in XAML, you would need to define a new XML namespace to bring in mscorlib.dll (see Chapter 28 for more details): ee -e bo o ks - Microsoft XBox 360 Sony Playstation 3 Nintendo Wii Sony PSP Nintendo DS w w w fr Conversely, if you really wanted to, you could programmatically populate an ItemsControlderived type using strongly typed ListBoxItem objects; however, you really gain nothing for the current example and have in fact created additional work for yourself (as the ListBoxItem does not have a constructor to set the Content property!) Adding Arbitrary Content Because ListBox and ComboBox both have ContentControl in their inheritance chain, they can contain data well beyond a simple string Consider the following ComboBox, which contains various containing 2D graphical objects and a descriptive label: Yellow Blue 1077 8849CH29.qxd 10/16/07 12:17 PM Page 1078 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS Green ee -e bo o ks - w nl oa d o rg Figure 29-12 shows the output of our current list types Figure 29-12 ItemsControl-derived types can contain any sort of content you desire .fr Determining the Current Selection w w Once you have populated a ListBox or ComboBox type, the next obvious issue is how to determine at runtime which item the user has selected As it turns out, you have three ways to so If you are interested in finding the numerical index of the item selected, you can use the SelectedIndex property (which is zero based; a value of -1 represents no selection) If you wish to obtain the object within the list that has been selected, the SelectedItem property fits the bill Finally, the SelectedValue allows you to obtain the value of the selected object (typically obtained via a call to ToString()) Sounds simple enough, right? Well, to test how each property behaves, assume you have defined two new Button types for the current window, both of which handle the Click event: w 1078 Get Video Game System Get Color The Click handler for btnGetGameSystem will obtain the values of the SelectedIndex, SelectedItem, and SelectedValue properties of the lstVideoGameConsoles object and display them in a message box: 8849CH29.qxd 10/16/07 12:17 PM Page 1079 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS protected void btnGetGameSystem_Click(object sender, RoutedEventArgs args) { string data = string.Empty; data += string.Format("SelectedIndex = {0}\n", lstVideoGameConsoles.SelectedIndex); data += string.Format("SelectedItem = {0}\n", lstVideoGameConsoles.SelectedItem); data += string.Format("SelectedValue = {0}\n", lstVideoGameConsoles.SelectedValue); MessageBox.Show(data, "Your Game Info"); } ee -e bo o Figure 29-13 Finding a selected string ks - w nl oa d o rg If you were to select “Nintendo Wii” from the list of game consoles and click the related button, you would find the message box shown in Figure 29-13 However, what about obtaining the selected color? Determining the Current Selection for Nested Content w w w fr Assume the Click event handler for the btnGetColor Button has implemented btnGetColor_Click() to print out the current selection, index, and value of the lstColors ListBox object Now, if you were to select the first item in the lstColors list box (and click the related button), you may be surprised to find the output shown in Figure 29-14 Figure 29-14 Finding a selected StackPanel? The reason for this output is the fact that the lstColors object is maintaining three StackPanel objects, each of which contains nested content Therefore, SelectedItem and SelectedValue are simply calling ToString() on the StackPanel type, which returns its fully qualified name While you would be able to simply figure out which item was selected using the numerical value returned from SelectedIndex, another approach is to drill into the StackPanel’s child 1079 8849CH29.qxd 10/16/07 12:17 PM Page 1080 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS collection to grab the Content value of the Label using the StackPanel’s internally maintained Children collection as follows: protected void btnGetColor_Clicked(object sender, RoutedEventArgs args) { // Get the Content value in the selected Label in the StackPanel StackPanel selectedStack = (StackPanel)lstColors.Items[lstColors.SelectedIndex]; string color = ((Label)(selectedStack.Children[1])).Content.ToString(); string data = string.Empty; data += string.Format("SelectedIndex = {0}\n", lstColors.SelectedIndex); data += string.Format("Color = {0}", color); MessageBox.Show(data, "Your Game Info"); rg } ee -e bo o ks - w nl oa d o While this does the trick, this solution is very fragile in that we have hard-coded positions within the StackPanel (the second child, being the Label) and are required to perform numerous casting operations Another alternative is to set the Tag property of each StackPanel, which is defined in the FrameworkElement base class: fr Using this approach, our code cleans up considerably, as we can pluck out the value assigned to Tag programmatically as follows: w w protected void btnGetColor_Clicked(object sender, RoutedEventArgs args) { string data = string.Empty; data += string.Format("SelectedIndex = {0}\n", lstColors.SelectedIndex); data += string.Format("SelectedItem = {0}\n", lstColors.SelectedItem); data += string.Format("SelectedValue = {0}", (lstColors.Items[lstColors.SelectedIndex] as StackPanel).Tag); MessageBox.Show(data, "Your Color Info"); } w 1080 While this approach is a bit cleaner than our first attempt, there are other manners in which you can capture values from a complex control using data templates To so requires an understanding of the WPF data-binding engine, which you will examine at the conclusion of this chapter s Source Code The ListControls project is included under the Chapter 29 subdirectory 8849CH29.qxd 10/16/07 12:17 PM Page 1081 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS Working with Text Areas WPF ships with a number of UI elements that allow you to gather textual-based user input The most primitive types would be TextBox and PasswordBox, which we will examine here using a new Visual Studio 2008 WPF Application named TextControls Working with the TextBox Type Like other TextBox types you have used in the past, the WPF TextBox type can be configured to hold a single line of text (the default setting) or multiple lines of text if the AcceptReturn property is set to true Information within a TextBox will always be treated as character data, and therefore the “content” is always a string type that can be set and retrieved using the Text property: rg w nl oa d o One aspect of the WPF TextBox type that is very unique is that it has the built-in ability to check the spelling of the data entered within it by setting the SpellCheck.IsEnabled property to true When you so, you will notice that like Microsoft Office, misspelled words are underlined in a red squiggle Even better, there is an underlying programming model that gives you access to the spellchecker engine, which allows you to get a list of suggestions for misspelled words Update your current window XAML definition to make use of a Label, TextBox, and Button as follows (notice this TextBox supports multiple lines of text and has enabled spell checking): w w w fr ee -e bo o ks - Is this word spelled correctly? With just this much functionality, you will already notice that when you type misspelled words into your TextBox, errors are marked as such To complete our simple spell checker, update the Click event handler for the Button type as follows: protected void btnOK_Click(object sender, RoutedEventArgs args) { string spellingHints = string.Empty; // Try to get a spelling error at the current caret location SpellingError error = txtData.GetSpellingError(txtData.CaretIndex); if (error != null) { // Build a string of spelling suggestions foreach (string s in error.Suggestions) { spellingHints += string.Format("{0}\n", s); 1081 8849CH29.qxd 10/16/07 12:17 PM Page 1082 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS } // Show suggestions MessageBox.Show(spellingHints, "Try these instead"); } } ee -e bo o ks - w nl oa d o rg The code is quite simple We simply figure the current location of the caret in the text box using the CaretIndex property in order to extract a SpellingError object If there is an error at said location (meaning the value is not null), we loop over the list of suggestions via the aptly named Suggestions property Finally, we display the possibilities using a simple MessageBox.Show() request Figure 29-15 shows a possible test run when the caret is within the misspelled word “auromatically.” fr Figure 29-15 A custom spell checker! w Working with the PasswordBox Type w The PasswordBox type, not surprisingly, allows you to define a safe place to enter sensitive text data By default, the password character is a circle type; however, this can be changed using the PasswordChar property To obtain the value entered by the end user, simply check the Password property Let’s update our current spell-checking application by requiring the correct password to see the list of spelling suggestions First, update your existing with a nested that places the PasswordBox horizontally alongside the existing : w 1082 Is this word spelled correctly? 8849CH29.qxd 10/16/07 12:17 PM Page 1083 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS Now update your current Button Click event handler to make a call to a helper function named CheckPassword(), which tests against a hard-coded string Be sure to only allow the suggestions to be presented if the check is successful Here are the relevant updates: o rg public partial class MainWindow : System.Windows.Window { protected void btnOK_Click(object sender, RoutedEventArgs args) { if (CheckPassword()) { // Same spell-checking logic as before } else MessageBox.Show("Security error!!"); } w nl oa d private bool CheckPassword() { if (pwdText.Password == "Chucky") return true; else return false; } ks - } w w w fr ee -e bo o Beyond TextBox and PasswordBox, be aware that if you are building an application that has a text area that can contain any type of content (graphical renderings, text, etc.), WPF also provides the RichTextBox Furthermore, if you require the horsepower to build an extremely text-intensive application, WPF provides an entire document presentation API represented primarily within the System.Windows.Documents namespace Here you will find types that allow you to build flow documents, which allow you to programmatically represent (in XAML or C# code) paragraphs, sections of related text blocks, sticky notes, annotations, tables, and other rich document-centric types This edition of the text does not cover the RichTextBox or the flow document API, however; be sure to consult the NET Framework 3.5 SDK documentation for further details if you are so inclined s Source Code The TextControls project is included under the Chapter 29 subdirectory That wraps up our initial look at the WPF control set You’ll see how to build menu systems, status bars, and toolbars later in this chapter The next task, however, is to learn how to arrange UI elements within a Window type using any number of panel types Controlling Content Layout Using Panels A real-world WPF application invariability contains a good number of UI elements (user input controls, graphical content, menu systems, status bars, etc.) that need to be well organized within the containing window As well, once the UI widgets have been placed in their new home, you will want to make sure they behave as intended when the end user resizes the window or possibly a portion of the window (as in the case of a splitter window) To ensure your WPF controls retain their position within the hosting window, we are provided with a good number of panel types 1083 8849CH29.qxd 10/16/07 12:17 PM Page 1084 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS As you may recall from the previous chapter, when you place content within a window that does not make use of panels, it is positioned dead center within the container Consider the following simple window declaration containing a single Button type Regardless of how you resize the window, the UI widget is always equidistant on all four sizes of the client area OK o rg Also recall that if you attempt to place multiple elements directly within the scope of a , you will receive markup and/or compile-time errors The reason for these errors is that a window (or any descendant of ContentControl for that matter) can assign only a single object to its Content property: w nl oa d ee -e bo o ks - ! Enter Car Information OK w fr Obviously a window that can only contain a single item is of little use When a window needs to contain multiple elements, they must be arranged within any number of panels The panel will contain all of the UI elements that represent the window, after which the panel itself is used as the object assigned to the Content property w The Core Panel Types of WPF The System.Windows.Controls namespace System.Windows.Controls namespace provides numerous panel types, each of which controls how subelements are positioned Using panels, you can establish how the widgets behave when the end user resizes the window, if they remain exactly where placed at design time, if they reflow horizontally left to right or vertically top to bottom, and so forth To build complex user interfaces, panel controls can be intermixed (e.g., a DockPanel that contains a StackPanel) to provide for a great deal of flexibility and control Furthermore, the panel types can work in conjunction with other document-centric controls (such as the ViewBox, TextBlock, TextFlow, and Paragraph types) to further customize how content is arranged within a given panel Table 29-3 documents the role of some commonly used WPF panel controls w 1084 Table 29-3 Core WPF Panel Controls Panel Control Meaning in Life Canvas Provides a “classic” mode of content placement Items stay exactly where you put them at design time DockPanel Locks content to a specified side of the panel (Top, Bottom, Left, or Right) Grid Arranges content within a series of cells, maintained within a tabular grid 8849CH29.qxd 10/16/07 12:17 PM Page 1085 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS Panel Control Meaning in Life StackPanel Stacks content in a vertical or horizontal manner, as dictated by the Orientation property WrapPanel Positions content from left to right, breaking the content to the next line at the edge of the containing box Subsequent ordering happens sequentially from top to bottom or from right to left, depending on the value of the Orientation property ee -e bo o ks - w nl oa d o rg To illustrate the use of these commonly used panel types, in the next sections we’ll build the UI shown in Figure 29-16 within various panels and observe how the positioning changes when the window is resized Figure 29-16 Our target UI layout w fr Positioning Content Within Canvas Panels w w Far and away, the simplest panel is Canvas Most likely, Canvas is the panel you will feel most at home with, as it emulates the default layout of a Windows Forms application Simply put, a Canvas panel allows for absolute positioning of UI content If the end user resizes the window to an area that is smaller than the layout maintained by the Canvas panel, the internal content will not be visible until the container is stretched to a size equal to or larger than the Canvas area To add content to a Canvas, define the required subelements within the scope of the opening and closing tags and specify the location where rendering should occur (note that the content position can be relative to the left/right or top/bottom of the Canvas, but not both) If you wish to have the Canvas stretch over the entire surface of the container, simply omit the Height and Width properties Consider the following XAML markup, which defines the layout shown in Figure 29-16: OK Enter Car Information Make 1085 8849CH29.qxd 10/16/07 12:17 PM Page 1086 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS Color Pet Name fr ee -e bo o ks - w nl oa d o rg In this example, each item within the scope is qualified by a Canvas.Left and Canvas Top value, which control the content’s top-left positioning within the panel, using attached property syntax (see Chapter 28) As you may have gathered, vertical positioning is controlled using the Top or Bottom property, while horizontal positioning is established using Left or Right Given that each widget has been placed within the element, we find that as the window is resized, widgets are covered up if the container’s surface area is smaller than the content (see Figure 29-17) w w Figure 29-17 Content in a Canvas panel allows for absolute positioning The order you declare content within a Canvas is not used to calculate placement, as this is based on the control’s size and the Canvas.Top, Canvas.Bottom, Canvas.Left, and Canvas.Right properties Given this, the following markup (which groups together like-minded controls) results in an identical rendering: w 1086 Enter Car Information Color Pet Name Make OK 8849CH29.qxd 10/16/07 12:17 PM Page 1087 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS s Note If subelements within a Canvas not define a specific location using attached property syntax, they automatically attach to the extreme upper-left corner of the Canvas The SimpleCanvas.xaml file can be found under the Chapter 29 subdirectory w nl s Source Code oa d o rg Although using the Canvas type may seem like a preferable way to arrange content (because it feels so familiar), it does suffer from some limitations First of all, items within a Canvas not dynamically resize themselves when applying styles or templates (e.g., their font sizes are unaffected) The other obvious limitation is that the Canvas will not attempt to keep elements visible when the end user resizes the window to a smaller surface Perhaps the best use of the Canvas type is to position graphical content For example, if you were building a custom image using XAML, you certainly would want the lines, shapes, and text to remain in the same location, rather than having them dynamically repositioned as the user resizes the window! You’ll revisit the Canvas in the next chapter when we examine WPF’s graphical rendering services Positioning Content Within WrapPanel Panels ee -e bo o ks - A WrapPanel allows you to define content that will flow across the panel as the window is resized When positioning elements in a WrapPanel, you not specify top, bottom, left, and right docking values as you typically with the Canvas However, each subelement is free to define a Height and Width value (among other property values) to control its overall size in the container Because content within a WrapPanel does not “dock” to a given side of the panel, the order in which you declare the elements is critical (content is rendered from the first element to the last) Consider the following XAML snippet: w w w fr Enter Car Information Make Color Pet Name OK When you view this markup, the content will look out of sorts as you resize the width, as it is flowing left to right across the window (see Figure 29-18) Figure 29-18 Content in a WrapPanel behaves much like a vanilla-flavored HTML page 1087 8849CH29.qxd 10/16/07 12:17 PM Page 1088 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS By default, content within a WrapPanel flows left to right However, if you change the value of the Orientation property to Vertical, you can have content wrap in a top-to-bottom manner: A WrapPanel (as well as some other panel types) may be declared by specifying ItemWidth and ItemHeight values, which control the default size of each item If a subelement does provide its own Height and/or Width value, it will be positioned relative to the size established by the panel Consider the following markup: nl oa d o rg Enter Car Information Make Color Pet Name OK ee -e bo o ks - w When rendered, we find the output shown in Figure 29-19 (notice the size and position of the Button widget) w fr Figure 29-19 A WrapPanel can establish the width and height of a given item w As you might agree after looking at Figure 29-19, a WrapPanel is not typically the best choice for arranging content directly in a window, as the elements can become scrambled as the user resizes the window In most cases, a WrapPanel will be a subelement to another panel type, to allow a small area of the window to wrap its content when resized w 1088 s Source Code The SimpleWrapPanel.xaml file can be found under the Chapter 29 subdirectory Positioning Content Within StackPanel Panels Like a WrapPanel, a StackPanel control arranges content into a single line that can be oriented horizontally or vertically (the default), based on the value assigned to the Orientation property The difference, however, is that the StackPanel will not attempt to wrap the content as the user resizes the window Rather, the items in the StackPanel will simply stretch (based on their orientation) to accommodate the size of the StackPanel itself For example, the following markup results in the output shown in Figure 29-20: 8849CH29.qxd 10/16/07 12:17 PM Page 1089 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS ee -e bo o Figure 29-20 Vertical stacking of content ks - w nl oa d o rg Enter Car Information Make Color Pet Name OK If we assign the Orientation property to Horizontal as follows, the rendered output will match that of Figure 29-21: w w w fr Figure 29-21 Horizontal stacking of content Again, like the WrapPanel, you will seldom want to use a StackPanel to directly arrange content within a window Rather, a StackPanel is better suited as a subpanel to a master panel s Source Code The SimpleStackPanel.xaml file can be found under the Chapter 29 subdirectory 1089 8849CH29.qxd 10/16/07 12:17 PM Page 1090 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 29 s PROGRAMMING WITH WPF CONTROLS Positioning Content Within Grid Panels Of all the panels provided with the WPF APIs, Grid is far and away the most flexible Like an HTML table, the Grid can be carved up into a set of cells, each one of which provides content When defining a Grid, you perform three steps: Define and configure each column Define and configure each row Assign content to each cell of the grid using attached property syntax s Note oa d o rg If you not define any rows or columns, the defaults to a single cell that fills the entire surface of the window Furthermore, if you not assign a cell value for a subelement within a , it automatically attaches to column 0, row w nl The first two steps (defining the columns and rows) are achieved by using the and elements, which contain a collection of and elements, respectively Because each cell within a grid is indeed a true NET type, you can configure the look and feel and behavior of each item as you see fit Here is a rather simple definition that arranges our UI content as shown in Figure 29-22: w fr ee -e bo o ks - w