{"id":354,"date":"2023-10-20T13:47:38","date_gmt":"2023-10-20T05:47:38","guid":{"rendered":"https:\/\/miie.net\/?p=354"},"modified":"2023-10-20T13:47:38","modified_gmt":"2023-10-20T05:47:38","slug":"pro-c10-chapter-28-wpf-resources-animations-styles-and-templates","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=354","title":{"rendered":"Pro C#10 CHAPTER 28 WPF Resources, Animations, Styles, and Templates"},"content":{"rendered":"<p>CHAPTER 28<\/p>\n<p>WPF Resources, Animations, Styles, and Templates<\/p>\n<p>This chapter introduces you to three important (and interrelated) topics that will deepen your understanding of the Windows Presentation Foundation (WPF) API. The first order of business is to learn the role of logical resources. As you will see, the logical resource (also known as an object resource) system is a way to name and refer to commonly used objects within a WPF application. While logical resources are often authored in XAML, they can also be defined in procedural code.<br \/>\nNext, you will learn how to define, execute, and control an animation sequence. Despite what you might think, WPF animations are not limited to video game or multimedia applications. Under the WPF API, animations can be as subtle as making a button appear to glow when it receives focus or expanding the size of a selected row in a DataGrid. Understanding animations is a key aspect of building custom control templates (as you will see later in this chapter).<br \/>\nYou will then explore the role of WPF styles and templates. Much like a web page that uses CSS or the ASP.NET theme engine, a WPF application can define a common look and feel for a set of controls. You can define these styles in markup and store them as object resources for later use, and you can also apply them dynamically at runtime. The final example will teach you how to build custom control templates.<\/p>\n<p>Understanding the WPF Resource System<br \/>\nYour first task is to examine the topic of embedding and accessing application resources. WPF supports two flavors of resources. The first is a binary resource, and this category typically includes items most programmers consider to be resources in the traditional sense (embedded image files or sound clips, icons used by the application, etc.).<br \/>\nThe second flavor, termed object resources or logical resources, represents a named .NET object that can be packaged and reused throughout the application. While any .NET object can be packaged as an object resource, logical resources are particularly helpful when working with graphical data of any sort, given that you can define commonly used graphic primitives (brushes, pens, animations, etc.) and refer to them when required.<\/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_28\"><a href=\"https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_28\"><a href=\"https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_28\">https:\/\/doi.org\/10.1007\/978-1-4842-7869-7_28<\/a><\/a><\/a><\/p>\n<p>1233<\/p>\n<p>Working with Binary Resources<br \/>\nBefore getting to the topic of object resources, let\u2019s quickly examine how to package up binary resources such as icons or image files (e.g., company logos or images for an animation) into your applications. If you would like to follow along, create a new WPF application named BinaryResourcesApp. Update the markup for your initial window to handle the Window Loaded event and to use a DockPanel as the layout root, like so:<\/p>\n<p>&lt;Window x:Class=&quot;BinaryResourcesApp.MainWindow&quot;<\/p>\n<p><!-- Omitted for brevity --><\/p>\n<p>Title=&quot;Fun with Binary Resources&quot; Height=&quot;500&quot; Width=&quot;649&quot; Loaded=&quot;MainWindow_OnLoaded&quot;&gt;<\/p>\n<p><DockPanel LastChildFill=\"True\"><br \/>\n<\/DockPanel><\/p>\n<p><\/Window><\/p>\n<p>Now, let\u2019s say your application needs to display one of three image files inside part of the window, based on user input. The WPF Image control can be used to display not only a typical image file (<em>.bmp,<br \/>\n<\/em>.gif, <em>.ico, <\/em>.jpg, <em>.png, <\/em>.wdp, or *.tiff) but also data in a DrawingImage (as you saw in Chapter 27). You might build a UI for your window that supports a DockPanel containing a simple toolbar with Next and Previous buttons. Below this toolbar you can place an Image control, which currently does not have a value set to the Source property, like so:<\/p>\n<p><DockPanel LastChildFill=\"True\"><br \/>\n<ToolBar Height=\"60\" Name=\"picturePickerToolbar\" DockPanel.Dock=\"Top\"><br \/>\n<Button x:Name=\"btnPreviousImage\" Height=\"40\" Width=\"100\" BorderBrush=\"Black\" Margin=\"5\" Content=\"Previous\" Click=\"btnPreviousImage_Click\"\/><br \/>\n<Button x:Name=\"btnNextImage\" Height=\"40\" Width=\"100\" BorderBrush=\"Black\" Margin=\"5\" Content=\"Next\" Click=\"btnNextImage_Click\"\/><br \/>\n<\/ToolBar><br \/>\n<!-- We will fill this Image in code. --><br \/>\n<Border BorderThickness=\"2\" BorderBrush=\"Green\"><br \/>\n<Image x:Name=\"imageHolder\" Stretch=\"Fill\" \/><br \/>\n<\/Border><br \/>\n<\/DockPanel><\/p>\n<p>Next, add the following empty event handlers:<\/p>\n<p>private void MainWindow_OnLoaded( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n}<br \/>\nprivate void btnPreviousImage_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n}<br \/>\nprivate void btnNextImage_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n}<\/p>\n<p>When the window loads, images will be added to a collection that the Next and Previous buttons will cycle through. Now that the application framework is in place, let\u2019s examine the different options for implementing this.<\/p>\n<p>Including Loose Resource Files in a Project<br \/>\nOne option is to ship your image files as a set of loose files in a subdirectory of the application install path. Start by adding a new folder (named Images) to your project. Into this folder add some images by right- clicking and selecting Add \u27a4 Existing Item. Make sure to change the file filter in the Add Existing Item dialog to <em>.<\/em> so the image files show. You can add your own image files or use the three image files named Deer. jpg, Dogs.jpg, and Welcome.jpg from the downloadable code.<\/p>\n<p>Configuring the Loose Resources<br \/>\nTo copy the content of your \\Images folder to the \\bin\\Debug folder when the project builds, begin by selecting all the images in Solution Explorer. Now, with these images still selected, right-click and select Properties to open the Properties window. Set the Build Action property to Content, and set the Copy to Output Directory property to \u201cCopy always\u201d (see Figure 28-1).<\/p>\n<p>Figure 28-1. Configuring the image data to be copied to your output directory<\/p>\n<p>\u25a0Note you could also select Copy if newer, which will save you time if you\u2019re building large projects with a lot of content. For this example, \u201cCopy always\u201d works.<\/p>\n<p>If you build your project, you can now click the Show All Files button of Solution Explorer and view the copied Image folder under your \\bin\\Debug directory (you might need to click the Refresh button).<\/p>\n<p>Programmatically Loading an Image<br \/>\nWPF provides a class named BitmapImage, which is part of the System.Windows.Media.Imaging namespace.<br \/>\nThis class allows you to load data from an image file whose location is represented by a System.Uri object. Add a List<BitmapImage> to hold the images, as well as an int to store the index of the currently displayed image.<\/p>\n<p>\/\/ A List of BitmapImage files.<br \/>\nList<BitmapImage> _images=new List<BitmapImage>();<br \/>\n\/\/ Current position in the list.<br \/>\nprivate int _currImage=0;<\/p>\n<p>In the Loaded event of your window, fill the list of images and then set the Image control source to the first image in the list.<\/p>\n<p>private void MainWindow_OnLoaded( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\ntry<br \/>\n{<br \/>\nstring path=Environment.CurrentDirectory;<br \/>\n\/\/ Load these images from disk when the window loads.<br \/>\n_images.Add(new BitmapImage(new Uri($@&quot;{path}\\Images\\Deer.jpg&quot;)));<br \/>\n_images.Add(new BitmapImage(new Uri($@&quot;{path}\\Images\\Dogs.jpg&quot;)));<br \/>\n_images.Add(new BitmapImage(new Uri($@&quot;{path}\\Images\\Welcome.jpg&quot;)));<br \/>\n\/\/ Show first image in the List.<br \/>\nimageHolder.Source=_images[_currImage];<br \/>\n}<br \/>\ncatch (Exception ex)<br \/>\n{<br \/>\nMessageBox.Show(ex.Message);<br \/>\n}<br \/>\n}<\/p>\n<p>Next, implement the previous and next handlers to loop through the images. If the user gets to the end of the list, start them back at the beginning and vice versa.<\/p>\n<p>private void btnPreviousImage_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\nif (--_currImage &lt; 0)<br \/>\n{<br \/>\n_currImage=_images.Count - 1;<br \/>\n}<br \/>\nimageHolder.Source=_images[_currImage];<br \/>\n}<br \/>\nprivate void btnNextImage_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\nif (++_currImage &gt;=_images.Count)<br \/>\n{<br \/>\n_currImage=0;<br \/>\n}<br \/>\nimageHolder.Source=_images[_currImage];<br \/>\n}<\/p>\n<p>At this point, you can run your program and flip through each picture.<\/p>\n<p>Embedding Application Resources<br \/>\nIf you would rather configure your image files to be compiled directly into your .NET assembly as binary resources, select the image files in Solution Explorer (in the \\Images folder, not in the \\bin\\Debug\\Images folder). Change the Build Action property to Resource and set the Copy to Output Directory property to \u201cDo not copy\u201d (see Figure 28-2).<\/p>\n<p>Figure 28-2. Configuring the images to be embedded resources<\/p>\n<p>Now, using Visual Studio\u2019s Build menu, select the Clean Solution option to wipe out the current contents of \\bin\\Debug\\Images and then rebuild your project. Refresh Solution Explorer and observe the absence of data in your \\bin\\Debug\\Images directory. With the current build options, your graphical data is no longer copied to the output folder and is now embedded within the assembly itself. This ensures that the resources exist but also increases the size of your compiled assembly.<br \/>\nYou need to modify your code to load these images into your list by extracting them from the compiled assembly.<\/p>\n<p>\/\/ Extract from the assembly and then load images<br \/>\n_images.Add(new BitmapImage(new Uri(@&quot;\/Images\/Deer.jpg&quot;, UriKind.Relative)));<br \/>\n_images.Add(new BitmapImage(new Uri(@&quot;\/Images\/Dogs.jpg&quot;, UriKind.Relative)));<br \/>\n_images.Add(new BitmapImage(new Uri(@&quot;\/Images\/Welcome.jpg&quot;, UriKind.Relative)));<\/p>\n<p>In this case, you no longer need to determine the installation path and can simply list the resources by name, which considers the name of the original subdirectory. Also notice, when you create your Uri objects, you specify a UriKind value of Relative. At this point, your executable is a stand-alone entity that can be run from any location on the machine because all the compiled data is within the binary.<\/p>\n<p>Working with Object (Logical) Resources<br \/>\nWhen you are building a WPF application, it is common to define a blurb of XAML to use in multiple locations within a window or perhaps across multiple windows or projects. For example, say you have created the perfect linear gradient brush, which consists of ten lines of markup. Now, you want to use that brush as the background color for every Button control in the project (which consists of eight windows) for a total of 16 Button controls.<\/p>\n<p>The worst thing you could do is to copy and paste the XAML to every control. Clearly, this would be a nightmare to maintain, as you would need to make numerous changes any time you wanted to tweak the look and feel of the brush.<br \/>\nThankfully, object resources allow you to define a blob of XAML, give it a name, and store it in a fitting dictionary for later use. Like a binary resource, object resources are often compiled into the assembly that requires them. However, you do not need to tinker with the Build Action property to do so. If you place your XAML into the correct location, the compiler will take care of the rest.<br \/>\nWorking with object resources is a big part of WPF development. As you will see, object resources can be far more complex than a custom brush. You can define a XAML-based animation, a 3D rendering, a custom control style, a data template, a control template, and more, and package each one as a reusable resource.<\/p>\n<p>The Role of the Resources Property<br \/>\nAs mentioned, object resources must be placed in a fitting dictionary object to be used across an application. As it stands, every descendant of FrameworkElement supports a Resources property. This property encapsulates a ResourceDictionary object that contains the defined object resources. The ResourceDictionary can hold any type of item because it operates on System.Object types and may be manipulated via XAML or procedural code.<br \/>\nIn WPF, all controls, Windows, Pages (used when building navigation applications), and UserControls extend FrameworkElement, so just about all widgets provide access to a ResourceDictionary. Furthermore, the Application class, while not extending FrameworkElement, supports an identically named Resources property for the same purpose.<\/p>\n<p>Defining Window-wide Resources<br \/>\nTo begin exploring the role of object resources, create a new WPF application named ObjectResourcesApp and change the initial Grid to a horizontally aligned StackPanel layout manager. Into this StackPanel, define two Button controls like so (you really do not need much to illustrate the role of object resources, so this will do):<\/p>\n<p><StackPanel Orientation=\"Horizontal\"><br \/>\n<Button Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"OK\" FontSize=\"20\"\/><br \/>\n<Button Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"Cancel\" FontSize=\"20\"\/><br \/>\n<\/StackPanel><\/p>\n<p>Now, click the OK button and set the Background color property to a custom brush type using the integrated Brushes editor (discussed in Chapter 27). After you have done so, notice how the brush is embedded within the scope of the <Button> and <\/Button> tags, as shown here:<\/p>\n<p><Button Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"OK\" FontSize=\"20\"><br \/>\n<Button.Background><br \/>\n<RadialGradientBrush><br \/>\n<GradientStop Color=\"#FFC44EC4\" Offset=\"0\" \/><br \/>\n<GradientStop Color=\"#FF829CEB\" Offset=\"1\" \/><br \/>\n<GradientStop Color=\"#FF793879\" Offset=\"0.669\" \/><br \/>\n<\/RadialGradientBrush><br \/>\n<\/Button.Background><br \/>\n<\/Button><\/p>\n<p>To allow the Cancel button to use this brush as well, you should promote the scope of your<\/p>\n<p><RadialGradientBrush> to a parent element\u2019s resource dictionary. For example, if you move it to the<br \/>\n<StackPanel>, both buttons can use the same brush because they are child elements of the layout manager. Even better, you could package the brush into the resource dictionary of the Window itself so the window\u2019s content can use it.<br \/>\nWhen you need to define a resource, you use the property-element syntax to set the Resources property of the owner. You also give the resource item an x:Key value, which will be used by other parts of the window when they want to refer to the object resource. Be aware that x:Key and x:Name are not the same! The x:Name attribute allows you to gain access to the object as a member variable in your code file, while the x:Key attribute allows you to refer to an item in a resource dictionary.<br \/>\nVisual Studio allows you to promote a resource to a higher scope using its respective Properties window.<br \/>\nTo do so, first identify the property that has the complex object you want to package as a resource (the Background property, in this example). To the right of the property is a small square that, when clicked, will open a pop-up menu. From it, select the Convert to New Resource option (see Figure 28-3).<\/p>\n<p>Figure 28-3. Moving a complex object into a resource container<\/p>\n<p>You are asked to name your resource (myBrush) and specify where to place it. For this example, leave the default selection of the current document (see Figure 28-4).<\/p>\n<p>Figure 28-4. Naming the object resource<\/p>\n<p>When you are done, you will see the brush has been moved inside the Window.Resources tag.<\/p>\n<p><Window.Resources><br \/>\n<RadialGradientBrush x:Key=\"myBrush\"><br \/>\n<GradientStop Color=\"#FFC44EC4\" Offset=\"0\" \/><br \/>\n<GradientStop Color=\"#FF829CEB\" Offset=\"1\" \/><br \/>\n<GradientStop Color=\"#FF793879\" Offset=\"0.669\" \/><br \/>\n<\/RadialGradientBrush><br \/>\n<\/Window.Resources><\/p>\n<p>And the Button control\u2019s Background has been updated to use a new resource.<\/p>\n<p><Button Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"OK\" FontSize=\"20\" Background=\"{DynamicResource myBrush}\"\/><\/p>\n<p>The Create Resource Wizard creates the new resource as a DynamicResource. You will learn about<br \/>\nDynamicResources later in the text, but for now, change it to a StaticResource, like this:<\/p>\n<p><Button Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"OK\" FontSize=\"20\" Background=\"{StaticResource myBrush}\"\/><\/p>\n<p>To see the benefit, update the Cancel Button\u2019s Background property to the same StaticResource, and you can see the reuse in action.<\/p>\n<p><Button Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"Cancel\" FontSize=\"20\" Background=\"{StaticResource myBrush}\"\/><\/p>\n<p>The {StaticResource} Markup Extension<br \/>\nThe {StaticResource} markup extension applies the resource only once (on initialization) and stays \u201cconnected\u201d to the original object during the life of the application. Some properties (such as gradient stops) will update, but if you create a new Brush, for example, the control will not be updated. To see this in action, add a Name and Click event handler to each Button control, as follows:<\/p>\n<p><Button Name=\"Ok\" Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"OK\" FontSize=\"20\" Background=\"{StaticResource myBrush}\" Click=\"Ok_OnClick\"\/><br \/>\n<Button Name=\"Cancel\" Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"Cancel\" FontSize=\"20\" Background=\"{StaticResource myBrush}\" Click=\"Cancel_OnClick\"\/><\/p>\n<p>Next, add the following code to the Ok_OnClick() event handler:<\/p>\n<p>private void Ok_OnClick(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Get the brush and make a change.<br \/>\nvar b=(RadialGradientBrush)Resources[\"myBrush\"]; b.GradientStops[1]=new GradientStop(Colors.Black, 0.0);<br \/>\n}<\/p>\n<p>\u25a0Note you are using the Resources indexer to locate a resource by name here. Be aware, however, that this will throw a runtime exception if the resource cannot be found. you could also use the<br \/>\nTryFindResource() method, which will not throw a runtime error; it will simply return null if the specified resource cannot be located.<\/p>\n<p>When you run the program and click the OK Button, you see the gradients change appropriately. Now add the following code to the Cancel_OnClick() event handler:<\/p>\n<p>private void Cancel_OnClick(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n\/\/ Put a totally new brush into the myBrush slot. Resources[\"myBrush\"]=new SolidColorBrush(Colors.Red);<br \/>\n}<\/p>\n<p>Run the program again, click the Cancel Button, and nothing happens!<\/p>\n<p>The {DynamicResource} Markup Extension<br \/>\nIt is also possible for a property to use the DynamicResource markup extension. To see the difference, change the markup for the Cancel Button to the following:<\/p>\n<p><Button Name=\"Cancel\" Margin=\"25\" Height=\"200\" Width=\"200\" Content=\"Cancel\"\nFontSize=\"20\" Background=\"{DynamicResource myBrush}\" Click=\"Cancel_OnClick\"\/><\/p>\n<p>This time, when you click the Cancel Button, the background for the Cancel Button changes, but the background for the OK Button remains the same. This is because the {DynamicResource} markup extension can detect whether the underlying keyed object has been replaced with a new object. As you might guess,<\/p>\n<p>this requires some extra runtime infrastructure, so you should typically stick to using {StaticResource} unless you know you have an object resource that will be swapped with a different object at runtime and you want all items using that resource to be informed.<\/p>\n<p>Application-Level Resources<br \/>\nWhen you have object resources in a window\u2019s resource dictionary, all items in the window are free to make use of it, but other windows in the application cannot. The solution to share resources across your application is to define the object resource at the application level, rather than at the window level. There is no way to automate this within Visual Studio, so simply cut the current brush object out of the <Windows. Resources> scope and place it in the <Application.Resources> scope in your App.xaml file.<br \/>\nNow any additional window or control in your application is free to use this same brush. If you want to set the Background property for a control, the application-level resources are available for selection, as shown in Figure 28-5.<\/p>\n<p>Figure 28-5. Applying application-level resources<\/p>\n<p>\u25a0Note placing the resources at the application level and assigning it to a control\u2019s property will freeze the resource, preventing changing values at runtime. the resource can be cloned, and the clone can be updated.<\/p>\n<p>Defining Merged Resource Dictionaries<br \/>\nApplication-level resources are a quite often good enough, but they do not help reuse across projects. In this case, you want to define what is known as a merged resource dictionary. Think of it as a class library for WPF resources; it is nothing more than a XAML file that contains a collection of resources. A single project can have as many of these files as required (one for brushes, one for animations, etc.), each of which can be inserted using the Add New Item dialog box activated via the Project menu (see Figure 28-6).<\/p>\n<p>Figure 28-6. Inserting a new merged resource dictionary<\/p>\n<p>In the new MyBrushes.xaml file, cut the current resources in the Application.Resources scope and move them into your dictionary, like so:<\/p>\n<p><ResourceDictionary xmlns=http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation xmlns:local=\"clr-namespace:ObjectResourcesApp\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"><br \/>\n<RadialGradientBrush x:Key=\"myBrush\"><br \/>\n<GradientStop Color=\"#FFC44EC4\" Offset=\"0\" \/><br \/>\n<GradientStop Color=\"#FF829CEB\" Offset=\"1\" \/><br \/>\n<GradientStop Color=\"#FF793879\" Offset=\"0.669\" \/><br \/>\n<\/RadialGradientBrush><br \/>\n<\/ResourceDictionary><\/p>\n<p>Even though this resource dictionary is part of your project, all resource dictionaries must be merged (typically at the application level) into an existing resource dictionary to be used. To do this, use the following format in the App.xaml file (note that multiple resource dictionaries can be merged by adding multiple <ResourceDictionary> elements within the <ResourceDictionary.MergedDictionaries> scope):<\/p>\n<p><Application.Resources><br \/>\n<ResourceDictionary><br \/>\n<ResourceDictionary.MergedDictionaries><\/p>\n<p><ResourceDictionary Source=\"MyBrushes.xaml\"\/><br \/>\n<\/ResourceDictionary.MergedDictionaries><br \/>\n<\/ResourceDictionary><br \/>\n<\/Application.Resources><\/p>\n<p>The problem with this approach is that each resource file must be added to each project that needs the resources. A better approach for sharing resources is to define a .NET class library to share between projects, which you will do next.<\/p>\n<p>Defining a Resource-Only Assembly<br \/>\nThe easiest way to build a resource-only assembly is to begin with a WPF User Control Library project. Add such a project (named MyBrushesLibrary) to your current solution via the Add \u27a4 New Project menu option of Visual and add a project reference to it from the ObjectResourcesApp project.<br \/>\nNow, delete the UserControl1.xaml file from the project. Next, drag and drop the MyBrushes.xaml file into your MyBrushesLibrary project and delete it from the ObjectResourcesApp project. Finally, open MyBrushes.xaml in the MyBrushesLibrary project and change the x:local namespace in the file to clr- namespace:MyBrushesLibrary. Your MyBrushes.xaml file should look like this:<\/p>\n<p><ResourceDictionary xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\" xmlns:local=\"clr-namespace:MyBrushesLibrary\"><br \/>\n<RadialGradientBrush x:Key=\"myBrush\"><br \/>\n<GradientStop Color=\"#FFC44EC4\" Offset=\"0\" \/><br \/>\n<GradientStop Color=\"#FF829CEB\" Offset=\"1\" \/><br \/>\n<GradientStop Color=\"#FF793879\" Offset=\"0.669\" \/><br \/>\n<\/RadialGradientBrush><br \/>\n<\/ResourceDictionary><\/p>\n<p>Compile your User Control Library project. Now, merge these binary resources into the application- level resource dictionary of the ObjectResourcesApp project. Doing so, however, requires some rather funky syntax, shown here:<\/p>\n<p><Application.Resources><br \/>\n<ResourceDictionary><br \/>\n<ResourceDictionary.MergedDictionaries><br \/>\n<!-- The syntax is \/NameOfAssembly;Component\/NameOfXamlFileInAssembly.xaml --><br \/>\n<ResourceDictionary Source=\"\/MyBrushesLibrary;Component\/MyBrushes.xaml\"\/><br \/>\n<\/ResourceDictionary.MergedDictionaries><br \/>\n<\/ResourceDictionary><br \/>\n<\/Application.Resources><\/p>\n<p>First, be aware that this string is space sensitive. If you have extra whitespace around your semicolon or forward slashes, you will generate errors. The first part of the string is the friendly name of the external library (no file extension). After the semicolon, type in the word Component followed by the name of the compiled binary resource, which will be identical to the original XAML resource dictionary.<br \/>\nThat wraps up the examination of WPF\u2019s resource management system. You will make good use of these techniques for most (if not all) of your applications. Next up, let\u2019s investigate the integrated animation API of Windows Presentation Foundation.<\/p>\n<p>Understanding WPF\u2019s Animation Services<br \/>\nIn addition to the graphical rendering services you examined in Chapter 27, WPF supplies a programming interface to support animation services. The term animation may bring to mind visions of spinning company logos, a sequence of rotating image resources (to provide the illusion of movement), text bouncing across the screen, or specific types of programs such as video games or multimedia applications.<br \/>\nWhile WPF\u2019s animation APIs could certainly be used for such purposes, animation can be used any time you want to give an application additional flair. For example, you could build an animation for a button on<br \/>\na screen that magnifies slightly when the mouse cursor hovers within its boundaries (and shrinks back once the mouse cursor moves beyond the boundaries). Or you could animate a window so that it closes using a particular visual appearance, such as slowly fading into transparency. A more business application\u2013centric use is to fade in error messages on an application screen to improve the user experience. In fact, WPF\u2019s animation support can be used within any sort of application (a business application, multimedia programs, video games, etc.) whenever you want to provide a more engaging user experience.<br \/>\nAs with many other aspects of WPF, the notion of building animations is nothing new. What is new is that, unlike other APIs you might have used in the past (including Windows Forms), developers are not required to author the necessary infrastructure by hand. Under WPF, there is no need to create the<br \/>\nbackground threads or timers used to advance the animation sequence, define custom types to represent the animation, erase and redraw images, or bother with tedious mathematical calculations. Like other aspects<br \/>\nof WPF, you can build an animation entirely using XAML, entirely using C# code, or using a combination of the two.<\/p>\n<p>\u25a0Note Visual studio has no support for authoring animations using Gui animation tools. if you author an animation with Visual studio, you will do so by typing in the Xaml directly. however, Blend for Visual studio (the companion product that ships with Visual studio 2019) does indeed have a built-in animation editor that can simplify your life a good deal.<\/p>\n<p>The Role of the Animation Class Types<br \/>\nTo understand WPF\u2019s animation support, you must begin by examining the animation classes within the System.Windows.Media.Animation namespace of PresentationCore.dll. Here, you will find more than 100 different class types that are named using the Animation token.<br \/>\nThese classes can be placed into one of three broad categories. First, any class that follows the name convention DataTypeAnimation (ByteAnimation, ColorAnimation, DoubleAnimation, Int32Animation, etc.) allows you to work with linear interpolation animations. This enables you to change a value smoothly over time from a start value to a final value.<br \/>\nNext, the classes that follow the naming convention DataTypeAnimationUsingKeyFrames (StringAnimationUsingKeyFrames, DoubleAnimationUsingKeyFrames, PointAnimationUsingKeyFrames, etc.) represent \u201ckey frame animations,\u201d which allow you to cycle through a set of defined values over a period of time. For example, you could use key frames to change the caption of a button by cycling through a series of individual characters.<br \/>\nFinally, classes that follow the DataTypeAnimationUsingPath naming convention (DoubleAnimationUsingPath, PointAnimationUsingPath, among others) are path-based animations that allow you to animate objects to move along a path you define. By way of an example, if you were building a GPS application, you could use a path-based animation to move an item along the quickest travel route to the user\u2019s destination.<\/p>\n<p>Now, obviously, these classes are not used to somehow provide an animation sequence directly to a variable of a particular data type (after all, how exactly could you animate the value \u201c9\u201d using an Int32Animation?).<br \/>\nFor example, consider the Label type\u2019s Height and Width properties, both of which are dependency properties wrapping a double. If you wanted to define an animation that would increase the height of a label over a time span, you could connect a DoubleAnimation object to the Height property and allow WPF to<br \/>\ntake care of the details of performing the actual animation itself. By way of another example, if you wanted to transition the color of a brush type from green to yellow over a period of five seconds, you could do so using the ColorAnimation type.<br \/>\nTo be clear, these Animation classes can be connected to any dependency property of a given object that<br \/>\nmatches the underlying types. As explained in Chapter 26, dependency properties are a specialized form of property required by many WPF services including animation, data binding, and styles.<br \/>\nBy convention, a dependency property is defined as a static, read-only field of the class and is named by suffixing the word Property to the normal property name. For example, the dependency property for the Height property of a Button would be accessed in code using Button.HeightProperty.<\/p>\n<p>The To, From, and By Properties<br \/>\nAll Animation classes define the following handful of key properties that control the starting and ending values used to perform the animation:<br \/>\n\u2022To: This property represents the animation\u2019s ending value.<br \/>\n\u2022From: This property represents the animation\u2019s starting value.<br \/>\n\u2022By: This property represents the total amount by which the animation changes its starting value.<br \/>\nDespite that all Animation classes support the To, From, and By properties, they do not receive them via virtual members of a base class. The reason for this is that the underlying types wrapped by these properties vary greatly (integers, colors, Thickness objects, etc.), and representing all possibilities using a single base class would result in complex coding constructs.<br \/>\nOn a related note, you might also wonder why .NET generics were not used to define a single generic animation class with a single type parameter (e.g., Animate<T>). Again, given that there are so many underlying data types (colors, vectors, ints, strings, etc.) used to animated dependency properties, it would not be as clean a solution as you might expect (not to mention XAML has only limited support for generic types).<\/p>\n<p>The Role of the Timeline Base Class<br \/>\nAlthough a single base class was not used to define virtual To, From, and By properties, the Animation classes do share a common base class: System.Windows.Media.Animation.Timeline. This type provides several additional properties that control the pacing of the animation, as described in Table 28-1.<\/p>\n<p>Table 28-1. Key Members of the Timeline Base Class<\/p>\n<p>Properties  Meaning in Life<br \/>\nAccelerationRatio, DecelerationRatio, SpeedRatio    These properties can be used to control the overall pacing of the animation sequence.<br \/>\nAutoReverse This property gets or sets a value that indicates whether the timeline plays in reverse after it completes a forward iteration (the default value is false).<br \/>\nBeginTime   This property gets or sets the time at which this timeline should begin. The default value is 0, which begins the animation immediately.<br \/>\nDuration    This property allows you to set a duration of time to play the timeline.<br \/>\nFillBehavior, RepeatBehavior    These properties are used to control what should happen once the timeline has completed (repeat the animation, do nothing, etc.).<\/p>\n<p>Authoring an Animation in C# Code<br \/>\nSpecifically, you will build a Window that contains a Button, which has the odd behavior of spinning in a circle (based on the upper-left corner) whenever the mouse enters its surface area. Begin by creating a new WPF application named SpinningButtonAnimationApp. Update the initial markup to the following (note you are handling the button\u2019s MouseEnter event):<\/p>\n<p><Button x:Name=\"btnSpinner\" Height=\"50\" Width=\"100\" Content=\"I Spin!\" MouseEnter=\"btnSpinner_MouseEnter\" Click=\"btnSpinner_OnClick\"\/><\/p>\n<p>In the code-behind file, import the System.Windows.Media.Animation namespace and add the following code in the window\u2019s C# code file:<br \/>\nprivate bool _isSpinning=false; private void btnSpinner_MouseEnter(<br \/>\nobject sender, MouseEventArgs e)<br \/>\n{<br \/>\nif (!_isSpinning)<br \/>\n{<br \/>\n_isSpinning=true;<br \/>\n\/\/ Make a double animation object, and register<br \/>\n\/\/ with the Completed event.<br \/>\nvar dblAnim=new DoubleAnimation();<br \/>\ndblAnim.Completed +=(o, s)=> { _isSpinning=false; };<br \/>\n\/\/ Set the start value and end value.<br \/>\ndblAnim.From=0; dblAnim.To=360;<br \/>\n\/\/ Now, create a RotateTransform object, and set<br \/>\n\/\/ it to the RenderTransform property of our<br \/>\n\/\/ button.<br \/>\nvar rt=new RotateTransform(); btnSpinner.RenderTransform=rt;<\/p>\n<p>\/\/ Now, animation the RotateTransform object.<br \/>\nrt.BeginAnimation(RotateTransform.AngleProperty, dblAnim);<br \/>\n}<br \/>\n}<br \/>\nprivate void btnSpinner_OnClick( object sender, RoutedEventArgs e)<br \/>\n{<\/p>\n<p>}<\/p>\n<p>The first major task of this method is to configure a DoubleAnimation object, which will start at the value 0 and end at the value 360. Notice that you are handling the Completed event on this object as well, to toggle a class-level bool variable that is used to ensure that if an animation is currently being performed, you don\u2019t \u201creset\u201d it to start again.<br \/>\nNext, you create a RotateTransform object that is connected to the RenderTransform property of your Button control (btnSpinner). Finally, you inform the RenderTransform object to begin animating its Angle property using your DoubleAnimation object. When you are authoring animations in code, you typically do so by calling BeginAnimation() and then pass in the underlying dependency property you would like to<br \/>\nanimate (remember, by convention, this is a static field on the class), followed by a related animation object.<br \/>\nLet\u2019s add another animation to the program, which will cause the button to fade into invisibility when clicked. First, add the following code in the Click event handler:<\/p>\n<p>private void btnSpinner_OnClick( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\nvar dblAnim=new DoubleAnimation<br \/>\n{<br \/>\nFrom=1.0, To=0.0<br \/>\n};<br \/>\nbtnSpinner.BeginAnimation(Button.OpacityProperty, dblAnim);<br \/>\n}<\/p>\n<p>Here, you are changing the Opacity property value to fade the button out of view. Currently, however, this is hard to do, as the button is spinning very fast! How, then, can you control the pace of an animation? Glad you asked.<\/p>\n<p>Controlling the Pace of an Animation<br \/>\nBy default, an animation will take approximately one second to transition between the values assigned to the From and To properties. Therefore, your button has one second to spin around a full 360-degree angle, while the button will fade away to invisibility (when clicked) over the course of one second.<br \/>\nIf you want to define a custom amount of time for an animation\u2019s transition, you may do so via the animation object\u2019s Duration property, which can be set to an instance of a Duration object. Typically, the time span is established by passing a TimeSpan object to the Duration\u2019s constructor. Consider the following update that will give the button a full four seconds to rotate:<\/p>\n<p>private void btnSpinner_MouseEnter( object sender, MouseEventArgs e)<br \/>\n{<br \/>\nif (!_isSpinning)<br \/>\n{<br \/>\n_isSpinning=true;<\/p>\n<p>\/\/ Make a double animation object, and register<br \/>\n\/\/ with the Completed event.<br \/>\nvar dblAnim=new DoubleAnimation();<br \/>\ndblAnim.Completed +=(o, s)=> { _isSpinning=false; };<\/p>\n<p>\/\/ Button has four seconds to finish the spin!<br \/>\ndblAnim.Duration=new Duration(TimeSpan.FromSeconds(4));<\/p>\n<p>...<br \/>\n}<br \/>\n}<\/p>\n<p>With this adjustment, you should have a fighting chance of clicking the button while it is spinning, at which point it will fade away.<\/p>\n<p>\u25a0Note the BeginTime property of an Animation class also takes a TimeSpan object. recall that this property can be set to establish a wait time before starting an animation sequence.<\/p>\n<p>Reversing and Looping an Animation<br \/>\nYou can also tell Animation objects to play an animation in reverse at the completion of the animation sequence by setting the AutoReverse property to true. For example, if you want to have the button come back into view after it has faded away, you could author the following:<\/p>\n<p>private void btnSpinner_OnClick(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\nDoubleAnimation dblAnim=new DoubleAnimation<br \/>\n{<br \/>\nFrom=1.0, To=0.0<br \/>\n};<br \/>\n\/\/ Reverse when done.<br \/>\ndblAnim.AutoReverse=true; btnSpinner.BeginAnimation(Button.OpacityProperty, dblAnim);<br \/>\n}<\/p>\n<p>If you would like to have an animation repeat some number of times (or to never stop once activated), you can do so using the RepeatBehavior property, which is common to all Animation classes. If you pass in a simple numerical value to the constructor, you can specify a hard-coded number of times to repeat.<\/p>\n<p>On the other hand, if you pass in a TimeSpan object to the constructor, you can establish an amount of time the animation should repeat. Finally, if you want an animation to loop ad infinitum, you can simply specify RepeatBehavior.Forever. Consider the following ways you could change the repeat behaviors of either of the DoubleAnimation objects used in this example:<\/p>\n<p>\/\/ Loop forever.<br \/>\ndblAnim.RepeatBehavior=RepeatBehavior.Forever;<\/p>\n<p>\/\/ Loop three times.<br \/>\ndblAnim.RepeatBehavior=new RepeatBehavior(3);<\/p>\n<p>\/\/ Loop for 30 seconds.<br \/>\ndblAnim.RepeatBehavior=new RepeatBehavior(TimeSpan.FromSeconds(30));<\/p>\n<p>That wraps up your investigation about how to animate aspects of an object using C# code and the WPF animation API. Next, you will learn how to do the same using XAML.<\/p>\n<p>Authoring Animations in XAML<br \/>\nAuthoring animations in markup is like authoring them in code, at least for simple, straightforward animation sequences. When you need to capture more complex animations, which may involve changing the values of numerous properties at once, the amount of markup can grow considerably. Even if you<br \/>\nuse a tool to generate XAML-based animations, it is important to know the basics of how an animation is represented in XAML because this will make it easier for you to modify and tweak tool-generated content.<\/p>\n<p>\u25a0Note you will find a number of Xaml files in the XamlAnimations folder of the downloadable source code. as you go through the next several pages, copy these markup files into your custom Xaml editor or into the Kaxaml editor to see the results.<\/p>\n<p>For the most part, creating an animation is like what you saw already. You still configure an Animation object and associate it to an object\u2019s property. One big difference, however, is that WPF is not function call friendly. As a result, instead of calling BeginAnimation(), you use a storyboard as a layer of indirection.<br \/>\nLet\u2019s walk through a complete example of an animation defined in terms of XAML, followed by a detailed breakdown. The following XAML definition will display a window that contains a single label. As soon as the Label object loads into memory, it begins an animation sequence in which the font size increases from 12 points to 100 over a period of four seconds. The animation will repeat for as long as the<br \/>\nWindow object is loaded in memory. You can find this markup in the GrowLabelFont.xaml file, so copy it into Kaxaml (make sure to press F5 to show the window) and observe the behavior.<\/p>\n<p><Window xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\nHeight=\"200\" Width=\"600\" WindowStartupLocation=\"CenterScreen\" Title=\"Growing Label Font!\"><br \/>\n<StackPanel><br \/>\n<Label Content=\"Interesting...\"><br \/>\n<Label.Triggers><br \/>\n<EventTrigger RoutedEvent=\"Label.Loaded\"><br \/>\n<EventTrigger.Actions><\/p>\n<p><BeginStoryboard><br \/>\n<Storyboard TargetProperty=\"FontSize\"><br \/>\n<DoubleAnimation From=\"12\" To=\"100\" Duration=\"0:0:4\" RepeatBehavior=\"Forever\"\/><br \/>\n<\/Storyboard><br \/>\n<\/BeginStoryboard><br \/>\n<\/EventTrigger.Actions><br \/>\n<\/EventTrigger><br \/>\n<\/Label.Triggers><br \/>\n<\/Label><br \/>\n<\/StackPanel><br \/>\n<\/Window><\/p>\n<p>Now, let\u2019s break this example down, bit by bit.<\/p>\n<p>The Role of Storyboards<br \/>\nWorking from the innermost element outward, you first encounter the <DoubleAnimation> element, which makes use of the same properties you set in procedural code (From, To, Duration, and RepeatBehavior).<\/p>\n<p><DoubleAnimation From=\"12\" To=\"100\" Duration=\"0:0:4\"\nRepeatBehavior=\"Forever\"\/><\/p>\n<p>As mentioned, Animation elements are placed within a <Storyboard> element, which is used to map the animation object to a given property on the parent type via the TargetProperty property, which in this case is FontSize. A <Storyboard> is always wrapped in a parent element named <BeginStoryboard>.<\/p>\n<p><BeginStoryboard><br \/>\n<Storyboard TargetProperty=\"FontSize\"><br \/>\n<DoubleAnimation From=\"12\" To=\"100\" Duration=\"0:0:4\"\nRepeatBehavior=\"Forever\"\/><br \/>\n<\/Storyboard><br \/>\n<\/BeginStoryboard><\/p>\n<p>The Role of Event Triggers<br \/>\nAfter the <BeginStoryboard> element has been defined, you need to specify some sort of action that will cause the animation to begin executing. WPF has a few different ways to respond to runtime conditions in markup, one of which is termed a trigger. From a high level, you can consider a trigger a way of responding to an event condition in XAML, without the need for procedural code.<br \/>\nTypically, when you respond to an event in C#, you author custom code that will execute when the event occurs. A trigger, however, is just a way to be notified that some event condition has happened (\u201cI\u2019m loaded into memory!\u201d or \u201cThe mouse is over me!\u201d or \u201cI have focus!\u201d).<br \/>\nOnce you have been notified that an event condition has occurred, you can start the storyboard. In this example, you are responding to the Label being loaded into memory. Because it is the Label\u2019s Loaded event you are interested in, the <EventTrigger> is placed in the Label\u2019s trigger collection.<\/p>\n<p><Label Content=\"Interesting...\"><br \/>\n<Label.Triggers><br \/>\n<EventTrigger RoutedEvent=\"Label.Loaded\"><br \/>\n<EventTrigger.Actions><\/p>\n<p><BeginStoryboard><br \/>\n<Storyboard TargetProperty=\"FontSize\"><br \/>\n<DoubleAnimation From=\"12\" To=\"100\" Duration=\"0:0:4\"\nRepeatBehavior=\"Forever\"\/><br \/>\n<\/Storyboard><br \/>\n<\/BeginStoryboard><br \/>\n<\/EventTrigger.Actions><br \/>\n<\/EventTrigger><br \/>\n<\/Label.Triggers><br \/>\n<\/Label><\/p>\n<p>Let\u2019s see another example of defining an animation in XAML, this time using a key frame animation.<\/p>\n<p>Animation Using Discrete Key Frames<br \/>\nUnlike the linear interpolation animation objects, which can only move between a starting point and an ending point, the key frame counterparts allow you to create a collection of specific values for an animation that should take place at specific times.<br \/>\nTo illustrate the use of a discrete key frame type, assume you want to build a Button control that animates its content so that over the course of three seconds the value \u201cOK!\u201d appears, one character at a time. You will find the following markup in the AnimateString.xaml file. Copy this markup into your MyXamlPad.exe program (or Kaxaml) and view the results:<\/p>\n<p><Window xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\" xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\nHeight=\"100\" Width=\"300\"\nWindowStartupLocation=\"CenterScreen\" Title=\"Animate String Data!\"><br \/>\n<StackPanel><br \/>\n<Button Name=\"myButton\" Height=\"40\"\nFontSize=\"16pt\" FontFamily=\"Verdana\" Width=\"100\"><br \/>\n<Button.Triggers><br \/>\n<EventTrigger RoutedEvent=\"Button.Loaded\"><br \/>\n<BeginStoryboard><br \/>\n<Storyboard><br \/>\n<StringAnimationUsingKeyFrames RepeatBehavior=\"Forever\" Storyboard.TargetProperty=\"Content\"\nDuration=\"0:0:3\"><br \/>\n<DiscreteStringKeyFrame Value=\"\" KeyTime=\"0:0:0\" \/><br \/>\n<DiscreteStringKeyFrame Value=\"O\" KeyTime=\"0:0:1\" \/><br \/>\n<DiscreteStringKeyFrame Value=\"OK\" KeyTime=\"0:0:1.5\" \/><br \/>\n<DiscreteStringKeyFrame Value=\"OK!\" KeyTime=\"0:0:2\" \/><br \/>\n<\/StringAnimationUsingKeyFrames><br \/>\n<\/Storyboard><br \/>\n<\/BeginStoryboard><br \/>\n<\/EventTrigger><br \/>\n<\/Button.Triggers><br \/>\n<\/Button><br \/>\n<\/StackPanel><br \/>\n<\/Window><\/p>\n<p>First, notice that you have defined an event trigger for your button to ensure that your storyboard executes when the button has loaded into memory. The StringAnimationUsingKeyFrames class oversees changing the content of the button, via the Storyboard.TargetProperty value.<br \/>\nWithin the scope of the <StringAnimationUsingKeyFrames> element, you define four DiscreteStringKeyFrame elements, which change the button\u2019s Content property over the course of two seconds (note that the duration established by StringAnimationUsingKeyFrames is a total of three seconds, so you will see a slight pause between the final ! and looping O).<br \/>\nNow that you have a better feel for how to build animations in C# code and XAML, let\u2019s look at the role of WPF styles, which make heavy use of graphics, object resources, and animations.<\/p>\n<p>Understanding the Role of WPF Styles<br \/>\nWhen you are building the UI of a WPF application, it is not uncommon for a family of controls to require a shared look and feel. For example, you might want all button types to have the same height, width, background color, and font size for their string content. Although you could handle this by setting each button\u2019s individual properties to identical values, such an approach makes it difficult to implement changes down the road because you would need to reset the same set of properties on multiple objects for every change.<br \/>\nThankfully, WPF offers a simple way to constrain the look and feel of related controls using styles.<br \/>\nSimply put, a WPF style is an object that maintains a collection of property-value pairs. Programmatically speaking, an individual style is represented using the System.Windows.Style class. This class has a property named Setters, which exposes a strongly typed collection of Setter objects. It is the Setter object that allows you to define the property-value pairs.<br \/>\nIn addition to the Setters collection, the Style class also defines a few other important members that allow you to incorporate triggers, restrict where a style can be applied, and even create a new style based on an existing style (think of it as \u201cstyle inheritance\u201d). Be aware of the following members of the Style class:<br \/>\n\u2022Triggers: Exposes a collection of trigger objects, which allow you to capture various event conditions within a style<br \/>\n\u2022BasedOn: Allows you to build a new style based on an existing style<br \/>\n\u2022TargetType: Allows you to constrain where a style can be applied<\/p>\n<p>Defining and Applying a Style<br \/>\nIn almost every case, a Style object will be packaged as an object resource. Like any object resource, you can package it at the window or application level, as well as within a dedicated resource dictionary (this is great because it makes the Style object easily accessible throughout your application). Now recall that the goal is to define a Style object that fills (at minimum) the Setters collection with a set of property-value pairs.<br \/>\nLet\u2019s build a style that captures the basic font characteristics of a control in your application. Start by creating a new WPF application named WpfStyles. Open your App.xaml file and define the following named style:<\/p>\n<p><Application.Resources><br \/>\n<Style x:Key=\"BasicControlStyle\"><br \/>\n<Setter Property=\"Control.FontSize\" Value=\"14\"\/><br \/>\n<Setter Property=\"Control.Height\" Value=\"40\"\/><br \/>\n<Setter Property=\"Control.Cursor\" Value=\"Hand\"\/><br \/>\n<\/Style><br \/>\n<\/Application.Resources><\/p>\n<p>Notice that your BasicControlStyle adds three Setter objects to the internal collection. Now, let\u2019s apply this style to a few controls in your main window. Because this style is an object resource, the controls that want to use it still need to use the {StaticResource} or {DynamicResource} markup extension to locate the style. When they find the style, they will set the resource item to the identically named Style property. Replace the default Grid control with the following markup:<\/p>\n<p><StackPanel><br \/>\n<Label x:Name=\"lblInfo\" Content=\"This style is boring...\" Style=\"{StaticResource BasicControlStyle}\" Width=\"150\"\/><br \/>\n<Button x:Name=\"btnTestButton\" Content=\"Yes, but we are reusing settings!\" Style=\"{StaticResource BasicControlStyle}\" Width=\"250\"\/><br \/>\n<\/StackPanel><\/p>\n<p>If you view the Window in the Visual Studio designer (or run the application), you will find that both controls support the same cursor, height, and font size.<\/p>\n<p>Overriding Style Settings<br \/>\nWhile both of your controls have opted into the style, if a control wants to apply a style and then change some of the defined settings, that is fine. For example, the Button will now use the Help cursor (rather than the Hand cursor defined in the style).<\/p>\n<p><Button x:Name=\"btnTestButton\" Content=\"Yes, but we are reusing settings!\" Cursor=\"Help\" Style=\"{StaticResource BasicControlStyle}\" Width=\"250\" \/><\/p>\n<p>Styles are processed before the individual property settings of the control using the style; therefore, controls can \u201coverride\u201d settings on a case-by-case basis.<\/p>\n<p>The Effect of TargetType on Styles<br \/>\nCurrently, your style is defined in such a way that any control can adopt it (and must do so explicitly by setting the control\u2019s Style property), given that each property is qualified by the Control class. For a program that defines dozens of settings, this would entail a good amount of repeated code. One way to clean this style up a bit is to use the TargetType attribute. When you add this attribute to a Style\u2019s opening element, you can mark exactly once where it can be applied (in this example, in App.XAML).<\/p>\n<p><Style x:Key=\"BasicControlStyle\" TargetType=\"Control\"><br \/>\n<Setter Property=\"FontSize\" Value=\"14\"\/><br \/>\n<Setter Property=\"Height\" Value=\"40\"\/><br \/>\n<Setter Property=\"Cursor\" Value=\"Hand\"\/><br \/>\n<\/Style><\/p>\n<p>\u25a0Note When you build a style that uses a base class type, you needn\u2019t be concerned if you assign a value to a dependency property not supported by derived types. if the derived type does not support a given dependency property, it is ignored.<\/p>\n<p>This is somewhat helpful, but you still have a style that can apply to any control. The TargetType attribute is more useful when you want to define a style that can be applied to only a particular type of control. Add the following new style to the application\u2019s resource dictionary:<\/p>\n<p><Style x:Key=\"BigGreenButton\" TargetType=\"Button\"><br \/>\n<Setter Property=\"FontSize\" Value=\"20\"\/><br \/>\n<Setter Property=\"Height\" Value=\"100\"\/><br \/>\n<Setter Property=\"Width\" Value=\"100\"\/><br \/>\n<Setter Property=\"Background\" Value=\"DarkGreen\"\/><br \/>\n<Setter Property=\"Foreground\" Value=\"Yellow\"\/><br \/>\n<\/Style><\/p>\n<p>This style will work only on Button controls (or a subclass of Button). If you apply it to an incompatible element, you will get markup and compiler errors. Add a new Button that uses this new style, as follows:<\/p>\n<p><Button x:Name=\"btnAnotherButton\" Content=\"OK!\" Margin=\"0,10,0,0\" Style=\"{StaticResource BigGreenButton}\" Width=\"250\" Cursor=\"Help\"\/><\/p>\n<p>You will see output like that shown in Figure 28-7.<\/p>\n<p>Figure 28-7. Controls with different styles<\/p>\n<p>Another effect of TargetType is that the style will get applied to all elements of that type within the scope of the style definition if the x:Key property does not exist.<br \/>\nHere is another application-level style that will apply automatically to all TextBox controls in the current application:<\/p>\n<p><!-- The default style for all text boxes. --><br \/>\n<Style TargetType=\"TextBox\"><br \/>\n<Setter Property=\"FontSize\" Value=\"14\"\/><br \/>\n<Setter Property=\"Width\" Value=\"100\"\/><br \/>\n<Setter Property=\"Height\" Value=\"30\"\/><br \/>\n<Setter Property=\"BorderThickness\" Value=\"5\"\/><br \/>\n<Setter Property=\"BorderBrush\" Value=\"Red\"\/><br \/>\n<Setter Property=\"FontStyle\" Value=\"Italic\"\/><br \/>\n<\/Style><\/p>\n<p>You can now define any number of TextBox controls, and they will automatically get the defined look.<br \/>\nIf a given TextBox does not want this default look and feel, it can opt out by setting the Style property to {x:Null}. For example, txtTest will get the default unnamed style, while txtTest2 is doing things its own way.<\/p>\n<p><TextBox x:Name=\"txtTest\"\/><br \/>\n<TextBox x:Name=\"txtTest2\" Style=\"{x:Null}\" BorderBrush=\"Black\" BorderThickness=\"5\" Height=\"60\" Width=\"100\" Text=\"Ha!\"\/><\/p>\n<p>Subclassing Existing Styles<br \/>\nYou can also build new styles using an existing style, via the BasedOn property. The style you are extending must have been given a proper x:Key in the dictionary, as the derived style will reference it by name<br \/>\nusing the {StaticResource} or {DynamicResource} markup extension. Here is a new style based on<br \/>\nBigGreenButton, which rotates the button element by 20 degrees:<\/p>\n<p><!-- This style is based on BigGreenButton. --><br \/>\n<Style x:Key=\"TiltButton\" TargetType=\"Button\" BasedOn=\"{StaticResource BigGreenButton}\"><br \/>\n<Setter Property=\"Foreground\" Value=\"White\"\/><br \/>\n<Setter Property=\"RenderTransform\"><br \/>\n<Setter.Value><br \/>\n<RotateTransform Angle=\"20\"\/><br \/>\n<\/Setter.Value><br \/>\n<\/Setter><br \/>\n<\/Style><\/p>\n<p>To use this new style, update the markup for the button to this:<\/p>\n<p><Button x:Name=\"btnAnotherButton\" Content=\"OK!\" Margin=\"0,10,0,0\" Style=\"{StaticResource TiltButton}\" Width=\"250\" Cursor=\"Help\"\/><\/p>\n<p>This changes the appearance to the image shown in Figure 28-8.<\/p>\n<p>Figure 28-8. Using a derived style<\/p>\n<p>Defining Styles with Triggers<br \/>\nWPF styles can also contain triggers by packaging up Trigger objects within the Triggers collection of the Style object. Using triggers in a style allows you to define certain <Setter> elements in such a way that they will be applied only if a given trigger condition is true. For example, perhaps you want to increase the size of a font when the mouse is over a button. Or maybe you want to make sure that the text box with the current<\/p>\n<p>focus is highlighted with a given color. Triggers are useful for these sorts of situations, in that they allow you to take specific actions when a property changes, without the need to author explicit C# code in a code- behind file.<br \/>\nHere is an update to the TextBox style that ensures that when a TextBox has the input focus, it will receive a yellow background:<\/p>\n<p><!-- The default style for all text boxes. --><br \/>\n<Style TargetType=\"TextBox\"><br \/>\n<Setter Property=\"FontSize\" Value=\"14\"\/><br \/>\n<Setter Property=\"Width\" Value=\"100\"\/><br \/>\n<Setter Property=\"Height\" Value=\"30\"\/><br \/>\n<Setter Property=\"BorderThickness\" Value=\"5\"\/><br \/>\n<Setter Property=\"BorderBrush\" Value=\"Red\"\/><br \/>\n<Setter Property=\"FontStyle\" Value=\"Italic\"\/><br \/>\n<!-- The following setter will be applied only when the text box is in focus. --><br \/>\n<Style.Triggers><br \/>\n<Trigger Property=\"IsFocused\" Value=\"True\"><br \/>\n<Setter Property=\"Background\" Value=\"Yellow\"\/><br \/>\n<\/Trigger><br \/>\n<\/Style.Triggers><br \/>\n<\/Style><\/p>\n<p>If you test this style, you\u2019ll find that as you tab between various TextBox objects, the currently selected TextBox has a bright yellow background (provided it has not opted out by assigning {x:Null} to the Style property).<br \/>\nProperty triggers are also very smart, in that when the trigger\u2019s condition is not true, the property automatically receives the default assigned value. Therefore, as soon as a TextBox loses focus, it also automatically becomes the default color without any work on your part. In contrast, event triggers (examined when you looked at WPF animations) do not automatically revert to a previous condition.<\/p>\n<p>Defining Styles with Multiple Triggers<br \/>\nTriggers can also be designed in such a way that the defined <Setter> elements will be applied when multiple conditions are true. Let\u2019s say you want to set the background of a TextBox to Yellow only if it has the active focus and the mouse is hovering within its boundaries. To do so, you can make use of the<br \/>\n<MultiTrigger> element to define each condition, like so:<\/p>\n<p><!-- The default style for all text boxes. --><br \/>\n<Style TargetType=\"TextBox\"><br \/>\n<Setter Property=\"FontSize\" Value=\"14\"\/><br \/>\n<Setter Property=\"Width\" Value=\"100\"\/><br \/>\n<Setter Property=\"Height\" Value=\"30\"\/><br \/>\n<Setter Property=\"BorderThickness\" Value=\"5\"\/><br \/>\n<Setter Property=\"BorderBrush\" Value=\"Red\"\/><br \/>\n<Setter Property=\"FontStyle\" Value=\"Italic\"\/><br \/>\n<!-- The following setter will be applied only when the text box is in focus AND the mouse is over the text box. --><br \/>\n<Style.Triggers><br \/>\n<MultiTrigger><br \/>\n<MultiTrigger.Conditions><br \/>\n<Condition Property=\"IsFocused\" Value=\"True\"\/><\/p>\n<p><Condition Property=\"IsMouseOver\" Value=\"True\"\/><br \/>\n<\/MultiTrigger.Conditions><br \/>\n<Setter Property=\"Background\" Value=\"Yellow\"\/><br \/>\n<\/MultiTrigger><br \/>\n<\/Style.Triggers><br \/>\n<\/Style><\/p>\n<p>Animated Styles<br \/>\nStyles can also incorporate triggers that kick off an animation sequence. Here is one final style that, when applied to Button controls, will cause the controls to grow and shrink in size when the mouse is inside the button\u2019s surface area:<\/p>\n<p><!-- The growing button style! --><br \/>\n<Style x:Key=\"GrowingButtonStyle\" TargetType=\"Button\"><br \/>\n<Setter Property=\"Height\" Value=\"40\"\/><br \/>\n<Setter Property=\"Width\" Value=\"100\"\/><br \/>\n<Style.Triggers><br \/>\n<Trigger Property=\"IsMouseOver\" Value=\"True\"><br \/>\n<Trigger.EnterActions><br \/>\n<BeginStoryboard><br \/>\n<Storyboard TargetProperty=\"Height\"><br \/>\n<DoubleAnimation From=\"40\" To=\"200\"\nDuration=\"0:0:2\" AutoReverse=\"True\"\/><br \/>\n<\/Storyboard><br \/>\n<\/BeginStoryboard><br \/>\n<\/Trigger.EnterActions><br \/>\n<\/Trigger><br \/>\n<\/Style.Triggers><br \/>\n<\/Style><\/p>\n<p>Here, the Triggers collection is on the lookout for the IsMouseOver property to return true. When this occurs, you define a <Trigger.EnterActions> element to execute a simple storyboard that forces the button to grow to a Height value of 200 (and then return to a Height of 40) over two seconds. If you want to perform other property changes, you could also define a <Trigger.ExitActions> scope to define any custom actions to take when IsMouseOver changes to false.<\/p>\n<p>Assigning Styles Programmatically<br \/>\nRecall that a style can be applied at runtime as well. This can be helpful if you want to let end users choose how their UI looks and feels or if you need to enforce a look and feel based on security settings (e.g., the DisableAllButton style) or what have you.<br \/>\nDuring this project, you have defined several styles, many of which can apply to Button controls. So,<br \/>\nlet\u2019s retool the UI of your main window to allow the user to pick from some of these styles by selecting names in a ListBox. Based on the user\u2019s selection, you will apply the appropriate style. Here is the new (and final) markup for the <Window> element:<\/p>\n<p><DockPanel ><br \/>\n<StackPanel Orientation=\"Horizontal\" DockPanel.Dock=\"Top\" Margin=\"0,0,0,50\"><br \/>\n<Label Content=\"Please Pick a Style for this Button\" Height=\"50\"\/><\/p>\n<p><ListBox x:Name=\"lstStyles\" Height=\"80\" Width=\"150\" Background=\"LightBlue\" SelectionChanged=\"comboStyles_Changed\" \/><br \/>\n<\/StackPanel><br \/>\n<Button x:Name=\"btnStyle\" Height=\"40\" Width=\"100\" Content=\"OK!\"\/><br \/>\n<\/DockPanel><\/p>\n<p>The ListBox control (named lstStyles) will be filled dynamically within the window\u2019s constructor, like so:<\/p>\n<p>public MainWindow()<br \/>\n{<br \/>\nInitializeComponent();<br \/>\n\/\/ Fill the list box with all the Button styles. lstStyles.Items.Add(\"GrowingButtonStyle\"); lstStyles.Items.Add(\"TiltButton\"); lstStyles.Items.Add(\"BigGreenButton\"); lstStyles.Items.Add(\"BasicControlStyle\");}<br \/>\n}<\/p>\n<p>The final task is to handle the SelectionChanged event in the related code file. Notice in the following code how you can extract the current resource by name, using the inherited TryFindResource() method:<\/p>\n<p>private void comboStyles_Changed(object sender, SelectionChangedEventArgs e)<br \/>\n{<br \/>\n\/\/ Get the selected style name from the list box.<br \/>\nvar currStyle=(Style)TryFindResource(lstStyles.SelectedValue); if (currStyle==null) return;<br \/>\n\/\/ Set the style of the button type.<br \/>\nthis.btnStyle.Style=currStyle;<br \/>\n}<\/p>\n<p>When you run this application, you can pick from one of these four button styles on the fly. Figure 28-9 shows your completed application.<\/p>\n<p>Figure 28-9. Controls with different styles<\/p>\n<p>Logical Trees, Visual Trees, and Default Templates<br \/>\nNow that you understand styles and resources, there are a few more preparatory topics to investigate before you begin learning how to build custom controls. Specifically, you need to learn the distinction between a logical tree, a visual tree, and a default template. When you are typing XAML into Visual Studio or a tool such as kaxaml.exe, your markup is the logical view of the XAML document. As well, if you author C# code that adds new items to a layout control, you are inserting new items into the logical tree. Essentially, a logical view represents how your content will be positioned within the various layout managers for a main Window (or another root element, such as Page or NavigationWindow).<br \/>\nHowever, behind every logical tree is a much more verbose representation termed a visual tree, which is used internally by WPF to correctly render elements onto the screen. Within any visual tree, there will be full details of the templates and styles used to render each object, including any necessary drawings, shapes, visuals, and animations.<br \/>\nIt is useful to understand the distinction between logical and visual trees because when you are building a custom control template, you are essentially replacing all or part of the default visual tree of a control and inserting your own. Therefore, if you want a Button control to be rendered as a star shape,<br \/>\nyou could define a new star template and plug it into the Button\u2019s visual tree. Logically, the Button is still<br \/>\nof type Button, and it supports the properties, methods, and events as expected. But visually, it has taken on a whole new appearance. This fact alone makes WPF an extremely useful API, given that other toolkits would require you to build a new class to make a star-shaped button. With WPF, you simply need to define new markup.<\/p>\n<p>\u25a0Note WpF controls are often described as lookless. this refers to the fact that the look and feel of a WpF control is completely independent (and customizable) from its behavior.<\/p>\n<p>Programmatically Inspecting a Logical Tree<br \/>\nWhile analyzing a window\u2019s logical tree at runtime is not a tremendously common WPF programming activity, it is worth mentioning that the System.Windows namespace defines a class named LogicalTreeHelper, which allows you to inspect the structure of a logical tree at runtime. To illustrate the connection between logical trees, visual trees, and control templates, create a new WPF application named TreesAndTemplatesApp.<br \/>\nReplace the Grid with the following markup that contains two Button controls and a large read-only TextBox with scrollbars enabled. Make sure you use the IDE to handle the Click event of each button. The following XAML will do nicely:<\/p>\n<p><DockPanel LastChildFill=\"True\"><br \/>\n<Border Height=\"50\" DockPanel.Dock=\"Top\" BorderBrush=\"Blue\"><br \/>\n<StackPanel Orientation=\"Horizontal\"><br \/>\n<Button x:Name=\"btnShowLogicalTree\" Content=\"Logical Tree of Window\"\nMargin=\"4\" BorderBrush=\"Blue\" Height=\"40\" Click=\"btnShowLogicalTree_Click\"\/><br \/>\n<Button x:Name=\"btnShowVisualTree\" Content=\"Visual Tree of Window\" BorderBrush=\"Blue\" Height=\"40\" Click=\"btnShowVisualTree_Click\"\/><br \/>\n<\/StackPanel><br \/>\n<\/Border><\/p>\n<p><TextBox x:Name=\"txtDisplayArea\" Margin=\"10\" Background=\"AliceBlue\" IsReadOnly=\"True\" BorderBrush=\"Red\" VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility\n=\"Auto\" \/><br \/>\n<\/DockPanel><\/p>\n<p>Within your C# code file, define a string member variable named _dataToShow. Now, within the Click handler for the btnShowLogicalTree object, call a helper function that calls itself recursively to populate the string variable with the logical tree of the Window. To do so, you will call the static GetChildren() method of LogicalTreeHelper. Here is the code:<\/p>\n<p>private string _dataToShow=string.Empty;<\/p>\n<p>private void btnShowLogicalTree_Click(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n_dataToShow=\"\"; BuildLogicalTree(0, this); txtDisplayArea.Text=_dataToShow;<br \/>\n}<\/p>\n<p>void BuildLogicalTree(int depth, object obj)<br \/>\n{<br \/>\n\/\/ Add the type name to the dataToShow member variable.<br \/>\n_dataToShow +=new string(' ', depth) + obj.GetType().Name + \"\\n\";<br \/>\n\/\/ If an item is not a DependencyObject, skip it. if (!(obj is DependencyObject))<br \/>\nreturn;<br \/>\n\/\/ Make a recursive call for each logical child.<br \/>\nforeach (var child in LogicalTreeHelper.GetChildren((DependencyObject)obj))<br \/>\n{<br \/>\nBuildLogicalTree(depth + 5, child);<br \/>\n}<br \/>\n}<br \/>\nprivate void btnShowVisualTree_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n}<\/p>\n<p>If you run your application and click this first button, you will see a tree print in the text area, which is just about an exact replica of the original XAML (see Figure 28-10).<\/p>\n<p>Figure 28-10. Viewing a logical tree at runtime<\/p>\n<p>Programmatically Inspecting a Visual Tree<br \/>\nA Window\u2019s visual tree can also be inspected at runtime using the VisualTreeHelper class of System. Windows.Media. Here is a Click implementation of the second Button control (btnShowVisualTree), which performs similar recursive logic to build a textual representation of the visual tree:<\/p>\n<p>using System.Windows.Media;<\/p>\n<p>private void btnShowVisualTree_Click(object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n_dataToShow=\"\"; BuildVisualTree(0, this); txtDisplayArea.Text=_dataToShow;<br \/>\n}<br \/>\nvoid BuildVisualTree(int depth, DependencyObject obj)<br \/>\n{<br \/>\n\/\/ Add the type name to the dataToShow member variable.<br \/>\n_dataToShow +=new string(' ', depth) + obj.GetType().Name + \"\\n\";<br \/>\n\/\/ Make a recursive call for each visual child.<br \/>\nfor (int i=0; i < VisualTreeHelper.GetChildrenCount(obj); i++)\n{\nBuildVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i));\n}\n}\n\nAs you can see in Figure 28-11, the visual tree exposes several lower-level rendering agents such as\nContentPresenter, AdornerDecorator, TextBoxLineDrawingVisual, and so forth.\n\nFigure 28-11. Viewing a visual tree at runtime\n\nProgrammatically Inspecting a Control\u2019s Default Template\nRecall that a visual tree is used by WPF to understand how to render a Window and all contained elements. Every WPF control stores its own set of rendering commands within its default template. Programmatically speaking, any template can be represented as an instance of the ControlTemplate class. As well, you can obtain a control\u2019s default template by using the aptly named Template property, like so:\n\n\/\/ Get the default template of the Button. Button myBtn=new Button();\nControlTemplate template=myBtn.Template;\n\nLikewise, you could create a new ControlTemplate object in code and plug it into a control\u2019s Template\nproperty as follows:\n\n\/\/ Plug in a new template for the button to use. Button myBtn=new Button();\nControlTemplate customTemplate=new ControlTemplate();\n\n\/\/ Assume this method adds all the code for a star template. MakeStarTemplate(customTemplate); myBtn.Template=customTemplate;\n\nWhile you could build a new template in code, it is far more common to do so in XAML. However, before you start building your own templates, let\u2019s finish the current example and add the ability to view the default template of a WPF control at runtime. This can be a useful way to look at the overall composition of a template. Update the markup of your window with a new StackPanel of controls docked to the left side of the master DockPanel, defined as so (placed just before the <TextBox> element):<\/p>\n<p><Border DockPanel.Dock=\"Left\" Margin=\"10\" BorderBrush=\"DarkGreen\" BorderThickness=\"4\" Width=\"358\"><br \/>\n<StackPanel><br \/>\n<Label Content=\"Enter Full Name of WPF Control\" Width=\"340\" FontWeight=\"DemiBold\" \/><br \/>\n<TextBox x:Name=\"txtFullName\" Width=\"340\" BorderBrush=\"Green\" Background=\"BlanchedAlmond\" Height=\"22\" Text=\"System.Windows.Controls.Button\" \/><br \/>\n<Button x:Name=\"btnTemplate\" Content=\"See Template\" BorderBrush=\"Green\" Height=\"40\" Width=\"100\" Margin=\"5\" Click=\"btnTemplate_Click\" HorizontalAlignment=\"Left\" \/><br \/>\n<Border BorderBrush=\"DarkGreen\" BorderThickness=\"2\" Height=\"260\" Width=\"301\" Margin=\"10\" Background=\"LightGreen\" ><br \/>\n<StackPanel x:Name=\"stackTemplatePanel\" \/><br \/>\n<\/Border><br \/>\n<\/StackPanel><br \/>\n<\/Border><\/p>\n<p>Add an empty event handler function for the btnTemplate_Click() event like this:<\/p>\n<p>private void btnTemplate_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n}<\/p>\n<p>The upper-left text area allows you to enter in the fully qualified name of a WPF control located in the PresentationFramework.dll assembly. Once the library is loaded, you will dynamically create an instance of the object and display it in the large square in the bottom left. Finally, the control\u2019s default template will be displayed in the right-hand text area. First, add a new member variable to your C# class of type Control, like so:<\/p>\n<p>private Control _ctrlToExamine=null;<\/p>\n<p>Here is the remaining code, which will require you to import the System.Reflection, System.Xml, and<br \/>\nSystem.Windows.Markup namespaces:<\/p>\n<p>private void btnTemplate_Click( object sender, RoutedEventArgs e)<br \/>\n{<br \/>\n_dataToShow=\"\";<br \/>\nShowTemplate();<br \/>\ntxtDisplayArea.Text=_dataToShow;<br \/>\n}<\/p>\n<p>private void ShowTemplate()<br \/>\n{<br \/>\n\/\/ Remove the control that is currently in the preview area. if (_ctrlToExamine !=null)<br \/>\nstackTemplatePanel.Children.Remove(_ctrlToExamine); try<br \/>\n{<br \/>\n\/\/ Load PresentationFramework, and create an instance of the<br \/>\n\/\/ specified control. Give it a size for display purposes, then add to the<br \/>\n\/\/ empty StackPanel.<br \/>\nAssembly asm=Assembly.Load(\"PresentationFramework, Version=4.0.0.0,\" + \"Culture=neutral, PublicKeyToken=31bf3856ad364e35\");<br \/>\n_ctrlToExamine=(Control)asm.CreateInstance(txtFullName.Text);<br \/>\n_ctrlToExamine.Height=200;<br \/>\n_ctrlToExamine.Width=200;<br \/>\n_ctrlToExamine.Margin=new Thickness(5); stackTemplatePanel.Children.Add(_ctrlToExamine);<br \/>\n\/\/ Define some XML settings to preserve indentation.<br \/>\nvar xmlSettings=new XmlWriterSettings{Indent=true};<br \/>\n\/\/ Create a StringBuilder to hold the XAML.<br \/>\nvar strBuilder=new StringBuilder();<br \/>\n\/\/ Create an XmlWriter based on our settings.<br \/>\nvar xWriter=XmlWriter.Create(strBuilder, xmlSettings);<br \/>\n\/\/ Now save the XAML into the XmlWriter object based on the ControlTemplate.<br \/>\nXamlWriter.Save(_ctrlToExamine.Template, xWriter);<br \/>\n\/\/ Display XAML in the text box.<br \/>\n_dataToShow=strBuilder.ToString();<br \/>\n}<br \/>\ncatch (Exception ex)<br \/>\n{<br \/>\n_dataToShow=ex.Message;<br \/>\n}<br \/>\n}<\/p>\n<p>The bulk of the work is just tinkering with the compiled BAML resource to map it into a XAML string. Figure 28-12 shows your final application in action, displaying the default template of the System.Windows. Controls.DatePicker control. The image shows the Calendar, which is accessed by clicking the button on the right side of the control.<\/p>\n<p>Figure 28-12. Investigating a ControlTemplate at runtime<\/p>\n<p>Great! You should have a better idea about how logical trees, visual trees, and control default templates work together. Now you can spend the remainder of this chapter learning how to build custom templates and user controls.<\/p>\n<p>Building a Control Template with the Trigger Framework<br \/>\nWhen you build a custom template for a control, you could do so with nothing but C# code. Using this approach, you would add data to a ControlTemplate object and then assign it to a control\u2019s Template property. Most of the time, however, you will define the look and feel of a ControlTemplate using XAML and add bits of code (or possibly quite a bit of code) to drive the runtime behavior.<br \/>\nIn the remainder of this chapter, you will examine how to build custom templates using Visual Studio. Along the way, you will learn about the WPF trigger framework and the Visual State Manager (VSM), and you will see how to use animations to incorporate visual cues for the end user. Using Visual Studio alone to build complex templates can entail a fair amount of typing and a bit of heavy lifting. To be sure, production- level templates will benefit from the use of Blend for Visual Studio, the (now) free companion application installed with Visual Studio. However, given that this edition of the text does not include coverage of Blend, it is time to roll up your sleeves and pound out some markup.<br \/>\nTo begin, create a new WPF application named ButtonTemplate. For this project, you are more interested in the mechanics of creating and using templates, so replace the Grid with the following markup:<\/p>\n<p><StackPanel Orientation=\"Horizontal\"><br \/>\n<Button x:Name=\"myButton\" Width=\"100\" Height=\"100\" Click=\"myButton_Click\"\/><br \/>\n<\/StackPanel><\/p>\n<p>In the Click event handler, simply display a message box (via MessageBox.Show()) to show a message confirming the clicking of the control. Remember, when you build custom templates, the behavior of the control is constant, but the look may vary.<br \/>\nCurrently, this Button is rendered using the default template, which, as the previous example illustrated, is a BAML resource within a given WPF assembly. When you want to define your own template, you essentially replace this default visual tree with your own creation. To begin, update the definition of the<br \/>\n<Button> element to specify a new template using the property-element syntax. This template will give the control a round appearance.<\/p>\n<p><Button x:Name=\"myButton\" Width=\"100\" Height=\"100\" Click=\"myButton_Click\"><br \/>\n<Button.Template><br \/>\n<ControlTemplate><br \/>\n<Grid x:Name=\"controlLayout\"><br \/>\n<Ellipse x:Name=\"buttonSurface\" Fill=\"LightBlue\"\/><br \/>\n<Label x:Name=\"buttonCaption\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\"\nFontWeight=\"Bold\" FontSize=\"20\" Content=\"OK!\"\/><br \/>\n<\/Grid><br \/>\n<\/ControlTemplate><br \/>\n<\/Button.Template><br \/>\n<\/Button><\/p>\n<p>Here, you have defined a template that consists of a named Grid control containing a named Ellipse and a Label. Because your Grid has no defined rows or columns, each child stacks on top of the previous control, which centers the content. If you run your application now, you will notice that the Click event will fire only when the mouse cursor is within the bounds of the Ellipse! This is a great feature of the WPF template architecture: you do not need to recalculate hit-testing, bounds checking, or any other low-level<br \/>\ndetail. So, if your template used a Polygon object to render some oddball geometry, you can rest assured that the mouse hit-testing details are relative to the shape of the control, not the larger bounding rectangle.<\/p>\n<p>Templates as Resources<br \/>\nCurrently, your template is embedded to a specific Button control, which limits reuse. Ideally, you would place your template into a resource dictionary so you can reuse your round button template between projects or, at minimum, move it into the application resource container for reuse within this project. Let\u2019s move the local Button resource to the application level by cutting the template definition from the Button and pasting it into the Application.Resources tag in the App.xaml file. Add in a Key and a TargetType, as follows:<\/p>\n<p><Application.Resources><br \/>\n<ControlTemplate x:Key=\"RoundButtonTemplate\" TargetType=\"{x:Type Button}\"><br \/>\n<Grid x:Name=\"controlLayout\"><br \/>\n<Ellipse x:Name=\"buttonSurface\" Fill=\"LightBlue\"\/><br \/>\n<Label x:Name=\"buttonCaption\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" FontWeight=\"Bold\" FontSize=\"20\" Content=\"OK!\"\/><br \/>\n<\/Grid><br \/>\n<\/ControlTemplate><br \/>\n<\/Application.Resources><\/p>\n<p>Update the Button markup to the following:<\/p>\n<p><Button x:Name=\"myButton\" Width=\"100\" Height=\"100\" Click=\"myButton_Click\"\nTemplate=\"{StaticResource RoundButtonTemplate}\"><br \/>\n<\/Button><\/p>\n<p>Now, because this resource is available for the entire application, you can define any number of round buttons just by simply applying the template. Create two additional Button controls that use this template for testing purposes (no need to handle the Click event for these new items).<\/p>\n<p><StackPanel><br \/>\n<Button x:Name=\"myButton\" Width=\"100\" Height=\"100\" Click=\"myButton_Click\"\nTemplate=\"{StaticResource RoundButtonTemplate}\"><\/Button><br \/>\n<Button x:Name=\"myButton2\" Width=\"100\" Height=\"100\" Template=\"{StaticResource RoundButtonTemplate}\"><\/Button><br \/>\n<Button x:Name=\"myButton3\" Width=\"100\" Height=\"100\" Template=\"{StaticResource RoundButtonTemplate}\"><\/Button><br \/>\n<\/StackPanel><\/p>\n<p>Incorporating Visual Cues Using Triggers<br \/>\nWhen you define a custom template, the visual cues of the default template are removed as well. For example, the default button template contains markup that informs the control how to look when certain UI events occur, such as when it receives focus, when it is clicked with the mouse, when it is enabled (or disabled), and so on. Users are quite accustomed to these sorts of visual cues because it gives the control somewhat of a tactile response. However, your RoundButtonTemplate does not define any such markup, so the look of the control is identical regardless of the mouse activity. Ideally, your control should look slightly different when clicked (maybe via a color change or drop shadow) to let the user know the visual state has changed.<br \/>\nThis can be done using triggers, as you have already learned. For simple operations, triggers work perfectly well. There are additional ways to do this that are beyond the scope of this book, but there is more information available at https:\/\/docs.microsoft.com\/en-us\/dotnet\/desktop-wpf\/themes\/how-to- create-apply-template.<br \/>\nBy way of example, update your RoundButtonTemplate with the following markup, which adds two<br \/>\ntriggers. The first will change the color of the control to blue and the foreground color to yellow when the mouse is over the surface. The second shrinks the size of the Grid (and, therefore, all child elements) when the control is pressed via the mouse.<\/p>\n<p><ControlTemplate x:Key=\"RoundButtonTemplate\" TargetType=\"Button\" ><br \/>\n<Grid x:Name=\"controlLayout\"><br \/>\n<Ellipse x:Name=\"buttonSurface\" Fill=\"LightBlue\" \/><br \/>\n<Label x:Name=\"buttonCaption\" Content=\"OK!\" FontSize=\"20\" FontWeight=\"Bold\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" \/><br \/>\n<\/Grid><br \/>\n<ControlTemplate.Triggers><br \/>\n<Trigger Property=\"IsMouseOver\" Value=\"True\"><\/p>\n<p><Setter TargetName=\"buttonSurface\" Property=\"Fill\" Value=\"Blue\"\/><br \/>\n<Setter TargetName=\"buttonCaption\" Property=\"Foreground\" Value=\"Yellow\"\/><br \/>\n<\/Trigger><br \/>\n<Trigger Property=\"IsPressed\" Value=\"True\"><br \/>\n<Setter TargetName=\"controlLayout\" Property=\"RenderTransformOrigin\" Value=\"0.5,0.5\"\/><br \/>\n<Setter TargetName=\"controlLayout\" Property=\"RenderTransform\"><br \/>\n<Setter.Value><br \/>\n<ScaleTransform ScaleX=\"0.8\" ScaleY=\"0.8\"\/><br \/>\n<\/Setter.Value><br \/>\n<\/Setter><br \/>\n<\/Trigger><br \/>\n<\/ControlTemplate.Triggers><br \/>\n<\/ControlTemplate><\/p>\n<p>The Role of the {TemplateBinding} Markup Extension<br \/>\nThe problem with the control template is that each of the buttons looks and says the same thing. Updating the markup to the following has no effect:<\/p>\n<p><Button x:Name=\"myButton\" Width=\"100\" Height=\"100\" Background=\"Red\" Content=\"Howdy!\" Click=\"myButton_Click\" Template=\"{StaticResource RoundButtonTemplate}\" \/><br \/>\n<Button x:Name=\"myButton2\" Width=\"100\" Height=\"100\" Background=\"LightGreen\" Content=\"Cancel!\" Template=\"{StaticResource RoundButtonTemplate}\" \/><br \/>\n<Button x:Name=\"myButton3\" Width=\"100\" Height=\"100\"\nBackground=\"Yellow\" Content=\"Format\" Template=\"{StaticResource RoundButtonTemplate}\" \/><\/p>\n<p>This is because the control\u2019s default properties (such as BackGround and Content) are overridden in the template. To enable them, they must be mapped to the related properties in the template. You can solve these issues by using the {TemplateBinding} markup extension when you build your template. This allows<br \/>\nyou to capture property settings defined by the control using your template and use them to set values in the template itself.<br \/>\nHere is a reworked version of RoundButtonTemplate, which now uses this markup extension to map the Background property of the Button to the Fill property of the Ellipse; it also makes sure the Content of the Button is indeed passed to the Content property of the Label:<\/p>\n<p><Ellipse x:Name=\"buttonSurface\" Fill=\"{TemplateBinding Background}\"\/><br \/>\n<Label x:Name=\"buttonCaption\" Content=\"{TemplateBinding Content}\" FontSize=\"20\" FontWeight=\"Bold\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" \/><\/p>\n<p>With this update, you can now create buttons of various colors and textual values. Figure 28-13 shows the result of the updated XAML.<\/p>\n<p>Figure 28-13. Template bindings allow values to pass through to the internal controls.<\/p>\n<p>The Role of ContentPresenter<br \/>\nWhen you designed your template, you used a Label to display the textual value of the control. Like the Button, the Label supports a Content property. Therefore, given your use of {TemplateBinding}, you could define a Button that contains complex content beyond that of a simple string.<br \/>\nHowever, what if you need to pass in complex content to a template member that does not have a Content property? When you want to define a generalized content display area in a template, you can use the ContentPresenter class as opposed to a specific type of control (Label or TextBlock). There is no need to do so for this example; however, here is some simple markup that illustrates how you could build a custom template that uses ContentPresenter to show the value of the Content property of the control using the template:<\/p>\n<p><!-- This button template will display whatever is set to the Content of the hosting button. --><br \/>\n<ControlTemplate x:Key=\"NewRoundButtonTemplate\" TargetType=\"Button\"><br \/>\n<Grid><br \/>\n<Ellipse Fill=\"{TemplateBinding Background}\"\/><br \/>\n<ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"\/><br \/>\n<\/Grid><br \/>\n<\/ControlTemplate><\/p>\n<p>Incorporating Templates into Styles<br \/>\nCurrently, your template simply defines a basic look and feel of the Button control. However, the process of establishing the basic properties of the control (content, font size, font weight, etc.) is the responsibility of the Button itself.<\/p>\n<p><!-- Currently the Button must set basic property values, not the template. --><br \/>\n<Button x:Name=\"myButton\" Foreground=\"Black\" FontSize=\"20\" FontWeight=\"Bold\"\nTemplate=\"{StaticResource RoundButtonTemplate}\" Click=\"myButton_Click\"\/><\/p>\n<p>If you want, you could establish these values in the template. By doing so, you can effectively create a default look and feel. As you might have already realized, this is a job for WPF styles. When you build a style (to account for basic property settings), you can define a template within the style! Here is your updated application resource in the application resources in App.xaml, which has been rekeyed as RoundButtonStyle:<\/p>\n<p><!-- A style containing a template. --><br \/>\n<Style x:Key=\"RoundButtonStyle\" TargetType=\"Button\"><br \/>\n<Setter Property=\"Foreground\" Value=\"Black\"\/><br \/>\n<Setter Property=\"FontSize\" Value=\"14\"\/><br \/>\n<Setter Property=\"FontWeight\" Value=\"Bold\"\/><br \/>\n<Setter Property=\"Width\" Value=\"100\"\/><br \/>\n<Setter Property=\"Height\" Value=\"100\"\/><br \/>\n<!-- Here is the template! --><br \/>\n<Setter Property=\"Template\"><br \/>\n<Setter.Value><br \/>\n<ControlTemplate TargetType=\"Button\"><br \/>\n<!-- Control template from above example --><br \/>\n<\/ControlTemplate><br \/>\n<\/Setter.Value><br \/>\n<\/Setter><br \/>\n<\/Style><\/p>\n<p>With this update, you can now create button controls by setting the Style property as so:<\/p>\n<p><Button x:Name=\"myButton\" Background=\"Red\" Content=\"Howdy!\" Click=\"myButton_Click\" Style=\"{StaticResource RoundButtonStyle}\"\/><\/p>\n<p>While the rendering and behavior of the button are identical, the benefit of nesting templates within styles is that you can provide a canned set of values for common properties. That wraps up your look at how to use Visual Studio and the trigger framework to build custom templates for a control. While there is still much more about the Windows Presentation Foundation API than has been examined here, you should be in a solid position for further study.<\/p>\n<p>Summary<br \/>\nThe first part of this chapter examined the resource management system of WPF. You began by looking at how to work with binary resources, and then you examined the role of object resources. As you learned, object resources are named blobs of XAML that can be stored at various locations to reuse content.<br \/>\nNext, you learned about WPF\u2019s animation framework. Here you had a chance to create some animations using C# code, as well as with XAML. You learned that if you define an animation in markup, you use<br \/>\n<Storyboard> elements and triggers to control execution. You then looked at the WPF-style mechanism, which makes heavy use of graphics, object resources, and animations.<br \/>\nYou examined the relationship between a logical tree and a visual tree. The logical tree is basically a one- to-one correspondence of the markup you author to describe a WPF root element. Behind this logical tree is a much deeper visual tree that contains detailed rendering instructions.<br \/>\nThe role of a default template was then examined. Remember, when you are building custom templates, you are essentially ripping out all (or part) of a control\u2019s visual tree and replacing it with your own custom implementation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CHAPTER 28 WPF Resources, Animations, Styles, and Templates This chapter introduces you to three important (and interrelated) topics that will deepen your understanding of the Windows Presentation Foundation (WPF) API. The first order of business is to learn the role of logical resources. As you will see, the logical resource (also known as an object [&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-354","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\/354","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=354"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/354\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=354"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=354"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=354"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}