{"id":350,"date":"2023-10-20T13:46:16","date_gmt":"2023-10-20T05:46:16","guid":{"rendered":"https:\/\/miie.net\/?p=350"},"modified":"2023-10-20T13:46:16","modified_gmt":"2023-10-20T05:46:16","slug":"pro-c10-chapter-26-wpf-controls-layouts-events-and-data-binding","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=350","title":{"rendered":"Pro C#10 CHAPTER 26 WPF Controls, Layouts, Events, and Data Binding"},"content":{"rendered":"<p>CHAPTER 26<\/p>\n<p>WPF Controls, Layouts, Events, and Data Binding<\/p>\n<p>Chapter 25 provided a foundation for the WPF programming model, including an examination of the Window and Application classes, the grammar of XAML, and the use of code files. Chapter 25 also introduced you to the process of building WPF applications using the designers of Visual Studio. In this chapter, you will dig into the construction of more sophisticated graphical user interfaces using several new controls and layout managers, learning about additional features of the WPF Visual Designer for XAML of Visual Studio along the way.<br \/>\nThis chapter will also examine some important related WPF control topics such as the data-binding programming model and the use of control commands. You will also learn how to use the Ink and Documents APIs, which allow you to capture stylus (or mouse) input and build rich text documents using the XML Paper Specification, respectively.<\/p>\n<p>A Survey of the Core WPF Controls<br \/>\nUnless you are new to the concept of building graphical user interfaces (which is fine), the general purpose of the major WPF controls should not raise too many issues. Regardless of which GUI toolkit you might have used in the past (e.g., VB6, MFC, Java AWT\/Swing, Windows Forms, macOS, or GTK+\/GTK# [among others]), the core WPF controls listed in Table 26-1 are likely to look familiar.<\/p>\n<p>Table 26-1. The Core WPF Controls<\/p>\n<p>WPF Control<br \/>\nCategory    Example Members Meaning in Life<br \/>\nCore user input controls    Button, RadioButton, ComboBox, CheckBox, Calendar, DatePicker, Expander, DataGrid, ListBox, ListView, ToggleButton, TreeView, ContextMenu, ScrollBar, Slider, TabControl, TextBlock, TextBox, RepeatButton, RichTextBox, Label  WPF provides an entire family of controls you can use to build the crux of a user interface.<br \/>\nWindow and control adornments   Menu, ToolBar, StatusBar, ToolTip, ProgressBar  You use these UI elements to decorate the frame of a Window object with input devices (such as the Menu) and user informational elements (e.g., StatusBar and ToolTip).<br \/>\n(continued)<\/p>\n<p>\u00a9 Andrew Troelsen, Phil Japikse 2022<br \/>\nA. Troelsen and P. Japikse, Pro C# 10 with .NET 6, <a href=\"https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_26\"><a href=\"https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_26\"><a href=\"https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_26\">https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_26<\/a><\/a><\/a><\/p>\n<p>1137<\/p>\n<p>Table 26-1. (continued)<\/p>\n<p>WPF Control<br \/>\nCategory    Example Members Meaning in Life<br \/>\nMedia controls  Image, MediaElement, SoundPlayerAction  These controls provide support for audio\/video playback and image display.<br \/>\nLayout controls Border, Canvas, DockPanel, Grid, GridView, GridSplitter, GroupBox, Panel, TabControl, StackPanel, Viewbox, WrapPanel    WPF provides numerous controls that allow you to group and organize other controls for the purpose of layout management.<\/p>\n<p>\u25a0Note The intent of this chapter is not to walk through each and every member of each and every WPF control. Rather, you will receive an overview of the various controls with an emphasis on the underlying programming model and key services common to most WPF controls.<\/p>\n<p>The WPF Ink Controls<br \/>\nIn addition to the common WPF controls listed in Table 26-1, WPF defines additional controls for working with the digital Ink API. This aspect of WPF development is useful during tablet PC development because it lets you capture input from the stylus. However, this is not to say a standard desktop application cannot leverage the Ink API because the same controls can capture input using the mouse.<br \/>\nThe System.Windows.Ink namespace of PresentationCore.dll contains various Ink API support types (e.g., Stroke and StrokeCollection); however, a majority of the Ink API controls (e.g., InkCanvas and InkPresenter) are packaged up with the common WPF controls under the System.Windows.Controls namespace in the PresentationFramework.dll assembly. You\u2019ll work with the Ink API later in this chapter.<\/p>\n<p>The WPF Document Controls<br \/>\nWPF also provides controls for advanced document processing, allowing you to build applications that incorporate Adobe PDF\u2013style functionality. Using the types within the System.Windows.Documents namespace (also in the PresentationFramework.dll assembly), you can create print-ready documents that support zooming, searching, user annotations (sticky notes), and other rich text services.<br \/>\nUnder the covers, however, the document controls do not use Adobe PDF APIs; rather, they use the XML Paper Specification (XPS) API. To the end user, there will really appear to be no difference because PDF documents and XPS documents have an almost identical look and feel. In fact, you can find many free utilities that allow you to convert between the two file formats on the fly. Because of space limitation, these controls won\u2019t be covered in this edition.<\/p>\n<p>WPF Common Dialog Boxes<br \/>\nWPF also provides you with a few common dialog boxes such as OpenFileDialog and SaveFileDialog. These dialog boxes are defined within the Microsoft.Win32 namespace of the PresentationFramework. dll assembly. Working with either of these dialog boxes is a matter of creating an object and invoking the ShowDialog() method, like so:<\/p>\n<p>using Microsoft.Win32;<br \/>\n\/\/omitted for brevity<br \/>\nprivate void btnShowDlg_Click(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Show a file save dialog.<br \/>\nSaveFileDialog saveDlg = new SaveFileDialog(); saveDlg.ShowDialog();<br \/>\n}<\/p>\n<p>As you would hope, these classes support various members that allow you to establish file filters and directory paths and gain access to user-selected files. You will put these file dialogs to use in later examples; you will also learn how to build custom dialog boxes to gather user input.<\/p>\n<p>A Brief Review of the Visual Studio WPF Designer<br \/>\nA majority of these standard WPF controls have been packaged up in the System.Windows.Controls namespace of the PresentationFramework.dll assembly. When you build a WPF application using Visual Studio, you will find most of these common controls contained in the toolbox, provided you have a WPF designer open as the active window.<br \/>\nSimilar to other UI frameworks created with Visual Studio, you can drag these controls onto the WPF window designer and configure them using the Properties window (which you learned about in Chapter 25). While Visual Studio will generate a good amount of the XAML on your behalf, it is not uncommon to edit the markup yourself manually. Let\u2019s review the basics.<\/p>\n<p>Working with WPF Controls Using Visual Studio<br \/>\nYou might recall from Chapter 25 that when you place a WPF control onto the Visual Studio designer, you want to set the x:Name property through the Properties window (or through XAML directly) because this allows you to access the object in your related C# code file. You might also recall that you can use the Events tab of the Properties window to generate event handlers for a selected control. Thus, you could use Visual Studio to generate the following markup for a simple Button control:<\/p>\n<p><Button x:Name=\"btnMyButton\" Content=\"Click Me!\" Height=\"23\" Width=\"140\" Click=\"btnMyButton_ Click\" \/><\/p>\n<p>Here, you set the Content property of the Button to a simple string with the value &quot;Click Me!&quot;.<br \/>\nHowever, thanks to the WPF control content model, you could fashion a Button that contains the following complex content:<\/p>\n<p><Button x:Name=\"btnMyButton\" Height=\"121\" Width=\"156\" Click=\"btnMyButton_Click\"><br \/>\n<Button.Content><br \/>\n<StackPanel Height=\"95\" Width=\"128\" Orientation=\"Vertical\"><br \/>\n<Ellipse Fill=\"Red\" Width=\"52\" Height=\"45\" Margin=\"5\"\/><br \/>\n<Label Width=\"59\" FontSize=\"20\" Content=\"Click!\" Height=\"36\" \/><br \/>\n<\/StackPanel><br \/>\n<\/Button.Content><br \/>\n<\/Button><\/p>\n<p>You might also recall that the immediate child element of a ContentControl-derived class is the implied content; therefore, you do not need to define a Button.Content scope explicitly when specifying complex content. You could simply author the following:<\/p>\n<p><Button x:Name=\"btnMyButton\" Height=\"121\" Width=\"156\" Click=\"btnMyButton_Click\"><br \/>\n<StackPanel Height=\"95\" Width=\"128\" Orientation=\"Vertical\"><br \/>\n<Ellipse Fill=\"Red\" Width=\"52\" Height=\"45\" Margin=\"5\"\/><br \/>\n<Label Width=\"59\" FontSize=\"20\" Content=\"Click!\" Height=\"36\" \/><br \/>\n<\/StackPanel><br \/>\n<\/Button><\/p>\n<p>In either case, you set the button\u2019s Content property to a StackPanel of related items. You can also author this sort of complex content using the Visual Studio designer. After you define the layout manager for a content control, you can select it on the designer to serve as a drop target for the internal controls. At this point, you can edit each using the Properties window. If you were to use the Properties window to handle the Click event for the Button control (as shown in the previous XAML declarations), the IDE would generate an empty event handler, to which you could add your own custom code, like so:<\/p>\n<p>private void btnMyButton_Click(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\nMessageBox.Show(&quot;You clicked the button!&quot;);<br \/>\n}<\/p>\n<p>Working with the Document Outline Editor<br \/>\nYou should recall from the previous chapter that the Document Outline window of Visual Studio (which you can open using the View \u27a4 Other Windows menu) is useful when designing a WPF control that has complex content. The logical tree of XAML is displayed for the Window you are building, and if you click any of these nodes, it is automatically selected in the visual designer and the XAML editor for editing.<br \/>\nWith the current edition of Visual Studio, the Document Outline window has a few additional features that you might find useful. To the right of any node you will find an icon that looks similar to an eyeball.<br \/>\nWhen you toggle this button, you can opt to hide or show an item on the designer, which can be helpful when you want to focus in on a particular segment to edit (note that this will not hide the item at runtime; this only hides items on the designer surface).<br \/>\nRight next to the \u201ceyeball icon\u201d is a second toggle that allows you to lock an item on the designer. As you might guess, this can be helpful when you want to make sure you (or your co-workers) do not accidentally change the XAML for a given item. In effect, locking an item makes it read-only at design time (however, you can change the object\u2019s state at runtime).<\/p>\n<p>Controlling Content Layout Using Panels<br \/>\nA WPF application invariably contains a good number of UI elements (e.g., user input controls, graphical content, menu systems, and status bars) that need to be well organized within various windows. After you place the UI elements, you need 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<br \/>\ncontrols retain their position within the hosting window, you can take advantage of a good number of panel types (also known as layout managers).<br \/>\nBy default, a new WPF Window created with Visual Studio will use a layout manager of type Grid (more details in just a bit). However, for now, assume a Window with no declared layout manager, like so:<\/p>\n<p>&lt;Window x:Class=&quot;MyWPFApp.MainWindow&quot; xmlns=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation<\/a><\/a><\/a>&quot; xmlns:x=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml<\/a><\/a><\/a>&quot;<br \/>\nTitle=&quot;Fun with Panels!&quot; Height=&quot;285&quot; Width=&quot;325&quot;&gt;<\/p>\n<p><\/Window><\/p>\n<p>When you declare a control directly inside a window that doesn\u2019t use panels, the control is positioned dead center in the container. Consider the following simple window declaration, which contains a single Button control. Regardless of how you resize the window, the UI widget is always equidistant from all four sides of the client area. The Button\u2019s size is determined by the assigned Height and Width properties of the Button.<\/p>\n<p>&lt;!- This button is in the center of the window at all times -&gt;<br \/>\n&lt;Window x:Class=&quot;MyWPFApp.MainWindow&quot; xmlns=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation<\/a><\/a><\/a>&quot; xmlns:x=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml<\/a><\/a><\/a>&quot;<br \/>\nTitle=&quot;Fun with Panels!&quot; Height=&quot;285&quot; Width=&quot;325&quot;&gt;<\/p>\n<p><Button x:Name=\"btnOK\" Height = \"100\" Width=\"80\" Content=\"OK\"\/><\/p>\n<p><\/Window><\/p>\n<p>You might also recall that if you attempt to place multiple elements directly within the scope of a Window, you will receive markup and 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. Therefore, the following XAML yields markup and compile-time errors:<\/p>\n<p>&lt;!- Error! Content property is implicitly set more than once! -&gt;<br \/>\n&lt;Window x:Class=&quot;MyWPFApp.MainWindow&quot; xmlns=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation<\/a><\/a><\/a>&quot; xmlns:x=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml<\/a><\/a><\/a>&quot;<br \/>\nTitle=&quot;Fun with Panels!&quot; Height=&quot;285&quot; Width=&quot;325&quot;&gt;<br \/>\n&lt;!- Error! Two direct child elements of the <Window>! -&gt;<\/p>\n<p><Label x:Name=\"lblInstructions\" Width=\"328\" Height=\"27\" FontSize=\"15\" Content=\"Enter Information\"\/><br \/>\n<Button x:Name=\"btnOK\" Height = \"100\" Width=\"80\" Content=\"OK\"\/><\/p>\n<p><\/Window><\/p>\n<p>Obviously, a window that can contain only a single control is of little use. When a window needs to contain multiple elements, those elements 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 single object assigned to the Content property.<br \/>\nThe System.Windows.Controls namespace provides numerous panels, each of which controls how<br \/>\nsubelements are maintained. You can use panels to establish how the controls behave if the end user resizes the window, if the controls remain exactly where they were placed at design time, if the controls reflow horizontally from left to right or vertically from top to bottom, and so forth.<br \/>\nYou can also intermix panel controls within other panels (e.g., a DockPanel that contains a StackPanel of other items) to provide a great deal of flexibility and control. Table 26-2 documents the role of some commonly used WPF panel controls.<\/p>\n<p>Table 26-2. Core WPF Panel Controls<\/p>\n<p>Panel<br \/>\nControl Meaning in Life<br \/>\nCanvas  Provides a classic mode of content placement. Items stay exactly where you put them at design time.<br \/>\nDockPanel   Locks content to a specified side of the panel (Top, Bottom, Left, or Right).<br \/>\nGrid    Arranges content within a series of cells, maintained within a tabular grid.<br \/>\nStackPanel  Stacks content in a vertical or horizontal manner, as dictated by the Orientation property.<br \/>\nWrapPanel   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.<\/p>\n<p>In the next few sections, you will learn how to use these commonly used panel types by copying some predefined XAML data into the kaxaml.exe application you installed in Chapter 25. You can find all these loose XAML files contained inside the PanelMarkup subfolder of your Chapter 26 code download folder.<br \/>\nWhen working with Kaxaml, to simulate resizing a window, change the height or width of the Page element in the markup.<\/p>\n<p>Positioning Content Within Canvas Panels<br \/>\nIf you come from a WinForms background, you will probably feel most at home with the Canvas panel because it 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.<br \/>\nTo add content to a Canvas, you begin by defining the required controls within the scope of the opening and closing Canvas tags. Next, specify the upper-left corner for each control; this is where the rendering should begin using the Canvas.Top and Canvas.Left properties. You can specify the bottom-right area indirectly in each control by setting its Height and Width properties or directly by using the Canvas.Right and Canvas.Bottom properties.<br \/>\nTo see Canvas in action, open the provided SimpleCanvas.xaml file using kaxaml.exe. You should see the following Canvas definition (if loading these examples into a WPF application, you will want to change the Page tag to a Window tag):<\/p>\n<p>&lt;Page<br \/>\nxmlns=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation<\/a><\/a><\/a>&quot; xmlns:x=&quot;<a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><a href=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\">http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml<\/a><\/a><\/a>&quot;<br \/>\nTitle=&quot;Fun with Panels!&quot; Height=&quot;285&quot; Width=&quot;325&quot;&gt;<\/p>\n<p><Canvas Background=\"LightSteelBlue\"><br \/>\n<Button x:Name=\"btnOK\" Canvas.Left=\"212\" Canvas.Top=\"203\" Width=\"80\" Content=\"OK\"\/><br \/>\n<Label x:Name=\"lblInstructions\" Canvas.Left=\"17\" Canvas.Top=\"14\" Width=\"328\" Height=\"27\" FontSize=\"15\"\nContent=\"Enter Car Information\"\/><br \/>\n<Label x:Name=\"lblMake\" Canvas.Left=\"17\" Canvas.Top=\"60\" Content=\"Make\"\/><br \/>\n<TextBox x:Name=\"txtMake\" Canvas.Left=\"94\" Canvas.Top=\"60\" Width=\"193\" Height=\"25\"\/><br \/>\n<Label x:Name=\"lblColor\" Canvas.Left=\"17\" Canvas.Top=\"109\" Content=\"Color\"\/><br \/>\n<TextBox x:Name=\"txtColor\" Canvas.Left=\"94\" Canvas.Top=\"107\" Width=\"193\" Height=\"25\"\/><\/p>\n<p><Label x:Name=\"lblPetName\" Canvas.Left=\"17\" Canvas.Top=\"155\" Content=\"Pet Name\"\/><br \/>\n<TextBox x:Name=\"txtPetName\" Canvas.Left=\"94\" Canvas.Top=\"153\" Width=\"193\" Height=\"25\"\/><br \/>\n<\/Canvas><\/p>\n<p><\/Page><\/p>\n<p>You should see the window shown in Figure 26-1 in the top half of the screen.<\/p>\n<p>Figure 26-1. The Canvas layout manager allows for absolute positioning of content<\/p>\n<p>Note that the order you declare content within a Canvas is not used to calculate placement; instead, placement is based on the control\u2019s size and the Canvas.Top, Canvas.Bottom, Canvas.Left, and Canvas. Right properties.<\/p>\n<p>\u25a0Note  if subelements within a Canvas do not define a specific location using attached property syntax (e.g.,<br \/>\nCanvas.Left and Canvas.Top), they automatically attach to the extreme upper-left corner of Canvas.<\/p>\n<p>Using the Canvas type might seem like the preferred way to arrange content (because it feels so familiar), but this approach does suffer from some limitations. First, items within a Canvas do not dynamically resize themselves when applying styles or templates (e.g., their font sizes are unaffected). Second, the Canvas will not attempt to keep elements visible when the end user resizes the window to a smaller surface.<br \/>\nPerhaps the best use of the Canvas type is for positioning 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 see them dynamically repositioned as the user resizes the window! You\u2019ll revisit Canvas in Chapter 27 when you examine WPF\u2019s graphical rendering services.<\/p>\n<p>Positioning Content Within WrapPanel Panels<br \/>\nA WrapPanel allows you to define content that will flow across the panel as the window is resized. When positioning elements in a WrapPanel, you do not specify top, bottom, left, and right docking values as you typically do with 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.<\/p>\n<p>Because content within a WrapPanel does not dock to a given side of the panel, the order in which you declare the elements is important (content is rendered from the first element to the last). If you were to load the XAML data found within the SimpleWrapPanel.xaml file, you would find it contains the following markup (enclosed within a Page definition):<\/p>\n<p><WrapPanel Background=\"LightSteelBlue\"><br \/>\n<Label x:Name=\"lblInstruction\" Width=\"328\" Height=\"27\" FontSize=\"15\" Content=\"Enter Car Information\"\/><br \/>\n<Label x:Name=\"lblMake\" Content=\"Make\"\/><br \/>\n<TextBox x:Name=\"txtMake\" Width=\"193\" Height=\"25\"\/><br \/>\n<Label x:Name=\"lblColor\" Content=\"Color\"\/><br \/>\n<TextBox x:Name=\"txtColor\" Width=\"193\" Height=\"25\"\/><br \/>\n<Label x:Name=\"lblPetName\" Content=\"Pet Name\"\/><br \/>\n<TextBox x:Name=\"txtPetName\" Width=\"193\" Height=\"25\"\/><br \/>\n<Button x:Name=\"btnOK\" Width=\"80\" Content=\"OK\"\/><br \/>\n<\/WrapPanel><\/p>\n<p>When you load this markup, the content looks out of sorts because it flows from left to right across the window (see Figure 26-2).<\/p>\n<p>Figure 26-2. Content in a WrapPanel behaves much like a traditional HTML page<\/p>\n<p>By default, content within a WrapPanel flows from left to right. However, if you change the value of the<br \/>\nOrientation property to Vertical, you can have content wrap in a top-to-bottom manner.<\/p>\n<p><WrapPanel Background=\"LightSteelBlue\" Orientation =\"Vertical\"><\/p>\n<p>You can declare a WrapPanel (as well as some other panel types) 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:<\/p>\n<p><Page\nxmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\nTitle=\"Fun with Panels!\" Height=\"100\" Width=\"650\"><br \/>\n<WrapPanel Background=\"LightSteelBlue\" Orientation =\"Horizontal\" ItemWidth =\"200\" ItemHeight =\"30\"><br \/>\n<Label x:Name=\"lblInstruction\" FontSize=\"15\" Content=\"Enter Car Information\"\/><br \/>\n<Label x:Name=\"lblMake\" Content=\"Make\"\/><br \/>\n<TextBox x:Name=\"txtMake\"\/><br \/>\n<Label x:Name=\"lblColor\" Content=\"Color\"\/><\/p>\n<p><TextBox x:Name=\"txtColor\"\/><br \/>\n<Label x:Name=\"lblPetName\" Content=\"Pet Name\"\/><br \/>\n<TextBox x:Name=\"txtPetName\"\/><br \/>\n<Button x:Name=\"btnOK\" Width =\"80\" Content=\"OK\"\/><br \/>\n<\/WrapPanel><br \/>\n<\/Page><\/p>\n<p>The rendered code looks like Figure 26-3 (notice the size and position of the Button control, which has a specified unique Width value).<\/p>\n<p>Figure 26-3. A WrapPanel can establish the width and height of a given item<\/p>\n<p>As you might agree after looking at Figure 26-3, a WrapPanel is not typically the best choice for arranging content directly in a window because its elements can become scrambled as the user resizes the window. In most cases, a WrapPanel will be a subelement to another panel type, allowing a small area of the window to wrap its content when resized (e.g., a ToolBar control).<\/p>\n<p>Positioning Content Within StackPanel Panels<br \/>\nLike 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<br \/>\nitems in the StackPanel will simply stretch (based on their orientation) to accommodate the size of the StackPanel itself. For example, the SimpleStackPanel.xaml file contains the following markup, which results in the output shown in Figure 26-4:<\/p>\n<p><Page\nxmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\nTitle=\"Fun with Panels!\" Height=\"200\" Width=\"400\"><br \/>\n<StackPanel Background=\"LightSteelBlue\" Orientation =\"Vertical\"><br \/>\n<Label Name=\"lblInstruction\"\nFontSize=\"15\" Content=\"Enter Car Information\"\/><br \/>\n<Label Name=\"lblMake\" Content=\"Make\"\/><br \/>\n<TextBox Name=\"txtMake\"\/><br \/>\n<Label Name=\"lblColor\" Content=\"Color\"\/><br \/>\n<TextBox Name=\"txtColor\"\/><br \/>\n<Label Name=\"lblPetName\" Content=\"Pet Name\"\/><br \/>\n<TextBox Name=\"txtPetName\"\/><br \/>\n<Button Name=\"btnOK\" Width =\"80\" Content=\"OK\"\/><br \/>\n<\/StackPanel><br \/>\n<\/Page><\/p>\n<p>Figure 26-4. Vertical stacking of content<\/p>\n<p>If you assign the Orientation property to Horizontal as follows, the rendered output will match that shown in Figure 26-5:<\/p>\n<p><StackPanel Background=\"LightSteelBlue\" Orientation=\"Horizontal\"><\/p>\n<p>Figure 26-5. Horizontal stacking of content<\/p>\n<p>Again, as is the case with the WrapPanel, you will seldom want to use a StackPanel to arrange content directly within a window. Instead, you should use StackPanel as a subpanel to a master panel.<\/p>\n<p>Positioning Content Within Grid Panels<br \/>\nOf 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 these three steps:<br \/>\n1.Define and configure each column.<br \/>\n2.Define and configure each row.<br \/>\n3.Assign content to each cell of the grid using attached property syntax.<\/p>\n<p>\u25a0Note  if you do not define any rows or columns, the Grid defaults to a single cell that fills the entire surface of the window. Furthermore, if you do not assign a cell value (column and row) for a subelement within a Grid, it automatically attaches to column 0, row 0.<\/p>\n<p>You achieve the first two steps (defining the columns and rows) by using the Grid.ColumnDefinitions and Grid.RowDefinitions elements, which contain a collection of ColumnDefinition and RowDefinition elements, respectively. Each cell within a grid is indeed a true .NET object, so you can configure the look and feel and behavior of each cell as you see fit.<br \/>\nHere is a Grid definition (that you can find in the SimpleGrid.xaml file) that arranges your UI content, as shown in Figure 26-6:<\/p>\n<p><Grid ShowGridLines =\"True\" Background =\"LightSteelBlue\"><br \/>\n<!-- Define the rows\/columns --><br \/>\n<Grid.ColumnDefinitions><br \/>\n<ColumnDefinition\/><br \/>\n<ColumnDefinition\/><br \/>\n<\/Grid.ColumnDefinitions><br \/>\n<Grid.RowDefinitions><br \/>\n<RowDefinition\/><br \/>\n<RowDefinition\/><br \/>\n<\/Grid.RowDefinitions><\/p>\n<p><!-- Now add the elements to the grid's cells --><br \/>\n<Label x:Name=\"lblInstruction\" Grid.Column =\"0\" Grid.Row =\"0\" FontSize=\"15\" Content=\"Enter Car Information\"\/><br \/>\n<Button x:Name=\"btnOK\" Height =\"30\" Grid.Column =\"0\"\nGrid.Row =\"0\" Content=\"OK\"\/><br \/>\n<Label x:Name=\"lblMake\" Grid.Column =\"1\" Grid.Row =\"0\" Content=\"Make\"\/><br \/>\n<TextBox x:Name=\"txtMake\" Grid.Column =\"1\" Grid.Row =\"0\" Width=\"193\" Height=\"25\"\/><br \/>\n<Label x:Name=\"lblColor\" Grid.Column =\"0\" Grid.Row =\"1\" Content=\"Color\"\/><br \/>\n<TextBox x:Name=\"txtColor\" Width=\"193\" Height=\"25\" Grid.Column =\"0\" Grid.Row =\"1\" \/><\/p>\n<p><!-- Just to keep things interesting, add some color to the pet name cell --><br \/>\n<Rectangle Fill =\"LightGreen\" Grid.Column =\"1\" Grid.Row =\"1\" \/><br \/>\n<Label x:Name=\"lblPetName\" Grid.Column =\"1\" Grid.Row =\"1\" Content=\"Pet Name\"\/><br \/>\n<TextBox x:Name=\"txtPetName\" Grid.Column =\"1\" Grid.Row =\"1\" Width=\"193\" Height=\"25\"\/><br \/>\n<\/Grid><\/p>\n<p>Figure 26-6. The Grid panel in action<\/p>\n<p>Notice that each element (including a light green Rectangle element thrown in for good measure) connects itself to a cell in the grid using the Grid.Row and Grid.Column attached properties. By default, the ordering of cells in a grid begins at the upper left, which you specify using Grid.Column=\"0\" Grid.Row=\"0\". Given that your grid defines a total of four cells, you can identify the bottom-right cell using Grid.Column=\"1\" Grid.Row=\"1\".<\/p>\n<p>Sizing Grid Columns and Rows<br \/>\nColumns and rows in a grid can be sized in one of three ways.<br \/>\n\u2022Absolute sizing (e.g., 100)<br \/>\n\u2022Autosizing<br \/>\n\u2022Relative sizing (e.g., 3x)<br \/>\nAbsolute sizing is exactly what you would expect; the column (or row) is sized to a specific number of device-independent units. Autosizing sizes each column or row based on the controls contained with the column or row. Relative sizing is pretty much equivalent to percentage sizing in CSS. The total count of the numbers in relatively sized columns or rows gets divided into the total amount of available space.<br \/>\nIn the following example, the first row gets 25 percent of the space, and the second row gets 75 percent of the space:<\/p>\n<p><Grid.ColumnDefinitions><br \/>\n<ColumnDefinition Width=\"1*\" \/><br \/>\n<ColumnDefinition Width=\"3*\" \/><br \/>\n<\/Grid.ColumnDefinitions><\/p>\n<p>Grids with GridSplitter Types<br \/>\nGrid objects can also support splitters. As you might know, splitters allow the end user to resize rows or columns of a grid type. As this is done, the content within each resizable cell will reshape itself based on how the items have been contained. Adding splitters to a Grid is easy to do; you simply define the GridSplitter control, using attached property syntax to establish which row or column it affects.<br \/>\nBe aware that you must assign a Width or Height value (depending on vertical or horizontal splitting) for the splitter to be visible on the screen. Consider the following simple Grid type with a splitter on the first column (Grid.Column = \"0\"). The contents of the provided GridWithSplitter.xaml file look like this:<\/p>\n<p><Grid Background =\"LightSteelBlue\"><br \/>\n<!-- Define columns --><br \/>\n<Grid.ColumnDefinitions><br \/>\n<ColumnDefinition Width =\"Auto\"\/><br \/>\n<ColumnDefinition\/><br \/>\n<\/Grid.ColumnDefinitions><\/p>\n<p><!-- Add this label to cell 0 --><br \/>\n<Label x:Name=\"lblLeft\" Background =\"GreenYellow\" Grid.Column=\"0\" Content =\"Left!\"\/><\/p>\n<p><!-- Define the splitter --><br \/>\n<GridSplitter Grid.Column =\"0\" Width =\"5\"\/><\/p>\n<p><!-- Add this label to cell 1 --><br \/>\n<Label x:Name=\"lblRight\" Grid.Column =\"1\" Content =\"Right!\"\/><br \/>\n<\/Grid><\/p>\n<p>First, notice that the column that will support the splitter has a Width property of Auto. Next, notice that the GridSplitter uses attached property syntax to establish which column it is working with. If you were<br \/>\nto view this output, you would find a five-pixel splitter that allows you to resize each Label. Note that the content fills up the entire cell because you have not specified Height or Width properties for either Label (see Figure 26-7).<\/p>\n<p>Figure 26-7. Grid types containing splitters<\/p>\n<p>Positioning Content Within DockPanel Panels<br \/>\nDockPanel is typically used as a container that holds any number of additional panels for grouping related content. DockPanels use attached property syntax (as shown with the Canvas or Grid types) to control where each item docks itself within the DockPanel.<br \/>\nThe SimpleDockPanel.xaml file defines the following simple DockPanel definition that results in the output shown in Figure 26-8:<\/p>\n<p><DockPanel LastChildFill =\"True\" Background=\"AliceBlue\"><br \/>\n<!-- Dock items to the panel --><br \/>\n<Label DockPanel.Dock =\"Top\" Name=\"lblInstruction\" FontSize=\"15\" Content=\"Enter Car Information\"\/><br \/>\n<Label DockPanel.Dock =\"Left\" Name=\"lblMake\" Content=\"Make\"\/><\/p>\n<p><Label DockPanel.Dock =\"Right\" Name=\"lblColor\" Content=\"Color\"\/><br \/>\n<Label DockPanel.Dock =\"Bottom\" Name=\"lblPetName\" Content=\"Pet Name\"\/><br \/>\n<Button Name=\"btnOK\" Content=\"OK\"\/><br \/>\n<\/DockPanel><\/p>\n<p>Figure 26-8. A simple DockPanel<\/p>\n<p>\u25a0Note  if you add multiple elements to the same side of a DockPanel, they will stack along the specified edge in the order they are declared.<\/p>\n<p>The benefit of using DockPanel types is that, as the user resizes the window, each element remains connected to the specified side of the panel (through DockPanel.Dock). Also notice that the opening DockPanel tag in this example sets the LastChildFill attribute to true. Given that the Button control is indeed the \u201clast child\u201d in the container, it will therefore be stretched within the remaining space.<\/p>\n<p>Enabling Scrolling for Panel Types<br \/>\nIt is worth pointing out that WPF supplies a ScrollViewer class, which provides automatic scrolling behaviors for data within panel objects. The SimpleScrollViewer.xaml file defines the following:<\/p>\n<p><ScrollViewer><br \/>\n<StackPanel><br \/>\n<Button Content =\"First\" Background = \"Green\" Height =\"50\"\/><br \/>\n<Button Content =\"Second\" Background = \"Red\" Height =\"50\"\/><br \/>\n<Button Content =\"Third\" Background = \"Pink\" Height =\"50\"\/><br \/>\n<Button Content =\"Fourth\" Background = \"Yellow\" Height =\"50\"\/><br \/>\n<Button Content =\"Fifth\" Background = \"Blue\" Height =\"50\"\/><br \/>\n<\/StackPanel><br \/>\n<\/ScrollViewer><\/p>\n<p>You can see the result of the previous XAML definition in Figure 26-9 (notice the scrollbar on the right since the window isn\u2019t sized to show all five buttons).<\/p>\n<p>Figure 26-9. Working with the ScrollViewer type<\/p>\n<p>As you would expect, each panel provides numerous members that allow you to fine-tune content placement. On a related note, many WPF controls support two properties of interest (Padding and Margin) that allow the control itself to inform the panel how it wants to be treated. Specifically, the Padding property controls how much extra space should surround the interior control, while Margin controls the extra space around the exterior of a control.<br \/>\nThis wraps up this chapter\u2019s look at the major panel types of WPF, as well as the various ways they position their content. Next, you\u2019ll learn how to use the Visual Studio designers to create layouts.<\/p>\n<p>Configuring Panels Using the Visual Studio Designers<br \/>\nNow that you have been given a walk-through of the XAML used to define some common layout managers, you will be happy to know that Visual Studio has very good design-time support for constructing your layouts. The key to doing so lies with the Document Outline window described earlier in this chapter. To illustrate some of the basics, create a new WPF application project named VisualLayoutTester.<br \/>\nRecall that the WFP Application project template doesn\u2019t set the options for implicit using statements or nullable reference types. For each of the projects in this chapter, I\u2019ve enabled global implicit using statements and disabled nullable reference types at the project level.<\/p>\n<p><Project Sdk=\"Microsoft.NET.Sdk\"><br \/>\n<PropertyGroup><br \/>\n<OutputType>WinExe<\/OutputType><br \/>\n<TargetFramework>net6.0-windows<\/TargetFramework><br \/>\n<UseWPF>true<\/UseWPF><br \/>\n<Nullable>disable<\/Nullable><br \/>\n<ImplicitUsings>enable<\/ImplicitUsings><br \/>\n<\/PropertyGroup><br \/>\n<\/Project><\/p>\n<p>Notice how your initial Window makes use of a Grid layout by default, as shown here:<\/p>\n<p><Window x:Class=\"VisualLayoutTester.MainWindow\" xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\" xmlns:d=\"http:\/\/schemas.microsoft.com\/expression\/blend\/2008\" xmlns:local=\"clr-namespace:VisualLayoutTesterApp\"\n\nmc:Ignorable=\"d\"\nTitle=\"MainWindow\" Height=\"450\" Width=\"800\"><br \/>\n<Grid><br \/>\n<\/Grid><br \/>\n<\/Window><\/p>\n<p>If you are happy using the Grid layout system, notice in Figure 26-10 that you can easily carve out and resize the grid\u2019s cells using the visual layout. To do so, first select the Grid component in your Document Outline window and then click the grid\u2019s border to create new rows and columns.<\/p>\n<p>Figure 26-10. The Grid control can be visually cut into cells using the IDE\u2019s designer<\/p>\n<p>Now, let\u2019s say you have defined a grid with some number of cells. You can then drag and drop controls into a given cell of the layout system, and the IDE will automatically set the Grid.Row and Grid.Column properties of the control in question. Here is some possible markup generated by the IDE after dragging a Button into a predefined cell:<\/p>\n<p><Button x:Name=\"button\" Content=\"Button\" Grid.Column=\"0\" HorizontalAlignment=\"Left\" Margin=\"21,21.4,0,0\" Grid.Row=\"1\" VerticalAlignment=\"Top\" Width=\"75\"\/><\/p>\n<p>Now, let\u2019s say you would rather not use a Grid at all. If you right-click any layout node in the Document Outline window, you will find a menu option that allows you to change the current container into another (see Figure 26-11). Be aware that when you do so, you will (most likely) radically change the positioning of the controls because the controls will conform to the rules of the new panel type.<\/p>\n<p>Figure 26-11. The Document Outline window allows you to convert to new panel types<\/p>\n<p>Another handy trick is the ability to select a set of controls on the visual designer and group them into a new, nested layout manager. Assume you have a Grid that contains a set of random objects. Now, select a set of items on the designer by holding down the Ctrl key and clicking each item with the left mouse button. If you then right-click the selection, you can group the selected items into a new subpanel (see Figure 26-12).<\/p>\n<p>Figure 26-12. Grouping items into a new subpanel<\/p>\n<p>After you have done so, examine the Document Outline window once again to verify the nested layout system. As you build full-featured WPF windows, you will most likely always need to make use of a nested layout system, rather than simply picking a single panel for all of the UI display (in fact, the remaining WPF examples in the text will typically do so). On a final note, the nodes in the Document Outline window are all drag and droppable. For example, if you wanted to move a control currently in the DockPanel into the parent panel, you could do so as suggested in Figure 26-13.<\/p>\n<p>Figure 26-13. Relocating items via the Document Outline window<\/p>\n<p>As you work through the remaining WPF chapters, I\u2019ll point out additional layout shortcuts where possible. However, it\u2019s definitely worth your time to experiment and test various features yourself. To keep you moving in the right direction, the next example in the chapter will illustrate how to build a nested layout manager for a custom text processing application (with spell-checking!).<\/p>\n<p>Building a Window\u2019s Frame Using Nested Panels<br \/>\nAs mentioned, a typical WPF window will not use a single panel control but instead will nest panels within other panels to gain the desired layout system. Begin by creating a new WPF application named MyWordPad and enabling implicit using statements and disabling nullable reference types:<\/p>\n<p><Project Sdk=\"Microsoft.NET.Sdk\"><br \/>\n<PropertyGroup><br \/>\n<OutputType>WinExe<\/OutputType><br \/>\n<TargetFramework>net6.0-windows<\/TargetFramework><br \/>\n<UseWPF>true<\/UseWPF><br \/>\n<Nullable>disable<\/Nullable><br \/>\n<ImplicitUsings>enable<\/ImplicitUsings><br \/>\n<\/PropertyGroup><br \/>\n<\/Project><\/p>\n<p>Your goal is to construct a layout where the main window has a topmost menu system, a toolbar under the menu system, and a status bar mounted on the bottom of the window. The status bar will contain a pane to hold text prompts that are displayed when the user selects a menu item (or toolbar button), while the menu system and toolbar will offer UI triggers to close the application and display spelling suggestions in an Expander widget. Figure 26-14 shows the initial layout you are shooting for; it also shows the spell-checking capabilities within WPF.<\/p>\n<p>Figure 26-14. Using nested panels to establish a window\u2019s UI<\/p>\n<p>To begin building this UI, update the initial XAML definition for your Window type so it uses a DockPanel<br \/>\nchild element, rather than the default Grid, as follows:<\/p>\n<p><Window x:Class=\"MyWordPad.MainWindow\" xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\" xmlns:d=\"http:\/\/schemas.microsoft.com\/expression\/blend\/2008\"\n\nxmlns:local=\"clr-namespace:MyWordPad\" mc:Ignorable=\"d\"\nTitle=\"My Spell Checker\" Height=\"450\" Width=\"800\"><br \/>\n<!-- This panel establishes the content for the window --><br \/>\n<DockPanel><br \/>\n<\/DockPanel><br \/>\n<\/Window><\/p>\n<p>Building the Menu System<br \/>\nMenu systems in WPF are represented by the Menu class, which maintains a collection of MenuItem objects. When building a menu system in XAML, you can have each MenuItem handle various events. The most notable of these events is Click, which occurs when the end user selects a subitem. In this example, you begin by building the two topmost menu items (File and Tools; you will build the Edit menu later in this example), which expose Exit and Spelling Hints subitems, respectively.<br \/>\nIn addition to handling the Click event for each subitem, you also need to handle the MouseEnter and MouseExit events, which you will use to set the status bar text in a later step. Add the following markup within your DockPanel scope:<\/p>\n<p><!-- Dock menu system on the top --><br \/>\n<Menu DockPanel.Dock =\"Top\"\nHorizontalAlignment=\"Left\" Background=\"White\" BorderBrush =\"Black\"><br \/>\n<MenuItem Header=\"_File\"><br \/>\n<Separator\/><br \/>\n<MenuItem Header =\"_Exit\" MouseEnter =\"MouseEnterExitArea\" MouseLeave =\"MouseLeaveArea\" Click =\"FileExit_Click\"\/><br \/>\n<\/MenuItem><br \/>\n<MenuItem Header=\"_Tools\"><br \/>\n<MenuItem Header =\"_Spelling Hints\" MouseEnter =\"MouseEnterToolsHintsArea\"\nMouseLeave =\"MouseLeaveArea\" Click =\"ToolsSpellingHints_Click\"\/><br \/>\n<\/MenuItem><br \/>\n<\/Menu><\/p>\n<p>Notice that you dock the menu system to the top of the DockPanel. Also, you use the Separator element to insert a thin horizontal line in the menu system, directly before the Exit option. Also notice that the Header values for each MenuItem contain an embedded underscore token (e.g., _Exit). You use this token to establish which letter will be underlined when the end user presses the Alt key (for keyboard shortcuts). This is a change from the & character used in Windows Forms since XAML is based on XML, and the & character has meaning in XML.<br \/>\nSo far you\u2019ve implemented the complete menu system definition; next, you need to implement the various event handlers. First, you have the File Exit handler, FileExit_Click(), which simply closes the window, which in turn terminates the application because this is your topmost window. The MouseEnter<\/p>\n<p>and MouseExit event handlers for each subitem will eventually update your status bar; however, for now, you will simply provide shells. Finally, the ToolsSpellingHints_Click() handler for the Tools Spelling Hints menu item will also remain a shell for the time being. Here are the current updates to your code- behind file (including the updated using statements):<\/p>\n<p>using System.IO; using System.Windows;<br \/>\nusing System.Windows.Controls; using System.Windows.Input; using Microsoft.Win32;<\/p>\n<p>namespace MyWordPad;<\/p>\n<p>public partial class MainWindow : Window<br \/>\n{<br \/>\npublic MainWindow()<br \/>\n{<br \/>\nInitializeComponent();<br \/>\n}<\/p>\n<p>protected void FileExit_Click(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\n\/\/ Close this window.<br \/>\nThis.Close();<br \/>\n}<\/p>\n<p>protected void ToolsSpellingHints_Click(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\n}<br \/>\nprotected void MouseEnterExitArea(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\n}<br \/>\nprotected void MouseEnterToolsHintsArea(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\n}<br \/>\nprotected void MouseLeaveArea(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\n}<br \/>\n}<\/p>\n<p>Building Menus Visually<br \/>\nWhile it is always good to know how to manually define items in XAML, it can be a tad on the tedious side. Visual Studio supports visual design support for menu systems, toolbars, status bars, and many other UI controls. If you right-click the Menu control, you will notice an Add MenuItem option. As the name suggests, this adds a new menu item to the Menu control. After you have added a set of topmost items, you can then add submenu items and separators, expand or collapse the menu itself, and perform other menu-centric operations via a second right-click.<br \/>\nAs you see for the remainder of the current MyWordPad example, I\u2019ll typically show you the final generated XAML; however, do take the time to experiment with the visual designers to simplify the task at hand.<\/p>\n<p>Building the Toolbar<br \/>\nToolbars (represented by the ToolBar class in WPF) typically provide an alternative manner for activating a menu option. Add the following markup directly after the closing scope of your Menu definition:<\/p>\n<p><!\u2014Put Toolbar under the Menu --><br \/>\n<ToolBar DockPanel.Dock =\"Top\" ><br \/>\n<Button Content =\"Exit\" MouseEnter =\"MouseEnterExitArea\" MouseLeave =\"MouseLeaveArea\" Click =\"FileExit_Click\"\/><br \/>\n<Separator\/><br \/>\n<Button Content =\"Check\" MouseEnter =\"MouseEnterToolsHintsArea\" MouseLeave =\"MouseLeaveArea\" Click =\"ToolsSpellingHints_Click\" Cursor=\"Help\" \/><br \/>\n<\/ToolBar><\/p>\n<p>Your ToolBar control consists of two Button controls, which just so happen to handle the same events and are handled by the same methods in your code file. Using this technique, you can double up your handlers to serve both menu items and toolbar buttons. Although this toolbar uses the typical push buttons, you should appreciate that the ToolBar type \u201cis-a\u201d ContentControl; therefore, you are free to embed any types into its surface (e.g., drop-down lists, images, and graphics). The only other point of interest here is that the Check button supports a custom mouse cursor through the Cursor property.<\/p>\n<p>\u25a0Note  you can optionally wrap the ToolBar element within a ToolBarTray element, which controls layout, docking, and drag-and-drop operations for a set of ToolBar objects.<\/p>\n<p>Building the Status Bar<br \/>\nA StatusBar control will be docked to the lower portion of the DockPanel and contain a single TextBlock control, which you have not used prior to this point in the chapter. You can use a TextBlock to hold text that supports numerous textual annotations, such as bold text, underlined text, line breaks, and so forth. Add the following markup directly after the previous ToolBar definition:<\/p>\n<p><!-- Put a StatusBar at the bottom --><br \/>\n<StatusBar DockPanel.Dock =\"Bottom\" Background=\"Beige\" ><br \/>\n<StatusBarItem><br \/>\n<TextBlock Name=\"statBarText\" Text=\"Ready\"\/><br \/>\n<\/StatusBarItem><br \/>\n<\/StatusBar><\/p>\n<p>Finalizing the UI Design<br \/>\nThe final aspect of your UI design is to define a splittable Grid that defines two columns. On the left, place an Expander control that will display a list of spelling suggestions, wrapped within a StackPanel. On the right, place a TextBox control that supports multiple lines and scrollbars and includes enabled spell-checking. You mount the entire Grid to the left of the parent DockPanel. Add the following XAML markup directly under the StatusBar markup to complete the definition of your window\u2019s UI:<\/p>\n<p><Grid DockPanel.Dock =\"Left\" Background =\"AliceBlue\"><br \/>\n\u2014-- Define the rows and columns --><br \/>\n<Grid.ColumnDefinitions><br \/>\n<ColumnDefinition \/><br \/>\n<ColumnDefinition \/><br \/>\n<\/Grid.ColumnDefinitions><\/p>\n<p><GridSplitter Grid.Column =\"0\" Width =\"5\" Background =\"Gray\" \/><br \/>\n<StackPanel Grid.Column=\"0\" VerticalAlignment =\"Stretch\" ><br \/>\n<Label Name=\"lblSpellingInstructions\" FontSize=\"14\" Margin=\"10,10,0,0\"> Spelling Hints<br \/>\n<\/Label><\/p>\n<p><Expander Name=\"expanderSpelling\" Header =\"Try these!\" Margin=\"10,10,10,10\"><br \/>\n\u2014-- This will be filled programmatically --><br \/>\n<Label Name =\"lblSpellingHints\" FontSize =\"12\"\/><br \/>\n<\/Expander><br \/>\n<\/StackPanel><\/p>\n<p>\u2014-- This will be the area to type within --><br \/>\n<TextBox Grid.Column =\"1\"\nSpellCheck.IsEnabled =\"True\" AcceptsReturn =\"True\"\nName =\"txtData\" FontSize =\"14\" BorderBrush =\"Blue\" VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Auto\"><br \/>\n<\/TextBox><br \/>\n<\/Grid><\/p>\n<p>Implementing the MouseEnter\/MouseLeave Event Handlers<br \/>\nAt this point, the UI of your window is complete. The only remaining tasks are to provide an implementation for the remaining event handlers. Begin by updating your C# code file so that each of the MouseEnter, MouseLeave, and MouseExit handlers sets the text pane of the status bar with a fitting message to help the end user, like so:<\/p>\n<p>public partial class MainWindow : System.Windows.Window<br \/>\n{<br \/>\n...<br \/>\nprotected void MouseEnterExitArea(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\nstatBarText.Text = \"Exit the Application\";<br \/>\n}<br \/>\nprotected void MouseEnterToolsHintsArea(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\nstatBarText.Text = \"Show Spelling Suggestions\";<br \/>\n}<br \/>\nprotected void MouseLeaveArea(object sender, RoutedEventArgs args)<br \/>\n{<\/p>\n<p>statBarText.Text = \"Ready\";<br \/>\n}<br \/>\n}<\/p>\n<p>At this point, you can run your application. You should see your status bar change its text based on which menu item\/toolbar button you hover your mouse over.<\/p>\n<p>Implementing the Spell-Checking Logic<br \/>\nThe WPF API ships with built-in spell-checker support, which is independent of Microsoft Office products. This means you don\u2019t need to use the COM interop layer to use the spell-checker of Microsoft Word; instead, you can easily add the same type of support with only a few lines of code.<br \/>\nYou might recall that when you defined the TextBox control, you set the SpellCheck.IsEnabled property to true. When you do this, misspelled words are underlined with a red squiggle, just as they are in Microsoft Office. Even better, the underlying programming model gives you access to the spell-checker engine, which allows you to get a list of suggestions for misspelled words. Add the following code to your ToolsSpellingHints_Click() method:<\/p>\n<p>protected void ToolsSpellingHints_Click(object sender, RoutedEventArgs args)<br \/>\n{<br \/>\nstring spellingHints = string.Empty;<\/p>\n<p>\/\/ Try to get a spelling error at the current caret location. SpellingError error = txtData.GetSpellingError(txtData.CaretIndex); if (error != null)<br \/>\n{<br \/>\n\/\/ Build a string of spelling suggestions.<br \/>\nforeach (string s in error.Suggestions)<br \/>\n{<br \/>\nspellingHints += $\"{s}\\n\";<br \/>\n}<\/p>\n<p>\/\/ Show suggestions and expand the expander. lblSpellingHints.Content = spellingHints; expanderSpelling.IsExpanded = true;<br \/>\n}<br \/>\n}<\/p>\n<p>The preceding code is quite simple. You simply figure out the current location of the caret in the text box by using the CaretIndex property to extract a SpellingError object. If there is an error at said location (meaning the value is not null), you loop over the list of suggestions using the aptly named Suggestions property. After you have all the suggestions for the misspelled word, you connect the data to the Label in the Expander.<br \/>\nSo there you have it! With only a few lines of procedural code (and a healthy dose of XAML), you have the beginnings of a functioning word processor. An understanding of control commands can help you add a bit more pizzazz.<\/p>\n<p>Understanding WPF Commands<br \/>\nWPF provides support for what might be considered control-agnostic events via the command architecture. A typical .NET Core event is defined within a specific base class and can be used only by that class or a derivative thereof. Therefore, normal .NET Core events are tightly coupled to the class in which they are defined.<\/p>\n<p>In contrast, WPF commands are event-like entities that are independent from a specific control and, in many cases, can be successfully applied to numerous (and seemingly unrelated) control types. By way of a few examples, WPF supports copy, paste, and cut commands, which you can apply to a wide variety of UI elements (e.g., menu items, toolbar buttons, and custom buttons), as well as keyboard shortcuts (e.g., Ctrl+C and Ctrl+V).<br \/>\nWhile other UI toolkits (such as Windows Forms) provided standard events for such purposes, using them typically left you with redundant and hard-to-maintain code. Under the WPF model, you can use commands as an alternative. The end result typically yields a smaller and more flexible code base.<\/p>\n<p>The Intrinsic Command Objects<br \/>\nWPF ships with numerous built-in control commands, all of which you can configure with associated keyboard shortcuts (or other input gestures). Programmatically speaking, a WPF command is any object that supports a property (often called Command) that returns an object implementing the ICommand interface, as shown here:<\/p>\n<p>public interface ICommand<br \/>\n{<br \/>\n\/\/ Occurs when changes occur that affect whether<br \/>\n\/\/ or not the command should execute.<br \/>\nevent EventHandler CanExecuteChanged;<\/p>\n<p>\/\/ Defines the method that determines whether the command<br \/>\n\/\/ can execute in its current state.<br \/>\nbool CanExecute(object parameter);<\/p>\n<p>\/\/ Defines the method to be called when the command is invoked.<br \/>\nvoid Execute(object parameter);<br \/>\n}<\/p>\n<p>WPF provides various command classes, which expose close to 100 command objects, out of the box. These classes define numerous properties that expose specific command objects, each of which implements ICommand. Table 26-3 documents some of the standard command objects available.<\/p>\n<p>Table 26-3. The Intrinsic WPF Control Command Objects<\/p>\n<p>WPF Class   Command Objects Meaning in Life<br \/>\nApplicationCommands Close, Copy, Cut, Delete, Find, Open, Paste, Save, SaveAs, Redo, Undo   Various application-level commands<br \/>\nComponentCommands   MoveDown, MoveFocusBack, MoveLeft, MoveRight, ScrollToEnd, ScrollToHome Various commands common to UI components<br \/>\nMediaCommands   BoostBase, ChannelUp, ChannelDown, FastForward, NextTrack, Play, Rewind, Select, Stop   Various media-centric commands<br \/>\nNavigationCommands  BrowseBack, BrowseForward, Favorites, LastPage, NextPage, Zoom  Various commands relating to the WPF navigation model<br \/>\nEditingCommands AlignCenter, CorrectSpellingError, DecreaseFontSize, EnterLineBreak, EnterParagraphBreak, MoveDownByLine, MoveRightByWord   Various commands relating to the WPF Documents API<\/p>\n<p>Connecting Commands to the Command Property<br \/>\nIf you want to connect any of the WPF command properties to a UI element that supports the Command property (such as a Button or MenuItem), you have very little work to do. You can see how to do this by updating the current menu system so it supports a new topmost menu item named Edit and three subitems to account for copying, pasting, and cutting of textual data, like so:<\/p>\n<p><Menu DockPanel.Dock =\"Top\" HorizontalAlignment=\"Left\" Background=\"White\" BorderBrush\n=\"Black\"><br \/>\n<MenuItem Header =\"_File\" ><br \/>\n<MenuItem Header =\"_Exit\" MouseEnter =\"MouseEnterExitArea\" MouseLeave =\"MouseLeaveArea\" Click =\"FileExit_Click\"\/><br \/>\n<\/MenuItem><\/p>\n<p><-- New menu item with commands! --><br \/>\n<MenuItem Header=\"_Edit\"><br \/>\n<MenuItem Command=\"ApplicationCommands.Copy\" \/><br \/>\n<MenuItem Command=\"ApplicationCommands.Cut\" \/><br \/>\n<MenuItem Command=\"ApplicationCommands.Paste\" \/><br \/>\n<\/MenuItem><\/p>\n<p><MenuItem Header=\"_Tools\"><br \/>\n<MenuItem Header=\"_Spelling Hints\" MouseEnter=\"MouseEnterToolsHintsArea\" MouseLeave=\"MouseLeaveArea\" Click=\"ToolsSpellingHints_Click\" \/><br \/>\n<\/MenuItem><\/p>\n<p>Notice that each of the subitems on the Edit menu has a value assigned to the Command property. Doing this means that the menu items automatically receive the correct name and shortcut key (e.g., Ctrl+C for a cut operation) in the menu item UI; it also means that the application is now copy, cut, and paste aware with no procedural code!<br \/>\nIf you run the application and select some of text, you can use your new menu items out of the box. As a bonus, your application is also equipped to respond to a standard right-click operation to present the user with the same options.<\/p>\n<p>Connecting Commands to Arbitrary Actions<br \/>\nIf you want to connect a command object to an arbitrary (application-specific) event, you will need to drop down to procedural code. Doing so is not complex, but it does involve a bit more logic than you see in XAML. For example, assume that you want to have the entire window respond to the F1 key so that when the end user presses this key, he will activate an associated help system. Also, assume your code file for the main window defines a new method named SetF1CommandBinding(), which you call within the constructor after the call to InitializeComponent().<\/p>\n<p>public MainWindow()<br \/>\n{<br \/>\nInitializeComponent();<br \/>\nSetF1CommandBinding();<br \/>\n}<\/p>\n<p>This new method will programmatically create a new CommandBinding object, which you can use whenever you need to bind a command object to a given event handler in your application. Here, you configure your CommandBinding object to operate with the ApplicationCommands.Help command, which is automatically F1 aware:<\/p>\n<p>private void SetF1CommandBinding()<br \/>\n{<br \/>\nCommandBinding helpBinding = new CommandBinding(ApplicationCommands.Help); helpBinding.CanExecute += CanHelpExecute;<br \/>\nhelpBinding.Executed += HelpExecuted; CommandBindings.Add(helpBinding);<br \/>\n}<\/p>\n<p>Most CommandBinding objects will want to handle the CanExecute event (which allows you to specify whether the command occurs based on the operation of your program) and the Executed event (which is where you can author the content that should occur once the command occurs). Add the following event handlers to your Window-derived type (note the format of each method, as required by the associated delegates):<\/p>\n<p>private void CanHelpExecute(object sender, CanExecuteRoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Here, you can set CanExecute to false if you want to prevent the command from executing.<br \/>\ne.CanExecute = true;<br \/>\n}<\/p>\n<p>private void HelpExecuted(object sender, ExecutedRoutedEventArgs e)<br \/>\n{<br \/>\nMessageBox.Sh\u201cw(\"Look, it is not that difficult. Just type something!\", \"Help!\");<br \/>\n}<\/p>\n<p>In the preceding snippet, you implemented CanHelpExecute() so it always allows F1 help to launch; you do this by simply returning true. However, if you have certain situations where the help system should not display, you can account for this and return false when necessary. Your \u201chelp system\u201d displayed within HelpExecuted() is little more than a message box. At this point, you can run your application. When you press the F1 key on your keyboard, you will see your message box appear.<\/p>\n<p>Working with the Open and Save Commands<br \/>\nTo complete the current example, you will add functionality to save your text data to an external file and open up *.txt files for editing. If you want to take the long road, you can manually add programming logic that enables or disables new menu items based on whether your TextBox has data inside it. Once again, however, you can use commands to decrease your burden.<br \/>\nBegin by updating the MenuItem element that represents your topmost File menu by adding the following two new submenus that use the Save and Open ApplicationCommands objects:<\/p>\n<p><MenuItem Head\"r=\"_F\"le\"><br \/>\n<MenuItem Comman\" =\"ApplicationCommands.O\"en\"\/><br \/>\n<MenuItem Comman\" =\"ApplicationCommands.S\"ve\"\/><br \/>\n<Separator\/><br \/>\n<MenuItem Heade\" =\"_E\"it\"\n\n<\/MenuItem><\/p>\n<p>MouseEnte\" =\"MouseEnterExitA\"ea\"<br \/>\nMouseLeav\" =\"MouseLeaveA\"ea\" Clic\" =\"FileExit_Cl\"ck\"\/><\/p>\n<p>Again, remember that all command objects implement the ICommand interface, which defines two events (CanExecute and Executed). Now you need to enable the entire window so it can check whether it is currently okay to fire these commands; if so, you can define an event handler to execute the custom code.<br \/>\nYou do this by populating the CommandBindings collection maintained by the window. Doing so in XAML requires that you use property-element syntax to define a Window.CommandBindings scope in which you place two CommandBinding definitions. Update your Window like this:<\/p>\n<p><Window x:Cla\"s=\"MyWordPad.MainWin\"ow\" xml\"s=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentat\"on\" xmlns\"x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/x\"ml\" Tit\"e=\"MySpellChec\"er\" Heig\"t=\"\"31\" Wid\"h=\"\"08\" WindowStartupLocatio\" =\"CenterScr\"en\" ><\/p>\n<p>\u2014-- This will inform the Window which handlers to call, when testing for the Open and Save commands. --><br \/>\n<Window.CommandBindings><br \/>\n<CommandBinding Comma\"d=\"ApplicationCommands.O\"en\"\nExecut\"d=\"OpenCmdExecu\"ed\" CanExecu\"e=\"OpenCmdCanExec\"te\"\/><br \/>\n<CommandBinding Comma\"d=\"ApplicationCommands.S\"ve\"\nExecut\"d=\"SaveCmdExecu\"ed\" CanExecu\"e=\"SaveCmdCanExec\"te\"\/><br \/>\n<\/Window.CommandBindings><\/p>\n<p>\u2014-- This panel establishes the content for the window --><br \/>\n<DockPanel><br \/>\n...<br \/>\n<\/DockPanel><br \/>\n<\/Window><\/p>\n<p>Now right-click each of the Executed and CanExecute attributes in your XAML editor and pick the Navigate to Event Handler menu option. As you might recall from Chapter 25, this will automatically generate stub code for the event itself. At this point, you should have four empty handlers in the C# code file for the window.<br \/>\nThe implementation of CanExecute event handlers will tell the window that it is okay to fire the corresponding Executed events at any time by setting the CanExecute property of the incoming CanExecuteRoutedEventArgs object.<\/p>\n<p>private void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)<br \/>\n{<br \/>\ne.CanExecute = true;<br \/>\n}<\/p>\n<p>private void SaveCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)<br \/>\n{<br \/>\ne.CanExecute = true;<br \/>\n}<\/p>\n<p>The corresponding Executed handlers perform the actual work of displaying the open and save dialog boxes; they also send the data in your TextBox to a file. Begin by making sure that you import the System.IO and Microsoft.Win32 namespaces into your code file. The following completed code is straightforward:<\/p>\n<p>private void OpenCmdExecuted(object sender, ExecutedRoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Create an open file dialog box and only show XAML files.<br \/>\nvar openDlg = new OpenFileDialog { Filter\u201c= \"Text Files |*.\"xt\"};<\/p>\n<p>\/\/ Did they click on the OK button? if (true == openDlg.ShowDialog())<br \/>\n{<br \/>\n\/\/ Load all text of selected file.<br \/>\nstring dataFromFile = File.ReadAllText(openDlg.FileName);<\/p>\n<p>\/\/ Show string in TextBox. txtData.Text = dataFromFile;<br \/>\n}<br \/>\n}<\/p>\n<p>private void SaveCmdExecuted(object sender, ExecutedRoutedEventArgs e)<br \/>\n{<br \/>\nvar saveDlg = new SaveFileDialog { Filter\"= \"Text Files |*.\"xt\"};<\/p>\n<p>\/\/ Did they click on the OK button? if (true == saveDlg.ShowDialog())<br \/>\n{<br \/>\n\/\/ Save data in the TextBox to the named file. File.WriteAllText(saveDlg.FileName, txtData.Text);<br \/>\n}<br \/>\n}<\/p>\n<p>\u25a0Note Chapter 29 will take a much deeper look into the WPF command system. in it, you will create custom commands based on the ICommand interface as well as create RelayCommands.<\/p>\n<p>That wraps up this example and your initial look at working with WPF controls. Here, you learned how to work with basic commands, menu systems, status bars, toolbars, nested panels, and a few basic UI controls, such as TextBox and Expander. The next example will work with some more exotic controls while examining several important WPF services at the same time.<\/p>\n<p>Understanding Routed Events<br \/>\nYou might have noticed the RoutedEventArgs parameter instead of EventArgs in the previous code example. The routed events model is a refinement of the standard CLR event model designed to ensure that events can be processed in a manner that is fitting for XAML\u2019s description of a tree of objects. Assume you have a new WPF application project named WpfRoutedEvents. Now, update the XAML definition of the<\/p>\n<p>MainWindow.xaml file by adding the following Button control, which has some complex content, inside the<br \/>\n<Grid> (Window and Grid elements omitted for brevity):<\/p>\n<p><Button Name=\"btnClickMe\" Height=\"75\" Width = \"250\" Click =\"btnClickMe_Clicked\"><br \/>\n<StackPanel Orientation =\"Horizontal\"><br \/>\n<Label Height=\"50\" FontSize =\"20\">Fancy Button!<\/Label><br \/>\n<Canvas Height =\"50\" Width =\"100\" ><br \/>\n<Ellipse Name = \"outerEllipse\" Fill =\"Green\" Height =\"25\" Width =\"50\" Cursor=\"Hand\" Canvas.Left=\"25\" Canvas.Top=\"12\"\/><br \/>\n<Ellipse Name = \"innerEllipse\" Fill =\"Yellow\" Height = \"15\" Width =\"36\"\nCanvas.Top=\"17\" Canvas.Left=\"32\"\/><br \/>\n<\/Canvas><br \/>\n<\/StackPanel><br \/>\n<\/Button><\/p>\n<p>Notice in the Button\u2019s opening definition that you have handled the Click event by specifying the name of a method to be called when the event is raised. The Click event works with the RoutedEventHandler delegate, which expects an event handler that takes an object as the first parameter and a System.Windows. RoutedEventArgs as the second. Implement this handler as so:<\/p>\n<p>public void btnClickMe_Clicked(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Do something when button is clicked.<br \/>\nMessageBox.Show(\"Clicked the button\");<br \/>\n}<\/p>\n<p>If you run your application, you will see this message box display, regardless of which part of the button\u2019s content you click (the green Ellipse, the yellow Ellipse, the Label, or the Button\u2019s surface). This is a good thing. Imagine how tedious WPF event handling would be if you were forced to handle a Click event for every one of these subelements. Not only would the creation of separate event handlers for each aspect of the Button be labor intensive, you would end up with some mighty nasty code to maintain down the road.<br \/>\nThankfully, WPF routed events take care of ensuring that your single Click event handler will be called regardless of which part of the button is clicked automatically. Simply put, the routed events model automatically propagates an event up (or down) a tree of objects, looking for an appropriate handler.<br \/>\nSpecifically speaking, a routed event can make use of three routing strategies. If an event is moving from the point of origin up to other defining scopes within the object tree, the event is said to be a bubbling event. Conversely, if an event is moving from the outermost element (e.g., a Window) down to the point of origin, the event is said to be a tunneling event. Finally, if an event is raised and handled only by the originating element (which is what could be described as a normal CLR event), it is said to be a direct event.<\/p>\n<p>The Role of Routed Bubbling Events<br \/>\nIn the current example, if the user clicks the inner yellow oval, the Click event bubbles out to the next level of scope (the Canvas), then to the StackPanel, and finally to the Button where the Click event handler is handled. In a similar way, if the user clicks the Label, the event is bubbled to the StackPanel and then finally to the Button element.<br \/>\nGiven this bubbling routed event pattern, you have no need to worry about registering specific Click event handlers for all members of a composite control. However, if you want to perform custom clicking logic for multiple elements within the same object tree, you can do so.<\/p>\n<p>By way of illustration, assume you need to handle the clicking of the outerEllipse control in a unique manner. First, handle the MouseDown event for this subelement (graphically rendered types such as the Ellipse do not support a Click event; however, they can monitor mouse button activity via MouseDown, MouseUp, etc.).<\/p>\n<p><Button Name=\"btnClickMe\" Height=\"75\" Width = \"250\" Click =\"btnClickMe_Clicked\"><br \/>\n<StackPanel Orientation =\"Horizontal\"><br \/>\n<Label Height=\"50\" FontSize =\"20\">Fancy Button!<\/Label><br \/>\n<Canvas Height =\"50\" Width =\"100\" ><br \/>\n<Ellipse Name = \"outerEllipse\" Fill =\"Green\"\nHeight =\"25\" MouseDown =\"outerEllipse_MouseDown\"\nWidth =\"50\" Cursor=\"Hand\" Canvas.Left=\"25\" Canvas.Top=\"12\"\/><br \/>\n<Ellipse Name = \"innerEllipse\" Fill =\"Yellow\" Height = \"15\" Width =\"36\" Canvas.Top=\"17\" Canvas.Left=\"32\"\/><br \/>\n<\/Canvas><br \/>\n<\/StackPanel><br \/>\n<\/Button><\/p>\n<p>Then implement an appropriate event handler, which for illustrative purposes will simply change the<br \/>\nTitle property of the main window, like so:<\/p>\n<p>public void outerEllipse_MouseDown(object sender, MouseButtonEventArgs e)<br \/>\n{<br \/>\n\/\/ Change title of window.<br \/>\nthis.Title = \"You clicked the outer ellipse!\";<br \/>\n}<\/p>\n<p>With this, you can now take different courses of action depending on where the end user has clicked (which boils down to the outer ellipse and everywhere else within the button\u2019s scope).<\/p>\n<p>\u25a0Note Routed bubbling events always move from the point of origin to the next defining scope. Thus, in this example, if you click the innerEllipse object, the event will be bubbled to the Canvas, not to the outerEllipse because they are both Ellipse types within the scope of Canvas.<\/p>\n<p>Continuing or Halting Bubbling<br \/>\nCurrently, if the user clicks the outerEllipse object, it will trigger the registered MouseDown event handler for this Ellipse object, at which point the event bubbles to the button\u2019s Click event. If you want to inform WPF to stop bubbling up the tree of objects, you can set the Handled property of the EventArgs parameter to true, as follows:<\/p>\n<p>public void outerEllipse_MouseDown(object sender, MouseButtonEventArgs e)<br \/>\n{<br \/>\n\/\/ Change title of window.<br \/>\nthis.Title = \"You clicked the outer ellipse!\";<br \/>\n\/\/ Stop bubbling!<br \/>\ne.Handled = true;<br \/>\n}<\/p>\n<p>In this case, you would find that the title of the window is changed, but you will not see the MessageBox displayed by the Click event handler of the Button. In a nutshell, routed bubbling events make it possible to allow a complex group of content to act either as a single logical element (e.g., a Button) or as discrete items (e.g., an Ellipse within the Button).<\/p>\n<p>The Role of Routed Tunneling Events<br \/>\nStrictly speaking, routed events can be bubbling (as just described) or tunneling in nature. Tunneling events (which all begin with the Preview suffix; e.g., PreviewMouseDown) drill down from the topmost element into the inner scopes of the object tree. By and large, each bubbling event in the WPF base class libraries is paired with a related tunneling event that fires before the bubbling counterpart. For example, before the bubbling MouseDown event fires, the tunneling PreviewMouseDown event fires first.<br \/>\nHandling a tunneling event looks just like the processing of handling any other events; simply assign the event handler name in XAML (or, if needed, use the corresponding C# event-handling syntax in your code file) and implement the handler in the code file. Just to illustrate the interplay of tunneling and bubbling events, begin by handling the PreviewMouseDown event for the outerEllipse object, like so:<\/p>\n<p><Ellipse Name = \"outerEllipse\" Fill =\"Green\" Height =\"25\" MouseDown =\"outerEllipse_MouseDown\" PreviewMouseDown =\"outerEllipse_PreviewMouseDown\"\nWidth =\"50\" Cursor=\"Hand\" Canvas.Left=\"25\" Canvas.Top=\"12\"\/><\/p>\n<p>Next, retrofit the current C# class definition by updating each event handler (for all objects) to append data about the current event into a string member variable named mouseActivity, using the incoming event args object. This will allow you to observe the flow of events firing in the background.<\/p>\n<p>public partial class MainWindow : Window<br \/>\n{<br \/>\nstring _mouseActivity = string.Empty; public MainWindow()<br \/>\n{<br \/>\nInitializeComponent();<br \/>\n}<br \/>\npublic void btnClickMe_Clicked(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\nAddEventInfo(sender, e); MessageBox.Show(_mouseActivity, \"Your Event Info\");<br \/>\n\/\/ Clear string for next round.<br \/>\n_mouseActivity = \"\";<br \/>\n}<br \/>\nprivate void AddEventInfo(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n_mouseActivity += string.Format(<br \/>\n\"{0} sent a {1} event named {2}.\\n\", sender, e.RoutedEvent.RoutingStrategy, e.RoutedEvent.Name);<br \/>\n}<br \/>\nprivate void outerEllipse_MouseDown(object sender, MouseButtonEventArgs e)<br \/>\n{<br \/>\nAddEventInfo(sender, e);<br \/>\n}<\/p>\n<p>private void outerEllipse_PreviewMouseDown(object sender, MouseButtonEventArgs e)<br \/>\n{<br \/>\nAddEventInfo(sender, e);<br \/>\n}<br \/>\n}<\/p>\n<p>Notice that you are not halting the bubbling of an event for any event handler. If you run this application, you will see a unique message box display based on where you click the button. Figure 26-15 shows the result of clicking the outer Ellipse object.<\/p>\n<p>Figure 26-15. Tunneling first, bubbling second<\/p>\n<p>So, why do WPF events typically tend to come in pairs (one tunneling and one bubbling)? The answer is that by previewing events, you have the power to perform any special logic (data validation, disable bubbling action, etc.) before the bubbling counterpart fires. By way of an example, assume you have a TextBox that should contain only numerical data. You could handle the PreviewKeyDown event, and if you see the user has entered non-numerical data, you could cancel the bubbling event by setting the Handled property to true.<br \/>\nAs you would guess, when you are building a custom control that contains custom events, you could author the event in such a way that it can bubble (or tunnel) through a tree of XAML. For the purpose of this chapter, I will not be examining how to build custom routed events (however, the process is not that different from building a custom dependency property). If you are interested, check out the topic \u201cRouted Events Overview\u201d within the .NET Framework 4.7 SDK documentation. In it you will find a number of tutorials that will help you on your way.<\/p>\n<p>A Deeper Look at WPF APIs and Controls<br \/>\nThe remainder of this chapter will give you a chance to build a new WPF application using Visual Studio. The goal is to create a UI that consists of a TabControl widget containing a set of tabs. Each tab will illustrate some new WPF controls and interesting APIs you might want to make use of in your software projects. Along the way, you will also learn additional features of the Visual Studio WPF designers. To get started, create a new WPF application named WpfControlsAndAPIs.<\/p>\n<p>Working with the TabControl<br \/>\nAs mentioned, your initial window will contain a TabControl with three different tabs, each of which shows off a set of related controls and\/or WPF APIs. Update the window\u2019s Width to 800 and Height to 350. Locate the TabControl control in the Visual Studio Toolbox, drop one onto your designer, and update the markup to the following:<\/p>\n<p><TabControl Name=\"MyTabControl\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Stretch\"><br \/>\n<TabItem Header=\"TabItem\"><br \/>\n<Grid Background=\"#FFE5E5E5\"\/><br \/>\n<\/TabItem><br \/>\n<TabItem Header=\"TabItem\"><br \/>\n<Grid Background=\"#FFE5E5E5\"\/><br \/>\n<\/TabItem><br \/>\n<\/TabControl><\/p>\n<p>You will notice that you are given two tab items automatically. To add additional tabs, you simply need to right-click the TabControl node in the Document Outline window and select the Add TabItem menu option (you can also right-click the TabControl on the designer to activate the same menu option) or just start typing in the XAML editor. Add one additional tab using either approach.<br \/>\nNow, update each TabItem control through the XAML editor and change the Header property for each tab, naming them Ink API, Data Binding, and DataGrid. At this point, your window designer should look like what you see in Figure 26-16.<\/p>\n<p>Figure 26-16. The initial layout of the tab system<\/p>\n<p>Be aware that when you select a tab for editing, that tab becomes the active tab, and you can design that tab by dragging controls from the Toolbox window. Now that you have the core TabControl defined, you can work out the details tab by tab and learn more features of the WPF API along the way.<\/p>\n<p>Building the Ink API Tab<br \/>\nThe first tab will be used to show the overall role of WPF\u2019s digital Ink API, which allows you to incorporate painting functionality into a program easily. Of course, the application does not literally need to<br \/>\nbe a painting application; you can use this API for a wide variety of purposes, including capturing handwriting input.<\/p>\n<p>\u25a0Note  For most of the rest of this chapter (and the next WPF chapters as well), i will be editing the XaMl directly instead of using the various designer windows. While the dragging and dropping of controls works, more often than not the layout isn\u2019t what you want (visual studio adds margins and padding based on where you drop the control), and you spend a significant amount of time cleaning up the XaMl anyway.<\/p>\n<p>Begin by changing the Grid tag under the Ink API TabItem to a StackPanel and add a closing tag (make sure to remove \"\/\" from the opening tag). Your markup should look like this:<br \/>\n<TabItem Header=\"Ink API\"><br \/>\n<StackPanel Background=\"#FFE5E5E5\"><br \/>\n<\/StackPanel><br \/>\n<\/TabItem><\/p>\n<p>Designing the Toolbar<br \/>\nAdd a new ToolBar control into the StackPanel (using the XAML editor) named InkToolbar with a height of 60.<br \/>\n<ToolBar Name=\"InkToolBar\" Height=\"60\"><br \/>\n<\/ToolBar><\/p>\n<p>Add three RadioButton controls inside a WrapPanel, inside a Border control, to the ToolBar as follows:<\/p>\n<p><Border Margin=\"0,2,0,2.4\" Width=\"280\" VerticalAlignment=\"Center\"><br \/>\n<WrapPanel><br \/>\n<RadioButton x:Name=\"inkRadio\" Margin=\"5,10\" Content=\"Ink Mode!\" IsChecked=\"True\" \/><br \/>\n<RadioButton x:Name=\"eraseRadio\" Margin=\"5,10\" Content=\"Erase Mode!\" \/><br \/>\n<RadioButton x:Name=\"selectRadio\" Margin=\"5,10\" Content=\"Select Mode!\" \/><br \/>\n<\/WrapPanel><br \/>\n<\/Border><\/p>\n<p>When a RadioButton control is not placed inside of a parent panel control, it will take on a UI identical to a Button control! That\u2019s why I wrapped the RadioButton controls in the WrapPanel.<br \/>\nNext, add a Separator and then a ComboBox with a Width of 175 and a Margin of 10,0,0,0. Add three ComboBoxItem tags with content of Red, Green, and Blue, and follow the entire ComboBox with another Separator control, as follows:<\/p>\n<p><Separator\/><br \/>\n<ComboBox x:Name=\"comboColors\" Width=\"175\" Margin=\"10,0,0,0\"><br \/>\n<ComboBoxItem Content=\"Red\"\/><br \/>\n<ComboBoxItem Content=\"Green\"\/><br \/>\n<ComboBoxItem Content=\"Blue\"\/><br \/>\n<\/ComboBox><br \/>\n<Separator\/><\/p>\n<p>The RadioButton Control<br \/>\nIn this example, you want these three RadioButton controls to be mutually exclusive. In other GUI frameworks, ensuring that a group of related controls (such as radio buttons) were mutually exclusive required that you place them in the same group box. You don\u2019t need to do this under WPF. Instead, you can<\/p>\n<p>simply assign them all to the same group name. This is helpful because the related items do not need to be physically collected in the same area but can be anywhere in the window.<br \/>\nThe RadioButton class includes an IsChecked property, which toggles between true and false when the end user clicks the UI element. Furthermore, RadioButton provides two events (Checked and Unchecked) that you can use to intercept this state change.<\/p>\n<p>Add the Save, Load, and Delete Buttons<br \/>\nThe final controls in the ToolBar control will be a Grid holding three Button controls. Add the following markup after the last Separator control:<br \/>\n<Grid><br \/>\n<Grid.ColumnDefinitions><br \/>\n<ColumnDefinition Width=\"Auto\"\/><br \/>\n<ColumnDefinition Width=\"Auto\"\/><br \/>\n<ColumnDefinition Width=\"Auto\"\/><br \/>\n<\/Grid.ColumnDefinitions><br \/>\n<Button Grid.Column=\"0\" x:Name=\"btnSave\" Margin=\"10,10\" Width=\"70\" Content=\"Save Data\"\/><br \/>\n<Button Grid.Column=\"1\" x:Name=\"btnLoad\" Margin=\"10,10\" Width=\"70\" Content=\"Load Data\"\/><br \/>\n<Button Grid.Column=\"2\" x:Name=\"btnClear\" Margin=\"10,10\" Width=\"70\" Content=\"Clear\"\/><br \/>\n<\/Grid><\/p>\n<p>Add the InkCanvas Control<br \/>\nThe final control for the TabControl is the InkCanvas control. Add the following markup after the closing<br \/>\nToolBar tag and before the closing StackPanel tag, as follows:<br \/>\n<InkCanvas x:Name=\"MyInkCanvas\" Background=\"#FFB6F4F1\" \/><\/p>\n<p>Preview the Window<br \/>\nAt this point, you\u2019re ready to test the program, which you can do by pressing the F5 key. You should now see three mutually exclusive radio buttons, a combo box with three selections, and three buttons (see Figure 26-17).<\/p>\n<p>Figure 26-17. The completed layout for the Ink API tab<\/p>\n<p>Handling Events for the Ink API Tab<br \/>\nThe next step for the Ink API tab is to handle the Click event for each RadioButton control. As you have done in other WPF projects in this book, simply click the lightning bolt icon of the Visual Studio Properties editor to enter the names of event handlers. Using this approach, route the Click event for each button<br \/>\nto the same handler, named RadioButtonClicked. After you handle all three Click events, handle the SelectionChanged event of the ComboBox using a handler named ColorChanged(). When you finish, you should have the following C# code:<\/p>\n<p>public partial class MainWindow : Window<br \/>\n{<br \/>\npublic MainWindow()<br \/>\n{<br \/>\nthis.InitializeComponent();<\/p>\n<p>\/\/ Insert code required on object creation below this point.<br \/>\n}<br \/>\nprivate void RadioButtonClicked(object sender,RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ TODO: Add event handler implementation here.<br \/>\n}<\/p>\n<p>private void ColorChanged(object sender,SelectionChangedEventArgs e)<br \/>\n{<br \/>\n\/\/ TODO: Add event handler implementation here.<br \/>\n}<br \/>\n}<\/p>\n<p>You will implement these handlers in a later step, so leave them empty for the time being.<\/p>\n<p>Add Controls to the Toolbox<br \/>\nYou added an InkCanvas control by editing the XAML directly. If you wanted to use the UI to add it, the Visual Studio toolbox does not show you every possible WPF component by default. But you can update the items that are displayed in the Toolbox.<br \/>\nTo do so, right-click anywhere in the Toolbox area and select the Choose Items menu option. After a moment or two, you will see a list of possible components to add to the Toolbox. For your purposes, you are interested in adding the InkCanvas control (see Figure 26-18).<\/p>\n<p>Figure 26-18. Adding new components to the Visual Studio Toolbox<\/p>\n<p>\u25a0Note The ink controls are not compatible with the visual studio XaMl designer in version 16.8.3 (the current version at the time of this writing) or visual studio 16.9 Preview 2. The controls can still be used, just not through the designer.<\/p>\n<p>The InkCanvas Control<br \/>\nJust adding the InkCanvas enables drawing in your window. You can use your mouse or, if you have a touch- enabled device, your finger or a digitizer pen. Run the application and draw into the box (see Figure 26-19).<\/p>\n<p>Figure 26-19. The InkCanvas in action<\/p>\n<p>The InkCanvas does more than draw mouse (or stylus) strokes; it also supports a number of unique editing modes, controlled by the EditingMode property. You can assign this property any value from the related InkCanvasEditingMode enumeration. For this example, you are interested in Ink mode, which is the default option you just witnessed; Select mode, which allows the user to select a region with the mouse to move or resize; and EraseByStoke, which will delete the previous mouse stroke.<\/p>\n<p>\u25a0Note a stroke is the rendering that takes place during a single mouse down\/mouse up operation. The InkCanvas stores all strokes in a StrokeCollection object, which you can access using the Strokes property.<\/p>\n<p>Update your RadioButtonClicked() handler with the following logic, which places the InkCanvas in the correct mode, based on the selected RadioButton:<\/p>\n<p>private void RadioButtonClicked(object sender,RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Based on which button sent the event, place the InkCanvas in a unique<br \/>\n\/\/ mode of operation.<br \/>\nthis.MyInkCanvas.EditingMode = (sender as RadioButton)?.Content.ToString() switch<br \/>\n{<br \/>\n\/\/ These strings must be the same as the Content values for each<br \/>\n\/\/ RadioButton.<br \/>\n\"Ink Mode!\" => InkCanvasEditingMode.Ink,<br \/>\n\"Erase Mode!\" => InkCanvasEditingMode.EraseByStroke, \"Select Mode!\" => InkCanvasEditingMode.Select,<br \/>\n_ => this.MyInkCanvas.EditingMode<br \/>\n};<br \/>\n}<\/p>\n<p>Also, set the mode to Ink by default in the window\u2019s constructor. And while you are at it, set a default selection for the ComboBox (more details on this control in the next section), as follows:<\/p>\n<p>public MainWindow()<br \/>\n{<br \/>\nthis.InitializeComponent();<\/p>\n<p>\/\/ Be in Ink mode by default. this.MyInkCanvas.EditingMode = InkCanvasEditingMode.Ink; this.inkRadio.IsChecked = true; this.comboColors.SelectedIndex = 0;<br \/>\n}<\/p>\n<p>Now run your program again by pressing F5. Enter Ink mode and draw some data. Next, enter Erase mode and remove the previous mouse stroke you entered (you\u2019ll notice the mouse icon automatically looks like an eraser). Finally, enter Select mode and select some strokes by using the mouse as a lasso.<br \/>\nAfter you circle the item, you can move it around the canvas and resize its dimensions. Figure 26-20 shows your edit modes at work.<\/p>\n<p>Figure 26-20. The InkCanvas in action, with edit modes!<\/p>\n<p>The ComboBox Control<br \/>\nAfter you populate a ComboBox control (or a ListBox), you have three ways to determine the selected item. First, if you want to find the numerical index of the item selected, you can use the SelectedIndex property (which is zero based; a value of -1 represents no selection). Second, if you want to obtain the object within the list that has been selected, the SelectedItem property fits the bill. Third, the SelectedValue allows you to obtain the value of the selected object (typically obtained using a call to ToString()).<br \/>\nYou need to add the last bit of code for this tab to change the color of the strokes entered on the InkCanvas. The DefaultDrawingAttributes property of InkCanvas returns a DrawingAttributes object that allows you to configure numerous aspects of the pen nib, including its size and color (among other settings). Update your C# code with this implementation of the ColorChanged() method:<\/p>\n<p>private void ColorChanged(object sender, SelectionChangedEventArgs e)<br \/>\n{<br \/>\n\/\/ Get the selected value in the combo box.<br \/>\nstring colorToUse =<br \/>\n(this.comboColors.SelectedItem as ComboBoxItem)?.Content.ToString();<\/p>\n<p>\/\/ Change the color used to render the strokes.<br \/>\nthis.MyInkCanvas.DefaultDrawingAttributes.Color = (Color)ColorConverter.ConvertFromString(colorToUse);<br \/>\n}<\/p>\n<p>Now recall that the ComboBox has a collection of ComboBoxItems. If you view the generated XAML, you\u2019ll see the following definition:<\/p>\n<p><ComboBox x:Name=\"comboColors\" Width=\"100\" SelectionChanged=\"ColorChanged\"><br \/>\n<ComboBoxItem Content=\"Red\"\/><br \/>\n<ComboBoxItem Content=\"Green\"\/><br \/>\n<ComboBoxItem Content=\"Blue\"\/><br \/>\n<\/ComboBox><\/p>\n<p>When you call SelectedItem, you grab the selected ComboBoxItem, which is stored as a general Object. After you cast the Object as a ComboBoxItem, you pluck out the value of the Content, which will be the string Red, Green, or Blue. This string is then converted to a Color object using the handy ColorConverter utility class. Now run your program again. You should be able to change between colors as you render your image.<br \/>\nNote that the ComboBox and ListBox controls can contain complex content as well, rather than a list of text data. You can get a sense of some of the things that are possible by opening the XAML editor for your window and changing the definition of your ComboBox so it contains a set of StackPanel elements, each of which contains an Ellipse and a Label (notice that the Width of the ComboBox is 175).<\/p>\n<p><ComboBox x:Name=\"comboColors\" Width=\"175\" Margin=\"10,0,0,0\" SelectionChanged=\"Colo rChanged\"><br \/>\n<StackPanel Orientation =\"Horizontal\" Tag=\"Red\"><br \/>\n<Ellipse Fill =\"Red\" Height =\"50\" Width =\"50\"\/><br \/>\n<Label FontSize =\"20\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Content=\"Red\"\/><br \/>\n<\/StackPanel><\/p>\n<p><StackPanel Orientation =\"Horizontal\" Tag=\"Green\"><br \/>\n<Ellipse Fill =\"Green\" Height =\"50\" Width =\"50\"\/><br \/>\n<Label FontSize =\"20\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Content=\"Green\"\/><br \/>\n<\/StackPanel><\/p>\n<p><StackPanel Orientation =\"Horizontal\" Tag=\"Blue\"><br \/>\n<Ellipse Fill =\"Blue\" Height =\"50\" Width =\"50\"\/><br \/>\n<Label FontSize =\"20\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Content=\"Blue\"\/><br \/>\n<\/StackPanel><br \/>\n<\/ComboBox><\/p>\n<p>Notice that each StackPanel assigns a value to its Tag property, which is a simple, fast, and convenient way to discover which stack of items has been selected by the user (there are better ways to do this, but this will do for now). With this adjustment, you need to change the implementation of your ColorChanged() method, like this:<\/p>\n<p>private void ColorChanged(object sender, SelectionChangedEventArgs e)<br \/>\n{<br \/>\n\/\/ Get the Tag of the selected StackPanel.<br \/>\nstring colorToUse = (this.comboColors.SelectedItem as StackPanel).Tag.ToString();<br \/>\n...<br \/>\n}<\/p>\n<p>Now run your program again and take note of your unique ComboBox (see Figure 26-21).<\/p>\n<p>Figure 26-21. A custom ComboBox, thanks to the WPF content model<\/p>\n<p>Saving, Loading, and Clearing InkCanvas Data<br \/>\nThe last part of this tab will enable you to save and load your canvas data, as well as clear it of all content by adding event handlers for the buttons in the toolbar. Update the XAML for the buttons by adding markup for the click events, as follows:<\/p>\n<p><Button Grid.Column=\"0\" x:Name=\"btnSave\" Margin=\"10,10\" Width=\"70\" Content=\"Save Data\" Click=\"SaveData\"\/><br \/>\n<Button Grid.Column=\"1\" x:Name=\"btnLoad\" Margin=\"10,10\" Width=\"70\" Content=\"Load Data\" Click=\"LoadData\"\/><br \/>\n<Button Grid.Column=\"2\" x:Name=\"btnClear\" Margin=\"10,10\" Width=\"70\" Content=\"Clear\" Click=\"Clear\"\/><\/p>\n<p>Next, import the System.IO and System.Windows.Ink namespaces to your code file. Implement the handlers, like this:<br \/>\nprivate void SaveData(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Save all data on the InkCanvas to a local file.<br \/>\nusing (FileStream fs = new FileStream(\"StrokeData.bin\", FileMode.Create)) this.MyInkCanvas.Strokes.Save(fs);<br \/>\nfs.Close();<br \/>\nMessageBox.Show(\"Image Saved\",\"Saved\");<br \/>\n}<\/p>\n<p>private void LoadData(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Fill StrokeCollection from file.<br \/>\nusing(FileStream fs = new FileStream(\"StrokeData.bin\", FileMode.Open, FileAccess.Read)) StrokeCollection strokes = new StrokeCollection(fs);<br \/>\nthis.MyInkCanvas.Strokes = strokes;<br \/>\n}<\/p>\n<p>private void Clear(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Clear all strokes.<br \/>\nthis.MyInkCanvas.Strokes.Clear();<br \/>\n}<\/p>\n<p>You should now be able to save your data to a file, load it from the file, and clear the InkCanvas of all data. That wraps up the first tab of the TabControl, as well as your examination of the WPF digital Ink API. To be sure, there is more to say about this technology; however, you should be in a good position to dig into the topic further if that interests you. Next, you will learn how to use WPF data binding.<\/p>\n<p>Introducing the WPF Data-Binding Model<br \/>\nControls are often the target of various data-binding operations. Simply put, data binding is the act of connecting control properties to data values that might change over the course of your application\u2019s lifetime. Doing so lets a user interface element display the state of a variable in your code. For example, you might use data binding to accomplish the following:<br \/>\n\u2022Check a CheckBox control based on a Boolean property of a given object.<br \/>\n\u2022Display data in DataGrid objects from a relational database table.<br \/>\n\u2022Connect a Label to an integer that represents the number of files in a folder.<br \/>\nWhen you use the intrinsic WPF data-binding engine, you must be aware of the distinction between the source and the destination of the binding operation. As you might expect, the source of a data-binding<br \/>\noperation is the data itself (e.g., a Boolean property or relational data), while the destination (target) is the UI control property that uses the data content (e.g., a property on a CheckBox or TextBox control).<br \/>\nIn addition to binding to traditional data, WPF enables element binding, as stated in the previous examples. This means you can bind (for example) the visibility of a property based on the checked property of a check box. You could certainly do this in WinForms, but it had to be done through code. The WPF framework provides a rich data-binding ecosystem that can be almost entirely handled in markup. This also enables you to ensure that the source and destination stay in sync if either of their values changes.<\/p>\n<p>Building the Data Binding Tab<br \/>\nUsing the Document Outline editor, change the Grid of your second tab to a StackPanel. Now, use the Toolbox and Properties editor of Visual Studio to build the following initial layout:<\/p>\n<p><TabItem x:Name=\"tabDataBinding\" Header=\"Data Binding\"><br \/>\n<StackPanel Width=\"250\"><br \/>\n<Label Content=\"Move the scroll bar to see the current value\"\/><\/p>\n<p><!-- The scrollbar's value is the source of this data bind. --><br \/>\n<ScrollBar x:Name=\"mySB\" Orientation=\"Horizontal\" Height=\"30\"\nMinimum = \"1\" Maximum = \"100\" LargeChange=\"1\" SmallChange=\"1\"\/><\/p>\n<p><!-- The label's content will be bound to the scroll bar! --><br \/>\n<Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\" BorderThickness=\"2\" Content = \"0\"\/><br \/>\n<\/StackPanel><br \/>\n<\/TabItem><\/p>\n<p>Notice that the ScrollBar object (named mySB here) has been configured with a range between 1 and<br \/>\n100. The goal is to ensure that, as you reposition the thumb of the scrollbar (or click the left or right arrow), the Label will automatically update with the current value. Currently, the Content property of the Label control is set to the value \"0\"; however, you will change this via a data-binding operation.<\/p>\n<p>Establishing Data Bindings<br \/>\nThe glue that makes it possible to define a binding in XAML is the {Binding} markup extension. While you can define bindings through Visual Studio, it\u2019s just as easy to do it directly in the markup. Edit the Content property for the Label named labelSBThumb to the following:<\/p>\n<p><Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\" BorderThickness=\"2\" Content = \"{Binding Path=Value, ElementName=mySB}\"\/><\/p>\n<p>Note the value assigned to the Label\u2019s Content property. The {Binding} statement denotes a data- binding operation. The ElementName value represents the source of the data-binding operation (the ScrollBar object), while the Path indicates the property being bound to, in this case the Value of the scrollbar.<br \/>\nIf you run your program again, you will find that the content of the label updates based on the scrollbar value as you move the thumb!<\/p>\n<p>The DataContext Property<br \/>\nYou can define a data-binding operation in XAML using an alternative format, where it is possible to break out the values specified by the {Binding} markup extension by explicitly setting the DataContext property to the source of the binding operation, as follows:<\/p>\n<p><!-- Breaking object\/value apart via DataContext --><br \/>\n<Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\" BorderThickness=\"2\" DataContext = \"{Binding ElementName=mySB}\" Content = \"{Binding Path=Value}\" \/><\/p>\n<p>In the current example, the output would be identical if you were to modify the markup in this way.<br \/>\nGiven this, you might wonder when you would want to set the DataContext property explicitly. Doing so can be helpful because subelements can inherit its value in a tree of markup.<br \/>\nIn this way, you can easily set the same data source to a family of controls, rather than having to repeat a bunch of redundant \"{Binding ElementName=X, Path=Y}\" XAML values to multiple controls. For example, assume you have added the following new Button to the StackPanel of this tab (you\u2019ll see why it is so large in just a moment):<\/p>\n<p><Button Content=\"Click\" Height=\"200\"\/><\/p>\n<p>You could use Visual Studio to generate data bindings for multiple controls, but instead try entering the modified markup manually using the XAML editor, like so:<\/p>\n<p><!-- Note the StackPanel sets the DataContext property. --><br \/>\n<StackPanel Background=\"#FFE5E5E5\" DataContext = \"{Binding ElementName=mySB}\"><br \/>\n...<br \/>\n<!-- Now both UI elements use the scrollbar's value in unique ways. --><br \/>\n<Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\" BorderThickness=\"2\"\n\nContent = \"{Binding Path=Value}\"\/><\/p>\n<p><Button Content=\"Click\" Height=\"200\" FontSize = \"{Binding Path=Value}\"\/><br \/>\n<\/StackPanel><\/p>\n<p>Here, you set the DataContext property on the StackPanel directly. Therefore, as you move the thumb, you see not only the current value on the Label but also the font size of the Button grow and shrink accordingly, based on the same value (Figure 26-22 shows one possible output).<\/p>\n<p>Figure 26-22. Binding the ScrollBar value to a Label and a Button<\/p>\n<p>Formatting the Bound Data<br \/>\nThe ScrollBar type uses a double to represent the value of the thumb, rather than an expected whole number (e.g., an integer). Therefore, as you drag the thumb, you will find various floating-point numbers displayed within the Label (e.g., 61.0576923076923). The end user would find this rather unintuitive because he is most likely expecting to see whole numbers (e.g., 61, 62, and 63).<br \/>\nIf you want to format the data, you can add a ContentStringFormat property, passing in a custom string and a .NET Core format specifier, as follows:<\/p>\n<p><Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\"\nBorderThickness=\"2\" Content = \"{Binding Path=Value}\" ContentStringFormat=\"The value is:\n{0:F0}\"\/><\/p>\n<p>If you don\u2019t have any text in the format specification, then you need to lead with an empty set of braces, which is the escape sequence for XAML. This lets the processor know that the next characters are literals and not a binding statement, for example. Here is the updated XAML:<\/p>\n<p><Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\"\nBorderThickness=\"2\" Content = \"{Binding Path=Value}\" ContentStringFormat=\"{}{0:F0}\"\/><\/p>\n<p>\u25a0Note  if you are binding a Text property of a control, you can add a StringFormat name-value pair right in the binding statement. it only needs to be separate for Content properties.<\/p>\n<p>Data Conversion Using IValueConverter<br \/>\nIf you need to do more than just format the data, you can create a custom class that implements the IValueConverter interface of the System.Windows.Data namespace. This interface defines two members that allow you to perform the conversion to and from the target and destination (in the case of a two-way data binding). After you define this class, you can use it to qualify further the processing of your data-binding operation.<br \/>\nInstead of using the format property, you can use a value converter to display whole numbers within the Label control. To do this, add a new class (named MyDoubleConverter) to the project class. Next, add the following:<\/p>\n<p>using System.Windows.Data; namespace WpfControlsAndAPIs;<\/p>\n<p>public class MyDoubleConverter : IValueConverter<br \/>\n{<br \/>\npublic object Convert(object value, Type targetType, object parameter, System. Globalization.CultureInfo culture)<br \/>\n{<br \/>\n\/\/ Convert the double to an int.<br \/>\ndouble v = (double)value; return (int)v;<br \/>\n}<\/p>\n<p>public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)<br \/>\n{<br \/>\n\/\/ You won't worry about \"two-way\" bindings here, so just return the value.<br \/>\nreturn value;<br \/>\n}<br \/>\n}<\/p>\n<p>The Convert() method is called when the value is transferred from the source (the ScrollBar) to the destination (the Text property of the TextBox). You will receive many incoming arguments, but you only need to manipulate the incoming object for this conversion, which is the value of the current double. You can use this type to cast the type into an integer and return the new number.<br \/>\nThe ConvertBack() method will be called when the value is passed from the destination to the source (if you have enabled a two-way binding mode). Here, you simply return the value straightaway. Doing<br \/>\nso lets you type a floating-point value into the TextBox (e.g., 99.9) and have it automatically convert to a whole-number value (e.g., 99) when the user tabs off the control. This \u201cfree\u201d conversion happens because the Convert() method is called again, after a call to ConvertBack(). If you were simply to return null from ConvertBack(), your binding would appear to be out of sync because the text box would still be displaying a floating-point number.<br \/>\nTo use this converter in markup, you first have to create a local resource representing the custom class you just built. Don\u2019t worry about the mechanics of adding resources; the next few chapters will dive deeper into this subject. Add the following just after the opening Window tag:<\/p>\n<p><Window.Resources><br \/>\n<local:MyDoubleConverter x:Key=\"DoubleConverter\"\/><br \/>\n<\/Window.Resources><\/p>\n<p>Next, update the binding statement for the Label control to the following:<\/p>\n<p><Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\"\nBorderThickness=\"2\" Content = \"{Binding Path=Value,Converter={StaticResource DoubleConverter}}\" \/><\/p>\n<p>Now when you run the app, you see only whole numbers.<\/p>\n<p>Establishing Data Bindings in Code<br \/>\nYou can also register your data conversion class in code. Begin by cleaning up the current definition of the<br \/>\nLabel control in your data binding tab so that it no longer uses the {Binding} markup extension.<\/p>\n<p><Label x:Name=\"labelSBThumb\" Height=\"30\" BorderBrush=\"Blue\" BorderThickness=\"2\" \/><\/p>\n<p>Make sure there is a using for System.Windows.Data; then in your window\u2019s constructor, call a new private helper function called SetBindings(). In this method, add the following code (and make sure to call it from the constructor):<\/p>\n<p>using System.Windows.Data;<br \/>\n...<br \/>\nnamespace WpfControlsAndAPIs;<\/p>\n<p>public partial class MainWindow : Window<br \/>\n{<br \/>\npublic MainWindow()<br \/>\n{<br \/>\nInitializeComponent();<br \/>\n\/\/ommitted for brevity<br \/>\nSetBindings();<br \/>\n}<br \/>\n\/\/ommited for brevity private void SetBindings()<br \/>\n{<br \/>\n\/\/ Create a Binding object.<br \/>\nBinding b = new Binding<br \/>\n{<br \/>\n\/\/ Register the converter, source, and path.<br \/>\nConverter = new MyDoubleConverter(), Source = this.mySB,<br \/>\nPath = new PropertyPath(\"Value\")<br \/>\n\/\/ Call the SetBinding method on the Label.<br \/>\nthis.labelSBThumb.SetBinding(Label.ContentProperty, b);<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>The only part of this function that probably looks a bit off is the call to SetBinding(). Notice that the first parameter calls a static, read-only field of the Label class named ContentProperty. As you will<br \/>\nlearn later in this chapter, you are specifying what is known as a dependency property. For the time being, just know that when you set bindings in code, the first argument will nearly always require you to specify the name of the class that wants the binding (the Label, in this case), followed by a call to the underlying property with the Property suffix. In any case, running the application illustrates that the Label prints out only whole numbers.<\/p>\n<p>Building the DataGrid Tab<br \/>\nThe previous data-binding example illustrated how to configure two (or more) controls to participate in a data-binding operation. While this is helpful, it is also possible to bind data from XML files, database data, and in-memory objects. To complete this example, you will design the final tab of your tab control so it displays data obtained from the Inventory table of the AutoLot database.<br \/>\nAs with the other tabs, you begin by changing the current Grid to a StackPanel. Do this by directly updating the XAML using Visual Studio. Now define a DataGrid control in your new StackPanel named gridInventory, like so:<\/p>\n<p><TabItem x:Name=\"tabDataGrid\" Header=\"DataGrid\"><br \/>\n<StackPanel><br \/>\n<DataGrid x:Name=\"gridInventory\" Height=\"288\"\/><br \/>\n<\/StackPanel><br \/>\n<\/TabItem><\/p>\n<p>Use the NuGet package manager to add the following packages to your project:<br \/>\n\u2022Microsoft.EntityFrameworkCore<br \/>\n\u2022Microsoft.EntityFrameworkCore.SqlServer<br \/>\n\u2022Microsoft.Extensions.Configuration<br \/>\n\u2022Microsoft.Extensions.Configuration.Json<br \/>\nIf you prefer to use the .NET Core command-line interface (CLI) to add the packages, enter the following commands (from the solution directory):<\/p>\n<p>dotnet add WpfControlsAndAPIs package Microsoft.EntityFrameworkCore<br \/>\ndotnet add WpfControlsAndAPIs package Microsoft.EntityFrameworkCore.SqlServer dotnet add WpfControlsAndAPIs package Microsoft.Extensions.Configuration dotnet add WpfControlsAndAPIs package Microsoft.Extensions.Configuration.Json<\/p>\n<p>Next, right-click the solution, select Add \u27a4 Existing Project, and add the AutoLot.Dal and AutoLot.Dal. Models projects from Chapter 23 and project references to those projects. You can also use the CLI to add the references with the following command (you need to adjust your path for your projects\u2019 locations and computer\u2019s operation system):<\/p>\n<p>dotnet sln .\\Chapter26_AllProjects.sln add ..\\Chapter_23\\AutoLot.Models dotnet sln .\\Chapter26_AllProjects.sln add ..\\Chapter_23\\AutoLot.Dal dotnet add WpfControlsAndAPIs reference ..\\Chapter_23\\AutoLot.Models dotnet add WpfControlsAndAPIs reference ..\\Chapter_23\\AutoLot.Dal<\/p>\n<p>Confirm that the project reference from AutoLot.Dal to AutoLot.Dal.Models is still in place. Add the following namespaces to MainWindow.xaml.cs:<\/p>\n<p>using System.Linq;<br \/>\nusing AutoLot.Dal.EfStructures; using AutoLot.Dal.Repos;<br \/>\nusing Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration;<\/p>\n<p>Add two module-level properties in MainWindow.cs to hold the instances of IConfiguration and<br \/>\nApplicationDbContext.<\/p>\n<p>private IConfiguration _configuration; private ApplicationDbContext _context;<\/p>\n<p>Add a new method named GetConfigurationAndContext() to create those instances and call it from the constructor. The entire method is listed here:<\/p>\n<p>private void GetConfigurationAndDbContext()<br \/>\n{<br \/>\n_configuration = new ConfigurationBuilder()<br \/>\n.SetBasePath(Directory.GetCurrentDirectory())<br \/>\n.AddJsonFile(\"appsettings.json\", true, true)<br \/>\n.Build();<br \/>\nvar optionsBuilder =<br \/>\nnew DbContextOptionsBuilder<ApplicationDbContext>(); var connectionString =<br \/>\n_configuration.GetConnectionString(\"AutoLot\"); optionsBuilder.UseSqlServer(connectionString,<br \/>\nsqlOptions => sqlOptions.EnableRetryOnFailure());<br \/>\n_context = new ApplicationDbContext(optionsBuilder.Options);<br \/>\n}<\/p>\n<p>Add a new JSON file named appsettings.json to the project and set its build status to copy always.<br \/>\nThis can be done by right-clicking the file in Solution Explorer, selecting Properties, and then entering Copy always for the Copy To Output Directory setting . You can also add this to the project file to accomplish the same thing:<\/p>\n<p><ItemGroup><br \/>\n<None Update=\"appsettings.json\"><br \/>\n<CopyToOutputDirectory>Always<\/CopyToOutputDirectory><br \/>\n<\/None><br \/>\n<\/ItemGroup><\/p>\n<p>Update the JSON file to the following (updating your connection string to match your environment):<\/p>\n<p>{<br \/>\n\"ConnectionStrings\": {<br \/>\n\"AutoLotFinal\": \"server=.,5433;Database=AutoLot;User Id=sa;Password=P@ssw0rd;\"<br \/>\n}<br \/>\n}<\/p>\n<p>Open MainWindow.xaml.cs, add a final helper function called ConfigureGrid(), and call it from your constructor after you have configured ApplicationDbContext. All you need to do is add a few lines of code, like so:<\/p>\n<p>private void ConfigureGrid()<br \/>\n{<br \/>\nusing var repo = new CarRepo(_context);<br \/>\ngridInventory.ItemsSource = repo.GetAllIgnoreQueryFilters().ToList()<br \/>\n.Select(x=> new { x.Id, Make=x.MakeName, x.Color, x.PetName });<br \/>\n}<\/p>\n<p>Now when you run the project, you see the data populating the grid. If you want to make the grid somewhat fancier, you can use the Visual Studio Properties window to edit the grid to make it more appealing.<br \/>\nThat wraps up the current example. You\u2019ll see some other controls in action during later chapters; at this point, however, you should feel comfortable with the process of building UIs in Visual Studio and manually using XAML and C# code.<\/p>\n<p>Understanding the Role of Dependency Properties<br \/>\nLike any .NET Core API, WPF makes use of each member of the .NET Core type system (classes, structures, interfaces, delegates, enumerations) and each type member (properties, methods, events, constant data, read-only fields, etc.) within its implementation. However, WPF also supports a unique programming concept termed a dependency property.<br \/>\nLike a \u201cnormal\u201d .NET Core property (often termed a CLR property in the WPF literature), dependency properties can be set declaratively using XAML or programmatically within a code file. Furthermore, dependency properties (like CLR properties) ultimately exist to encapsulate data fields of a class and can be configured as read-only, write-only, or read-write.<br \/>\nTo make matters more interesting, in almost every case you will be blissfully unaware that you have actually set (or accessed) a dependency property as opposed to a CLR property! For example, the Height and Width properties that WPF controls inherit from FrameworkElement, as well as the Content member inherited from ControlContent, are all, in fact, dependency properties.<\/p>\n<p><!-- Set three dependency properties! --><br \/>\n<Button x:Name = \"btnMyButton\" Height = \"50\" Width = \"100\" Content = \"OK\"\/><\/p>\n<p>Given all these similarities, why does WPF define a new term for such a familiar concept? The answer lies in how a dependency property is implemented within the class. You\u2019ll see a coding example in just a little bit; however, from a high level, all dependency properties are created in the following manner:<br \/>\n\u2022First, the class that defined a dependency property must have DependencyObject in its inheritance chain.<br \/>\n\u2022A single dependency property is represented as a public, static, read-only field in the class of type DependencyProperty. By convention, this field is named by suffixing the word Property to the name of the CLR wrapper (see the final bullet point).<br \/>\n\u2022The DependencyProperty variable is registered via a static call to DependencyProperty.Register(), which typically occurs in a static constructor or inline when the variable is declared.<br \/>\n\u2022Finally, the class will define a XAML-friendly CLR property, which makes calls to methods provided by DependencyObject to get and set the value.<\/p>\n<p>Once implemented, dependency properties provide a number of powerful features that are used by various WPF technologies including data binding, animation services, styles, templates, and so forth. In a nutshell, the motivation of dependency properties is to provide a way to compute the value of a property based on the value of other inputs. Here is a list of some of these key benefits, which go well beyond those of the simple data encapsulation found with a CLR property:<br \/>\n\u2022Dependency properties can inherit their values from a parent element\u2019s XAML definition. For example, if you defined a value for the FontSize attribute in the opening tag of a Window, all controls in that Window would have the same font size by default.<br \/>\n\u2022Dependency properties support the ability to have values set by elements contained within their XAML scope, such as a Button setting the Dock property of a DockPanel parent. (Recall that attached properties do this very thing because attached properties are a form of dependency properties.)<br \/>\n\u2022Dependency properties allow WPF to compute a value based on multiple external values, which can be important for animation and data-binding services.<br \/>\n\u2022Dependency properties provide infrastructure support for WPF triggers (also used quite often when working with animation and data binding).<br \/>\nNow remember, in many cases you will interact with an existing dependency property in a manner identical to a normal CLR property (thanks to the XAML wrapper). In the previous section, which covered data binding, you saw that if you need to establish a data binding in code, you must call the SetBinding() method on the object that is the destination of the operation and specify the dependency property it will operate on, like so:<\/p>\n<p>private void SetBindings()<br \/>\n{<br \/>\nBinding b = new Binding<br \/>\n{<br \/>\n\/\/ Register the converter, source, and path. Converter = new MyDoubleConverter(),<br \/>\nSource = this.mySB,<br \/>\nPath = new PropertyPath(\"Value\")<br \/>\n};<br \/>\n\/\/ Specify the dependency property!<br \/>\nthis.labelSBThumb.SetBinding(Label.ContentProperty, b);<br \/>\n}<\/p>\n<p>You will see similar code when you examine how to start an animation in code in Chapter 28.<\/p>\n<p>\/\/ Specify the dependency property!<br \/>\nrt.BeginAnimation(RotateTransform.AngleProperty, dblAnim);<\/p>\n<p>The only time you need to build your own custom dependency property is when you are authoring a custom WPF control. For example, if you are building a UserControl that defines four custom properties and you want these properties to integrate well within the WPF API, you should author them using dependency property logic.<br \/>\nSpecifically, if your properties need to be the target of a data-binding or animation operation, if the property must broadcast when it has changed, if it must be able to work as a Setter in a WPF style, or if it must be able<br \/>\nto receive their values from a parent element, a normal CLR property will not be enough. If you were to use a normal CLR property, other programmers may indeed be able to get and set a value; however, if they attempt to use your properties within the context of a WPF service, things will not work as expected. Because you can never<\/p>\n<p>know how others might want to interact with the properties of your custom UserControl classes, you should get in the habit of always defining dependency properties when building custom controls.<\/p>\n<p>Examining an Existing Dependency Property<br \/>\nBefore you learn how to build a custom dependency property, let\u2019s take a look at how the Height property of the FrameworkElement class has been implemented internally. The relevant code is shown here (with my included comments):<\/p>\n<p>\/\/ FrameworkElement is-a DependencyObject.<br \/>\npublic class FrameworkElement : UIElement, IFrameworkInputElement, IInputElement, ISupportInitialize, IHaveResources, IQueryAmbient<br \/>\n{<br \/>\n...<br \/>\n\/\/ A static read-only field of type DependencyProperty.<br \/>\npublic static readonly DependencyProperty HeightProperty;<\/p>\n<p>\/\/ The DependencyProperty field is often registered<br \/>\n\/\/ in the static constructor of the class.<br \/>\nstatic FrameworkElement()<br \/>\n{<br \/>\n...<br \/>\nHeightProperty = DependencyProperty.Register( \"Height\",<br \/>\ntypeof(double), typeof(FrameworkElement),<br \/>\nnew FrameworkPropertyMetadata((double) 1.0 \/ (double) 0.0, FrameworkPropertyMetadataOptions.AffectsMeasure,<br \/>\nnew PropertyChangedCallback(FrameworkElement.OnTransformDirty)), new ValidateValueCallback(FrameworkElement.IsWidthHeightValid));<br \/>\n}<br \/>\n\/\/ The CLR wrapper, which is implemented using<br \/>\n\/\/ the inherited GetValue()\/SetValue() methods. public double Height<br \/>\n{<br \/>\nget { return (double) base.GetValue(HeightProperty); } set { base.SetValue(HeightProperty, value); }<br \/>\n}<br \/>\n}<br \/>\nAs you can see, dependency properties require quite a bit of additional code from a normal CLR property! And in reality, a dependency can be even more complex than what you see here (thankfully, many implementations are simpler than Height).<br \/>\nFirst, remember that if a class wants to define a dependency property, it must have DependencyObject in the inheritance chain because this is the class that defines the GetValue() and SetValue() methods used in the CLR wrapper. Because FrameworkElement is-a DependencyObject, this requirement is satisfied.<br \/>\nNext, recall that the entity that will hold the actual value of the property (a double in the case of Height) is represented as a public, static, read-only field of type DependencyProperty. The name of this field should, by convention, always be named by suffixing the word Property to the name of the related CLR wrapper, like so:<\/p>\n<p>public static readonly DependencyProperty HeightProperty;<\/p>\n<p>Given that dependency properties are declared as static fields, they are typically created (and registered) within the static constructor of the class. The DependencyProperty object is created via a call to the static DependencyProperty.Register() method. This method has been overloaded many times; however, in the case of Height, DependencyProperty.Register() is invoked as follows:<\/p>\n<p>HeightProperty = DependencyProperty.Register( \"Height\",<br \/>\ntypeof(double), typeof(FrameworkElement),<br \/>\nnew FrameworkPropertyMetadata((double)0.0, FrameworkPropertyMetadataOptions.AffectsMeasure,<br \/>\nnew PropertyChangedCallback(FrameworkElement.OnTransformDirty)), new ValidateValueCallback(FrameworkElement.IsWidthHeightValid));<\/p>\n<p>The first argument to DependencyProperty.Register() is the name of the normal CLR property on the class (Height, in this case), while the second argument is the type information of the underlying data type it is encapsulating (a double). The third argument specifies the type information of the class that this property belongs to (FrameworkElement, in this case). While this might seem redundant (after all, the HeightProperty field is already defined within the FrameworkElement class), this is a clever aspect of WPF, in that it allows one class to register properties on another (even if the class definition has been sealed!).<br \/>\nThe fourth argument passed to DependencyProperty.Register() in this example is what really gives dependency properties their own unique flavor. Here, a FrameworkPropertyMetadata object is passed that describes various details regarding how WPF should handle this property with respect to callback notifications (if the property needs to notify others when the value changes) and various options (represented by the FrameworkPropertyMetadataOptions enum) that control what is affected by the property in question. (Does it work with data binding? Can it be inherited?) In this case, the constructor arguments of FrameworkPropertyMetadata break down as so:<\/p>\n<p>new FrameworkPropertyMetadata(<br \/>\n\/\/ Default value of property. (double)0.0,<\/p>\n<p>\/\/ Metadata options. FrameworkPropertyMetadataOptions.AffectsMeasure,<\/p>\n<p>\/\/ Delegate pointing to method called when property changes. new PropertyChangedCallback(FrameworkElement.OnTransformDirty)<br \/>\n)<\/p>\n<p>Because the final argument to the FrameworkPropertyMetadata constructor is a delegate, note that its constructor parameter is pointing to a static method on the FrameworkElement class named<br \/>\nOnTransformDirty(). I won\u2019t bother to show the code behind this method, but be aware that any time you are building a custom dependency property, you can specify a PropertyChangedCallback delegate to point to a method that will be called when your property value has been changed.<br \/>\nThis brings me to the final parameter passed to the DependencyProperty.Register() method, a second delegate of type ValidateValueCallback, which points to a method on the FrameworkElement class that is called to ensure the value assigned to the property is valid.<\/p>\n<p>new ValidateValueCallback(FrameworkElement.IsWidthHeightValid)<\/p>\n<p>This method contains logic you might normally expect to find in the set block of a property (more information on this point in the next section).<\/p>\n<p>private static bool IsWidthHeightValid(object value)<br \/>\n{<br \/>\ndouble num = (double) value;<br \/>\nreturn ((!DoubleUtil.IsNaN(num) && (num >= 0.0)) && !double.IsPositiveInfinity(num));<br \/>\n}<\/p>\n<p>After the DependencyProperty object has been registered, the final task is to wrap the field within a normal CLR property (Height, in this case). Notice, however, that the get and set scopes do not<br \/>\nsimply return or set a class-level double-member variable but do so indirectly using the GetValue() and<br \/>\nSetValue() methods from the System.Windows.DependencyObject base class, as follows:<\/p>\n<p>public double Height<br \/>\n{<br \/>\nget { return (double) base.GetValue(HeightProperty); } set { base.SetValue(HeightProperty, value); }<br \/>\n}<\/p>\n<p>Important Notes Regarding CLR Property Wrappers<br \/>\nSo, just to recap the story thus far, dependency properties look like normal everyday properties when you get or set their values in XAML or code, but behind the scenes they are implemented with much more elaborate coding techniques. Remember, the whole reason to go through this process is to build a custom control<br \/>\nthat has custom properties that need to integrate with WPF services that demand communication with a dependency property (e.g., animation, data binding, and styles).<br \/>\nEven though part of the implementation of a dependency property includes defining a CLR wrapper, you should never put validation logic in the set block. For that matter, the CLR wrapper of a dependency property should never do anything other than call GetValue() or SetValue().<br \/>\nThe reason is that the WPF runtime has been constructed in such a way that when you write XAML that seems to set a property, such as<\/p>\n<p><Button x:Name=\"myButton\" Height=\"100\" ...\/><\/p>\n<p>the runtime will completely bypass the set block of the Height property and directly call SetValue()!<br \/>\nThe reason for this odd behavior has to do with a simple optimization technique. If the WPF runtime were to call the set block of the Height property, it would have to perform runtime reflection to figure out where the DependencyProperty field (specified by the first argument to SetValue()) is located, reference it in memory, and so forth. The same story holds true if you were to write XAML that retrieves the value of the Height property\u2014GetValue() would be called directly.<br \/>\nSince this is the case, why do you need to build this CLR wrapper at all? Well, WPF XAML does not allow you to call functions in markup, so the following markup would be an error:<\/p>\n<p><!-- Nope! Can't call methods in WPF XAML! --><br \/>\n<Button x:Name=\"myButton\" this.SetValue(\"100\") ...\/><\/p>\n<p>In effect, when you set or get a value in markup using the CLR wrapper, think of it as a way to tell the WPF runtime \u201cHey! Go call GetValue()\/SetValue() for me since I can\u2019t directly do it in markup!\u201d Now, what if you call the CLR wrapper in code like so:<\/p>\n<p>Button b = new Button(); b.Height = 10;<\/p>\n<p>In this case, if the set block of the Height property contained code other than a call to SetValue(), it<br \/>\nwould execute because the WPF XAML parser optimization is not involved.<br \/>\nThe basic rule to remember is that when registering a dependency property, use a ValidateValueCallback delegate to point to a method that performs the data validation. This ensures that the correct behavior will occur, regardless of whether you use XAML or code to get\/set a dependency property.<\/p>\n<p>Building a Custom Dependency Property<br \/>\nIf you have a slight headache at this point in the chapter, this is a perfectly normal response. Building dependency properties can take some time to get used to. However, for better or worse, it is part of the process of building many custom WPF controls, so let\u2019s take a look at how to build a dependency property.<br \/>\nBegin by creating a new WPF application named CustomDependencyProperty. Now, using the Project menu, activate the Add User Control (WPF) menu option, and create a control named ShowNumberControl.xaml.<\/p>\n<p>\u25a0Note you will learn more details about the WPF UserControl in Chapter 28, so just follow along as shown for now.<\/p>\n<p>Just like a window, WPF UserControl types have a XAML file and a related code file. Update the XAML of your user control to define a single Label control in the Grid, like so:<\/p>\n<p><UserControl x:Class=\"CustomDepProp.ShowNumberControl\" xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\" xmlns:d=\"http:\/\/schemas.microsoft.com\/expression\/blend\/2008\" xmlns:local=\"clr-namespace: CustomDependencyProperty\" mc:Ignorable=\"d\"\nd:DesignHeight=\"300\" d:DesignWidth=\"300\"><br \/>\n<Grid><br \/>\n<Label x:Name=\"numberDisplay\" Height=\"50\" Width=\"200\" Background=\"LightBlue\"\/><br \/>\n<\/Grid><br \/>\n<\/UserControl><\/p>\n<p>In the code file of this custom control, create a normal, everyday .NET Core property that wraps an int<br \/>\nand sets the Content property of the Label with the new value, as follows:<\/p>\n<p>public partial class ShowNumberControl : UserControl<br \/>\n{<br \/>\npublic ShowNumberControl()<br \/>\n{<br \/>\nInitializeComponent();<br \/>\n}<\/p>\n<p>\/\/ A normal, everyday .NET property.<br \/>\nprivate int _currNumber = 0; public int CurrentNumber<br \/>\n{<br \/>\nget => _currNumber; set<br \/>\n{<br \/>\n_currNumber = value;<br \/>\nnumberDisplay.Content = CurrentNumber.ToString();<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>Now, update the XAML definition in MainWindow.xml to declare an instance of your custom control within a StackPanel layout manger. Because your custom control is not part of the core WPF assembly stack, you will need to define a custom XML namespace that maps to your control. Here is the required markup:<\/p>\n<p><Window x:Class=\"CustomDepPropApp.MainWindow\" xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\" xmlns:d=\"http:\/\/schemas.microsoft.com\/expression\/blend\/2008\" xmlns:myCtrls=\"clr-namespace: CustomDependencyProperty\" xmlns:local=\"clr-namespace: CustomDependencyProperty\" mc:Ignorable=\"d\"\nTitle=\"Simple Dependency Property App\" Height=\"450\" Width=\"450\"\nWindowStartupLocation=\"CenterScreen\"><br \/>\n<StackPanel><br \/>\n<myCtrls:ShowNumberControl HorizontalAlignment=\"Left\" x:Name=\"myShowNumberCtrl\" CurrentNumber=\"100\"\/><br \/>\n<\/StackPanel><br \/>\n<\/Window><\/p>\n<p>As you can see, the Visual Studio designer appears to correctly display the value that you set in the<br \/>\nCurrentNumber property (see Figure 26-23).<\/p>\n<p>Figure 26-23. It appears your property works as expected<\/p>\n<p>However, what if you want to apply an animation object to the CurrentNumber property so that the value changes from 100 to 200 over a period of 10 seconds? If you wanted to do so in markup, you might update your myCtrls:ShowNumberControl scope as so:<\/p>\n<p><myCtrls:ShowNumberControl x:Name=\"myShowNumberCtrl\" CurrentNumber=\"100\"><br \/>\n<myCtrls:ShowNumberControl.Triggers><br \/>\n<EventTrigger RoutedEvent = \"myCtrls:ShowNumberControl.Loaded\"><br \/>\n<EventTrigger.Actions><br \/>\n<BeginStoryboard><br \/>\n<Storyboard TargetProperty = \"CurrentNumber\"><br \/>\n<Int32Animation From = \"100\" To = \"200\" Duration = \"0:0:10\"\/><br \/>\n<\/Storyboard><br \/>\n<\/BeginStoryboard><br \/>\n<\/EventTrigger.Actions><br \/>\n<\/EventTrigger><br \/>\n<\/myCtrls:ShowNumberControl.Triggers><br \/>\n<\/myCtrls:ShowNumberControl><\/p>\n<p>If you run your application, the animation object cannot find a proper target, and an exception is thrown. The reason is that the CurrentNumber property has not been registered as a dependency property! To fix matters, return to the code file of your custom control, and completely comment out the current property logic (including the private backing field).<br \/>\nNow, add the following code to create CurrentNumber as a dependency property:<\/p>\n<p>public int CurrentNumber<br \/>\n{<br \/>\nget => (int)GetValue(CurrentNumberProperty); set => SetValue(CurrentNumberProperty, value);<br \/>\n}<br \/>\npublic static readonly DependencyProperty CurrentNumberProperty = DependencyProperty.Register(\"CurrentNumber\",<br \/>\ntypeof(int), typeof(ShowNumberControl), new UIPropertyMetadata(0));<\/p>\n<p>This is similar to what you saw in the implementation of the Height property; however, the code snippet registers the property inline rather than within a static constructor (which is fine). Also notice that a UIPropertyMetadata object is used to define the default value of the integer (0) rather than the more complex FrameworkPropertyMetadata object. This is the simplest version of CurrentNumber as a dependency property.<\/p>\n<p>Adding a Data Validation Routine<br \/>\nAlthough you now have a dependency property named CurrentNumber (and the exception is no longer being thrown), you still won\u2019t see your animation take hold. The next adjustment you might want to make is to specify a function to call to perform some data validation logic. For this example, assume that you need to ensure that the value of CurrentNumber is between 0 and 500.<br \/>\nTo do so, add a final argument to the DependencyProperty.Register() method of type<br \/>\nValidateValueCallback, which points to a method named ValidateCurrentNumber.<\/p>\n<p>ValidateValueCallback is a delegate that can only point to methods returning bool and take an object as the only argument. This object represents the new value that is being assigned. Implement ValidateCurrentNumber to return true or false, if the incoming value is within the expected range.<\/p>\n<p>public static readonly DependencyProperty CurrentNumberProperty = DependencyProperty.Register(\"CurrentNumber\",<br \/>\ntypeof(int), typeof(ShowNumberControl), new UIPropertyMetadata(100),<br \/>\nnew ValidateValueCallback(ValidateCurrentNumber));<\/p>\n<p>\/\/ Just a simple rule. Value must be between 0 and 500. public static bool ValidateCurrentNumber(object value) =><br \/>\nConvert.ToInt32(value) >= 0 && Convert.ToInt32(value) <= 500;\n\nResponding to the Property Change\nSo, now you have a valid number but still no animation. The final change you need to make is to specify a second argument to the constructor of UIPropertyMetadata, which is a PropertyChangedCallback object. This delegate can point to any method that takes a DependencyObject as the first parameter and a DependencyPropertyChangedEventArgs as the second. First, update your code as so:\n\n\/\/ Note the second param of UIPropertyMetadata constructor.\npublic static readonly DependencyProperty CurrentNumberProperty = DependencyProperty.Register(\"CurrentNumber\", typeof(int), typeof(ShowNumberControl), new UIPropertyMetadata(100, new PropertyChangedCallback(CurrentNumberChanged)),\nnew ValidateValueCallback(ValidateCurrentNumber));\n\nWithin the CurrentNumberChanged() method, your ultimate goal is to change the Content of the\nLabel to the new value assigned by the CurrentNumber property. You have one big problem, however: the CurrentNumberChanged() method is static, as it must be to work with the static DependencyProperty object. So, how are you supposed to gain access to the Label for the current instance of ShowNumberControl?\nThat reference is contained in the first DependencyObject parameter. You can find the new value using the incoming event arguments. Here is the necessary code that will change the Content property of the Label:\n\nprivate static void CurrentNumberChanged(DependencyObject depObj,   DependencyPropertyChangedEventArgs args)\n{\n\/\/ Cast the DependencyObject into ShowNumberControl.\nShowNumberControl c = (ShowNumberControl)depObj;\n\/\/ Get the Label control in the ShowNumberControl.\nLabel theLabel = c.numberDisplay;\n\/\/ Set the Label with the new value.\ntheLabel.Content = args.NewValue.ToString();\n}\n\nWhew! That was a long way to go just to change the output of a label. The benefit is that your CurrentNumber dependency property can now be the target of a WPF style, an animation object, the target of a data-binding operation, and so forth. If you run your application once again, you should now see the value change during execution.\n\nThat wraps up your look at WPF dependency properties. While I hope you have a much better idea about what these constructs allow you to do and have a better idea of how to make your own, please be aware that there are many details I have not covered here.\nIf you find yourself in a position where you are building a number of custom controls that support custom properties, please look up the topic \u201cProperties\u201d under the \u201cWPF Fundamentals\u201d node of the\n.NET Framework 4.7 SDK documentation. In it you will find many more examples of building dependency properties, attached properties, various ways to configure property metadata, and a slew of other details.\n\nSummary\nThis chapter examined several aspects of WPF controls, beginning with an overview of the control toolkit and the role of layout managers (panels). The first example gave you a chance to build a simple word processor application that illustrated the integrated spell-checking functionality of WPF, as well as how to build a main window with menu systems, status bars, and toolbars.\nMore importantly, you examined how to use WPF commands. Recall that you can attach these control- agnostic events to a UI element or an input gesture to inherit out-of-the-box services automatically (e.g., clipboard operations).\nYou also learned quite a bit about building complex UIs in XAML, and you learned about the WPF Ink API at the same time. You also received an introduction to WPF data-binding operations, including how to use the WPF DataGrid class to display data from your custom AutoLot database.\nFinally, you investigated how WPF places a unique spin on traditional .NET Core programming primitives, specifically properties and events. As you have seen, a dependency property allows you to build a property that can integrate within the WPF set of services (animations, data bindings, styles, etc.). On a related note, routed events provide a way for an event to flow up or down a tree of markup.\n<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CHAPTER 26 WPF Controls, Layouts, Events, and Data Binding Chapter 25 provided a foundation for the WPF programming model, including an examination of the Window and Application classes, the grammar of XAML, and the use of code files. Chapter 25 also introduced you to the process of building WPF applications using the designers of Visual [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[22],"class_list":["post-350","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-pro-csharp10-with-net6"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=350"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/350\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}