Designing and Developing Windows Applications Using Microsoft .NET Framework 4: Designing the Presentation Layer
- 12/17/2011
- Objective 2.1: Choose the Appropriate Windows Technology
- Objective 2.2: Design the UI Layout and Structure
- Objective 2.3: Design Application Workflow
- Objective 2.4: Design Data Presentation and Input
- Objective 2.5: Design Presentation Behavior
- Objective 2.6: Design for UI Responsiveness
- Objective 2.6: Design for UI Responsiveness
- Chapter Summary
- Answers
Objective 2.5: Design Presentation Behavior
With the advent of WPF, the developer now has a great deal of control over how the UI interacts with the user. While familiar behaviors such as dragging are still easy to implement, you can also use attached events, triggers, and animation to make the user experience more responsive than ever before.
Determine Which Behaviors Will Be Implemented and How
WPF provides unprecedented functionality for implementing behaviors in the Presentation layer through attached events, triggers, and animation.
Attached Events
It is possible for a control to define a handler for an event that the control cannot itself raise. These incidents are called attached events. For example, consider Button controls in a Grid. The Button class defines a Click event, but the Grid class does not. However, you still can define a handler for buttons in the grid by attaching the Click event of the Button control in the XAML code. The following example demonstrates attaching an event handler for a Button contained in a Grid:
<Grid Button.Click="changeGridColor"> <Button Height="23" Margin="132,80,70,0" Name="button1" VerticalAlignment="Top" >Button</Button> </Grid>
Now every time a button contained in the Grid shown here is clicked, the changeGridColor event handler will handle that event. In this way, the Grid can respond to another element’s event. Attached events make it possible for any element in your Presentation layer to respond directly to an event raised by other elements.
Triggers
Along with Setters, Triggers make up the bulk of objects that you use in creating styles in WPF applications. Triggers allow you to implement property changes declaratively in response to other property changes that would have required event-handling code in Windows Forms programming. There are five kinds of Trigger objects, as listed in Table 2-12.
Table 2-12 Types of Trigger Objects
Type |
Class Name |
Description |
Property trigger |
Trigger |
Monitors a property and activates when the value of that property matches the Value property. |
Multi-trigger |
MultiTrigger |
Monitors multiple properties and activates only when all the monitored property values match their corresponding Value properties. |
Data trigger |
DataTrigger |
Monitors a bound property and activates when the value of the bound property matches the Value property. |
Multi-data-trigger |
MultiDataTrigger |
Monitors multiple bound properties and activates only when all the monitored bound properties match their corresponding Value properties. |
Event trigger |
EventTrigger |
Initiates a series of Actions when a specified event is raised. |
A Trigger is active only when it is part of a Style.Triggers collection—with one exception. EventTrigger objects can be created within a Control.Triggers collection outside a Style. The Control.Triggers collection can accommodate only EventTriggers, and any other Trigger placed in this collection causes an error. EventTriggers are used primarily with animation.
Property Triggers
The most commonly used type of Trigger is the property trigger. The property trigger monitors the value of a property specified by Property. When the value of the specified property equals the Value property, the Trigger is activated.
Triggers listen to the property indicated by Property and compare that property to the Value property. When the referenced property and the Value property are equal, the Trigger is activated. Any Setter objects in the Setters collection of the Trigger are applied to the style, and any Actions in the EnterActions collections are initiated. When the referenced property no longer matches the Value property, the Trigger is inactivated. All Setter objects in the Setters collection of the Trigger are inactivated, and any Actions in the ExitActions collection are initiated.
The following example demonstrates a simple Trigger object that changes the FontWeight of a Button element to Bold when the mouse enters the Button:
<Style.Triggers> <Trigger Property="Button.IsMouseOver" Value="True"> <Setter Property="Button.FontWeight" Value="Bold" /> </Trigger> </Style.Triggers>
In this example, the Trigger defines one Setter in its Setters collection. When the Trigger is activated, that Setter is applied.
Multi-Triggers
Multi-triggers are similar to property triggers in that they monitor the value of properties and activate when those properties meet a specified value. The difference is that multi-triggers are capable of monitoring several properties at a single time and they activate only when all monitored properties equal their corresponding Value properties. The properties that are monitored and their corresponding Value properties are defined by a collection of Condition objects.
The following example demonstrates a MultiTrigger that sets the Button.FontWeight property to Bold only when the Button is focused and the mouse has entered the control:
<Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Button.IsMouseOver" Value="True" /> <Condition Property="Button.IsFocused" Value="True" /> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Button.FontWeight" Value="Bold" /> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers>
Data Triggers and Multi-Data-Triggers
Data triggers are similar to property triggers in that they monitor a property and activate when the property meets a specified value, but they differ in that the property they monitor is a bound property. Instead of Property, data triggers expose a Binding property that indicates the bound property to listen to.
The following shows a data trigger that changes the Background property of a Label to Red when the bound property CustomerName equals “Fabrikam”:
<Style.Triggers> <DataTrigger Binding="{Binding Path=CustomerName}" Value="Fabrikam"> <Setter Property="Label.Background" Value="Red" /> </DataTrigger> </Style.Triggers>
Multi-data-triggers are to data triggers as multi-triggers are to property triggers. They contain a collection of Condition objects, each of which specifies a bound property via its Binding property and a value to compare to that bound property. When all the conditions are satisfied, the MultiDataTrigger activates. The following example demonstrates a MultiDataTrigger that sets the Label.Background property to Red when CustomerName equals “Fabrikam” and OrderSize equals 500:
<Style.Triggers> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=CustomerName}" Value="Fabrikam" /> <Condition Binding="{Binding Path=OrderSize}" Value="500" /> </MultiDataTrigger.Conditions> <MultiDataTrigger.Setters> <Setter Property="Label.Background" Value="Red" /> </MultiDataTrigger.Setters> </MultiDataTrigger> </Style.Triggers>
Event Triggers
Event triggers are different from the other Trigger types. Whereas other Trigger types monitor the value of a property and compare it to an indicated value, event triggers specify an event and activate when that event is raised. In addition, event triggers do not have a Setters collection—rather, they have an Actions collection. The following two examples demonstrate the EventTrigger class. The first example uses a SoundPlayerAction to play a sound when a Button is clicked:
<EventTrigger RoutedEvent="Button.Click"> <SoundPlayerAction Source="C:\myFile.wav" /> </EventTrigger>
The second example demonstrates a simple animation that causes the Button to grow in height by 200 units when clicked:
<EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:5" Storyboard.TargetProperty="Height" To="200" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger>
Animation
The term animation brings to mind hand-drawn anthropomorphic animals performing amusing antics in video media, but in WPF, animation has a far simpler meaning. Generally speaking, an animation in WPF refers to an automated property change over a set period of time. You can animate an element’s size, location, color, or virtually any other property or properties associated with an element. You can use the Animation classes to implement these changes.
The Animation classes are a large group of classes designed to implement these automated property changes. There are 42 Animation classes in the System.Windows.Media.Animation namespace, and each one has a specific data type that they are designed to animate. Animation classes fall into three basic groups: linear animations, key frame–based animations, and path-based animations.
Linear animations, which automate a property change in a linear way, are named in the format <TypeName>Animation, where <TypeName> is the name of the type being animated. DoubleAnimation is an example of a linear animation class, and that is the animation class you are likely to use the most.
Key frame–based animations perform their animation on the basis of several waypoints, called key frames. The flow of a key-frame animation starts at the beginning and then progresses to each of the key frames before ending. The progression is usually linear. Key-frame animations are named in the format <TypeName>AnimationUsingKeyFrames, where <TypeName> is the name of the Type being animated. An example is StringAnimationUsingKeyFrames.
Path-based animations use a Path object to guide the animation. They are used most often to animate properties that relate to the movement of visual objects along a complex course. Path-based animations are named in the format <TypeName>AnimationUsingPath, where <TypeName> is the name of the type being animated. There are currently only three path-based Animation classes—PointAnimationUsingPath, DoubleAnimationUsingPath, and MatrixAnimationUsingPath.
Creating Attached Behaviors
You also can create custom attached behaviors that change or extend the behavior of standard classes. Usually, developers do this to change the way a UI element interacts with the user. For example, you might create a custom attached behavior to allow a button to respond to right-clicking in an M-V-VM application, or to add drag-and-drop behavior to a class that does not support it natively.
Custom attached behaviors are simpler to implement than deriving a new class from a standard control, and they do not require you to violate the presentation pattern (as discussed in Objective 2.1) by writing behavior logic in the ViewModel.
To implement an attached behavior, follow these high-level steps:
Create a new static class that represents the behavior.
Within the class, expose any attached properties as public dependency objects. The attached property will be used to enable your attached behavior.
Within the change event for your attached property, write the code that implements your attached behavior.
In the XAML, apply the attached behavior to the element.
For example, imagine that you wanted to extend the standard text box behavior with auto-complete functionality: whenever the user typed a character, the view would send the typed phrase to a web service that returns a list of options the user can select from. You could do this by handling the appropriate text box event in the ViewModel and then populating the list from the event handler. However, this technique requires writing UI logic in your ViewModel, breaking the M-V-VM presentation pattern. It also requires writing event handlers for every text box that you want to add this behavior to.
You also could do this by creating an attached behavior. By creating an attached behavior, the behavior code is contained within the view, which fits the presentation pattern better. In addition, the attached behavior uses less, more maintainable code.
Implementing Drag-and-Drop Functionality
Drag-and-drop functionality is ubiquitous in Windows programming. It refers to allowing the user to grab data—such as text, an image, or another object—with the mouse and drag it to another control. When the mouse button is released over the other control, the data that is being dragged is dropped onto the control, and a variety of effects can then occur.
The drag-and-drop operation is similar to cutting and pasting. The mouse pointer is positioned over a control and the mouse button is pressed. Data is copied from a source control; when the mouse button is released, the action is completed. All code for copying the data from the source control and any actions taken on the target control must be coded explicitly.
Drag-and-drop operations are very similar in WPF and Windows Forms applications. The primary difference is that in Windows Forms, drag-and-drop methods and events are exposed on individual controls, and in WPF, the methods are exposed on a static class called DragDrop, which also provides attached events to WPF controls to facilitate drag-and-drop operations.
The drag-and-drop process is primarily an event-driven process. There are events that occur on the source control and events that occur on the target control. The drag-and-drop events for the source control are described in Table 2-13. The drag-and-drop events for the target control are described in Table 2-14.
Table 2-13 Source Control Events Involved in Implementing Drag-and-Drop Operations
Event |
Description |
MouseDown |
Occurs when the mouse button is pressed while the pointer is over the control. In general, the DoDragDrop method is called in the method that handles this event. In WPF applications, this is a bubbling event. |
GiveFeedBack |
Provides an opportunity for the user to set a custom mouse pointer. In WPF applications, this is a bubbling event. |
QueryContinueDrag |
Enables the drag source to determine whether a drag event should be canceled. In WPF applications, this is a bubbling event. |
PreviewMouseDown |
WPF only. The tunneling version of the MouseDown event. |
PreviewGiveFeedBack |
WPF only. The tunneling version of the GiveFeedback event. |
PreviewQueryContinueDrag |
WPF only. The tunneling version of the QueryContinueDrag event. |
Table 2-14 Target Control Events Involved in Implementing Drag-and-Drop Operations
Event |
Description |
DragEnter |
Occurs when an object is dragged within a control’s bounds. The handler for this event receives a DragEventArgs object. In WPF, this is a bubbling event. |
DragOver |
Occurs when an object is dragged over a target control. The handler for this event receives a DragEventArgs object. In WPF, this is a bubbling event. |
DragDrop |
Occurs when the mouse button is released over a target control. The handler for this event receives a DragEventArgs object. In WPF, this is a bubbling event. |
DragLeave |
Occurs when an object is dragged out of the control’s bounds. In WPF, this is a bubbling event. |
PreviewDragEnter |
WPF only. The tunneling version of DragEnter. |
PreviewDragOver |
WPF only. The tunneling version of DragOver. |
PreviewDragDrop |
WPF only. The tunneling version of DragDrop. |
PreviewDragLeave |
WPF only. The tunneling version of DragLeave. |
In addition, the DoDragDrop method on the source control is required to initiate the drag-and-drop process in Windows Forms, and the DoDragDrop method of the DragDrop class is required for WPF. Furthermore, the target control must have the AllowDrop property set to True.
If you are creating an XBAP application, you must run the application with full trust to take advantage of true drag-and-drop; partial trust allows only a simulated drag-and-drop using limited mouse events. For more information about partial trust, refer to Objective 1.3, “Design the Security Implementation,” in Chapter 1, “Designing the Layers of a Solution.” For more information about deploying ClickOnce applications with full trust, refer to Objective 4.1, “Define a Client Deployment Strategy,” in Chapter 4, “Planning a Solution Deployment.”
The General Sequence of a Drag-and-Drop Operation
The general sequence of events that takes place in a drag-and-drop operation is as follows:
The drag-and-drop operation is initiated by calling the DoDragDrop method on the source control (for Windows Forms) or the DragDrop.DoDragDrop method for WPF applications. This is usually done in the MouseDown event handler. DoDragDrop copies the desired data from the source control to a new instance of DataObject and sets flags that specify which effects are allowed with this data.
The GiveFeedBack and QueryContinueDrag events are raised at this point. The GiveFeedback event handler can set the mouse pointer to a custom shape, and the QueryContinueDrag event handler can be used to determine if the drag operation should be continued or aborted.
The mouse pointer is dragged over a target control. Any control that has the AllowDrop property set to True is a potential drop target. When the mouse pointer enters a control with the AllowDrop property set to True, the DragEnter event for that control is raised. The DragEventArgs object that the event handler receives can be examined to determine if data appropriate for the target control is present. If so, the Effect property of the DragEventArgs object then can be set to an appropriate value.
The user releases the mouse button over a valid target control, raising the DragDrop event. The code in the DragDrop event handler then obtains the dragged data and takes whatever action is appropriate in the target control.
The DragDropEffects Enumeration
To complete a drag-and-drop operation, the drag effect specified in the DoDragDrop method must match the value of the Effect parameter of the DragEventArgs object associated with the drag-and-drop event, which is generally set in the DragEnter handler. The Effect property is an instance of the DragDropEffects enumeration. The members of the DragDropEffects enumeration are described in Table 2-15.
Table 2-15 DragDropEffects Enumeration Members
Member |
Explanation |
All |
Data is copied, removed from the drag source, and scrolled in the target. |
Copy |
The data is copied to the target. |
Link |
The data is linked to the target. |
Move |
The data is moved to the target. |
None |
The target does not accept the data. |
Scroll |
Scrolling is about to start or is currently occurring in the target. |
Note that the main function of the Effect parameter is to change the mouse cursor when it is over the target control. The value of the Effect parameter has no actual effect on the action that is executed except that when the Effect parameter is set to None, no drop can take place on that control because the DragDrop event will not be raised.
Initiating the Drag-and-Drop Operation in Windows Forms Applications
The drag-and-drop operation is initiated by calling the DoDragDrop method on the source control. The DoDragDrop method takes two parameters: an Object, which represents the data to be copied to the DataObject, and an instance of DragDropEffects, which specifies what drag effects will be allowed with this data. The following example demonstrates how to copy the text from a text box and set the allowed effects to Copy or Move:
Sample of Visual Basic.NET Code
Private Sub TextBox1_MouseDown(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles TextBox1.MouseDown TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy Or DragDropEffects.Move) End Sub
Sample of Visual C# Code
private void textBox1_MouseDown(object sender, MouseEventArgs e) { textBox1.DoDragDrop(textBox1.Text, DragDropEffects.Copy | DragDropEffects.Move); }
Note that you can use the Or operator (Visual Basic) or the | operator (C#) to combine members of the DragDropEffects enumeration to indicate multiple effects.
Initiating the Drag-and-Drop Operation in WPF Applications
In WPF applications, you initiate the drag-and-drop operation by calling DragDrop.DoDragDrop. This method takes three parameters: a DependencyObject that represents the source control for the drag operation, an Object that represents that data that will be copied to the DataObject, and an instance of DragDropEffects, which specifies what drag effects will be allowed with this data. The following example demonstrates how to copy the text from a text box and set the allowed effects to Copy or Move:
Sample of Visual Basic.NET Code
Private Sub TextBox1_MouseDown(ByVal sender As System.Object, _ ByVal e As System.Windows.Input.MouseButtonEventArgs) _ Handles TextBox1.MouseDown DragDrop.DoDragDrop(TextBox1, TextBox1.Text, DragDropEffects.Copy Or DragDropEffects.Move) End Sub
Sample of Visual C# Code
private void textBox1_MouseDown(object sender, MouseButtonEventArgs e) { DragDrop.DoDragDrop(textBox1, textBox1.Text, DragDropEffects.Copy | DragDropEffects.Move); }
Handling the DragEnter Event
The DragEnter event should be handled for every target control. This event occurs when a drag-and-drop operation is in progress and the mouse pointer enters the control. This event passes a DragEventArgs object to the method that handles it, and you can use the DragEventArgs object to query the DataObject associated with the drag-and-drop operation. If the data is appropriate for the target control, you can set the Effect property to an appropriate value for the control. The following example demonstrates how to examine the data format of the DataObject and set the Effect property:
Sample of Visual Basic.NET Code
' This is a Windows Forms example Private Sub TextBox2_DragEnter(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.DragEventArgs) _ Handles TextBox2.DragEnter If e.Data.GetDataPresent(DataFormats.Text) = True Then e.Effect = DragDropEffects.Copy End If End Sub ' This is a WPF example Private Sub TextBox2_DragEnter(ByVal sender As System.Object, _ ByVal e As System.Window.DragEventArgs) _ Handles TextBox2.DragEnter If e.Data.GetDataPresent(DataFormats.Text) = True Then e.Effect = DragDropEffects.Copy End If End Sub
Sample of Visual C# Code
// The Windows Forms and WPF examples look the same in C# private void textBox2_DragEnter (object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text)) { e.Effect = DragDropEffects.Copy; } }
Note that in WPF applications, this is an attached event that is based off of the DragDrop class. You can attach this event in the Events pane of the Properties window in Visual Studio.
Handling the DragDrop Event
When the mouse button is released over a target control during a drag-and-drop operation, the DragDrop event is raised. In the method that handles the DragDrop event, you can use the GetData method of the DataObject to retrieve the copied data from the DataObject and take whatever action is appropriate for the control. The following example demonstrates how to drop a String into a TextBox:
Sample of Visual Basic.NET Code
' This is a Windows Forms example Private Sub TextBox2_DragDrop(ByVal sender As System.Object, ByVal e As _ System.Windows.Forms.DragEventArgs) Handles TextBox2.DragDrop TextBox2.Text = TryCast(e.Data.GetData(DataFormats.Text), String) End Sub ' This is a WPF example Private Sub TextBox2_DragDrop(ByVal sender As System.Object, ByVal e As _ System.Windows.DragEventArgs) Handles TextBox2.DragDrop TextBox2.Text = TryCast(e.Data.GetData(DataFormats.Text), String) End Sub
Sample of Visual C# Code
// The Windows Forms and WPF examples look the same in C# private void textBox2_DragDrop(object sender, DragEventArgs e) { textBox2.Text = (string)e.Data.GetData(DataFormats.Text); }
Note that in WPF applications, this is an attached event that is based off the DragDrop class. You can attach this event in the Events pane of the Properties window in Visual Studio.
Implementing Drag-and-Drop Operations Between Applications
The system intrinsically supports drag-and-drop operations between .NET Framework applications. You don’t need to take any additional steps to enable drag-and-drop operations that take place between applications. The only conditions that must be satisfied to enable a drag-and-drop operation between applications are:
The target control must allow one of the drag effects specified in the DoDragDrop method call.
The target control must accept data in the format that was set in the DoDragDrop method call.
Objective Summary
Attached events allow you to enable any WPF element to respond to events raised by any other WPF element.
Triggers allow WPF UIs to respond dynamically to user input or actions.
Animation allows you to alter the appearance of WPF elements in real time.
Drag-and-drop operations are supported in both Windows Forms and WPF UIs.
Objective Review
Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of the chapter.
-
Look at the following XAML snippet:
<Window.Resources> <Style x:Key="Style1"> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="TextBox.IsMouseOver" Value="True" /> <Condition Property="TextBox.IsFocused" Value="True" /> </MultiTrigger.Conditions> <Setter Property="TextBox.Background" Value="Red" /> </MultiTrigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <TextBox Style="{StaticResource Style1}" Height="21" Margin="75,0,83,108" Name="TextBox1" VerticalAlignment="Bottom" /> </Grid>
When will TextBox1 appear with a red background?
When the mouse is over TextBox1
When TextBox1 is focused
When TextBox1 is focused and the mouse is over TextBox1
All of the above
Never
Which of the following events must be handled to execute a drag-and-drop operation? (Choose all that apply.)
MouseDown
MouseUp
DragLeave
DragDrop