Siêu thị PDFTải ngay đi em, trời tối mất

Thư viện tri thức trực tuyến

Kho tài liệu với 50,000+ tài liệu học thuật

© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

Apress pro Silverlight 3 in C# phần 8 pps
PREMIUM
Số trang
97
Kích thước
2.2 MB
Định dạng
PDF
Lượt xem
1094

Apress pro Silverlight 3 in C# phần 8 pps

Nội dung xem thử

Mô tả chi tiết

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

464

(which is in the FocusStates group). If you do, the result will depend on the order that the

control applies its states. For example, if the button applies the state from the FocusStates

group first and then the state from the CommonStates group, your focused state animation will

be active for just a split second before being replaced by the competing MouseOver state.

Figure 13-5. Focus in a custom button template

Transitions

The button shown in the previous example uses zero-length state animations. As a result, the

color change happens instantly when the mouse moves over the button.

You can lengthen the duration to create a more gradual color blending effect. Here’s

an example that fades in the new color over a snappy 0.2 seconds:

<VisualStateManager.VisualStateGroups>

<VisualStateGroup x:Name="CommonStates">

<VisualState x:Name="MouseOver">

<Storyboard>

<ColorAnimation Duration="0:0:0.2" ... />

</Storyboard>

</VisualState>

...

</VisualStateGroup>

</VisualStateManager.VisualStateGroups>

Although this works, the concept isn’t quite right. Technically, each visual state is

meant to represent the appearance of the control while it’s in that state (not including the

transition used to get into that state). Ideally, a visual state animation should be either a zero￾length animation like the ones shown earlier or a steady-state animation–an animation that

repeats itself one or more times. For example, a button that glimmers when you move the

mouse over it uses a steady-state animation.

If you want an animated effect to signal when the control switches from one state to

another, you should use a transition instead. A transition is an animation that starts from the

current state and ends at the new state. One of the advantages of the transition model is that

you don’t need to create the storyboard for this animation. Instead, Silverlight creates the

animation you need automatically.

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

465

■ Note Controls are smart enough to skip transition animations when the controls begin in a certain state. For

example, consider the CheckBox control, which has an Unchecked state and a Checked state. You may decide

to use an animation to fade in the checkmark gracefully when the check box is selected. If you add the fade-in

effect to the Checked state animation, it will apply when you show a checked check box for the first time. (For

example, if you have a page with three checked check boxes, all three checkmarks will fade in when the page

first appears.) However, if you add the fade-in effect through a transition, it will be used only when the user clicks

the check box to change its state. It won’t apply when the control is shown for the first time, which makes more

sense.

The Default Transition

Transitions apply to state groups. When you define a transition, you must add it to the

VisualStateGroup.Transitions collection. The simplest type of transition is a default transition,

which applies to all the state changes for that group. To create the default transition, you need

to add a VisualTransition element and set the GeneratedDuration property to set the length of

the transition effect. Here’s an example:

<VisualStateManager.VisualStateGroups>

<VisualStateGroup x:Name="CommonStates">

<VisualStateGroup.Transitions>

<VisualTransition GeneratedDuration="0:0:0.2" />

</VisualStateGroup.Transitions>

<VisualState x:Name="MouseOver">

<Storyboard>

<ColorAnimation Duration="0:0:0"

Storyboard.TargetName="ButtonBackgroundBrush"

Storyboard.TargetProperty="Color" To="Orange" />

</Storyboard>

</VisualState>

<VisualState x:Name="Normal">

</VisualState>

</VisualStateGroup>

</VisualStateManager.VisualStateGroups>

Now, whenever the button changes from one of the common states to another, the

default 0.2 second transition kicks in. That means that when the user moves the mouse over the

button, and the button enters the MouseOver state, the new color fades in over 0.2 seconds,

even though the MouseOver state animation has a zero length. Similarly, when the user moves

the mouse off the button, the button blends back to its original color over 0.2 seconds.

Essentially, a transition is an animation that takes you from one state to another.

VisualStateManager can create a transition animation as long as your state animations use one

of the following types:

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

466

• ColorAnimation or ColorAnimationUsingKeyFrames

• PointAnimation or PointAnimationUsingKeyFrames

• DoubleAnimation or DoubleAnimationUsingKeyFrames

The button example works because the Normal and MouseOver states use a

ColorAnimation, which is one of the supported types. If you use something else–say, an

ObjectAnimationUsingKeyFrames–the transition won’t have any effect. Instead, the old value

will stay in place, the transition will run out its duration, and then the new value will snap in.

■ Note In some cases, a state uses several animations. In this situation, all the animations that use supported

types are animated by the transition. Any unsupported types snap in at the end of the transition.

From and To Transitions

A default transition is convenient, but it’s a one-size-fits-all solution that’s not always suitable.

For example, you may want a button to transition to the MouseOver state over 0.2 seconds but

return instantly to the Normal state when the mouse moves away. To set this up, you need to

define multiple transitions, and you need to set the From and To properties to specify when the

transition will come into effect.

For example, if you have these transitions

<VisualStateGroup.Transitions>

<VisualTransition To="MouseOver" GeneratedDuration="0:0:0.5" />

<VisualTransition From="MouseOver" GeneratedDuration="0:0:0.1" />

</VisualStateGroup.Transitions>

the button will switch into the MouseOver state in 0.5 seconds, and it will leave the MouseOver

state in 0.1 seconds. There is no default transition, so any other state changes will happen

instantly.

This example shows transitions that apply when entering specific states and

transitions that apply when leaving specific states. You can also use the To and From properties

in conjunction to create even more specific transitions that apply only when moving between

two specific states. When applying transitions, Silverlight looks through the collection of

transitions to find the most specific one that applies, and it uses only that one. For example,

when the mouse moves over a button, the VisualStateManager searches for states in this order,

stopping when it finds a match:

1. A transition with From="Normal" and To="MouseOver"

2. A transition with To="MouseOver"

3. A transition with From="Normal"

4. The default transition

If there’s no default transition, it switches between the two states immediately.

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

467

Transitioning to a Steady State

So far, you’ve seen how transitions work with zero-length state animations. However, it’s

equally possible to create a control template that uses transitions to move between steady-state

animations. (Remember, a steady-state animation is a looping animation that repeats itself

more than one time.)

To understand what happens in this situation, you need to realize that a transition to a

steady-state animation moves from the current property value to the starting property value of

the steady-state animation. For example, imagine you want to create a button that pulses

steadily when the mouse is over it. As with all steady-state animations, you need to set the

RepeatBehavior property to a number of repetitions you want, or use Forever to loop

indefinitely (as in this example). Depending on the data type, you may also need to set the

AutoReverse property to true. For example, with a ColorAnimation, you need to use automatic

reversal to return to the original color before repeating the animation. With a key-frame

animation, this extra step isn’t necessary because you can animate from the last key frame at

the end of the animation to the first key frame of a new iteration.

Here’s the steady-state animation for the pulsing button:

<VisualState x:Name="MouseOver">

<Storyboard>

<ColorAnimation Duration="0:0:0.4" Storyboard.TargetName="ButtonBackgroundBrush"

Storyboard.TargetProperty="Color" From="DarkOrange" To="Orange"

RepeatBehavior="Forever" AutoReverse="True" />

</Storyboard>

</VisualState>

It’s not necessary to use a transition with this button–after all, you may want the

pulsing effect to kick in immediately. But if you do want to provide a transition, it will occur

before the pulsing begins. Consider a standard transition like this one:

<VisualStateGroup.Transitions>

<VisualTransition From="Normal" To="MouseOver" GeneratedDuration="0:0:1" />

</VisualStateGroup.Transitions>

This takes the button from its current color (Red) to the starting color of the steady￾state animation (DarkOrange) using a 1-second animation. After that, the pulsing begins.

Custom Transition

All the previous examples have used automatically generated transition animations. They

change a property smoothly from its current value to the value set by the new state. However,

you may want to define customized transitions that work differently. You may even choose to

mix standard transitions with custom transitions that apply only to specific state changes.

■ Tip You may create a custom transition for several reasons. Here are some examples: to control the pace

of the animation with a more sophisticated animation, to use an animation easing, to run several animations in

succession (as in the FlipPanel example at the end of this chapter), or to play a sound at the same time as an

animation.

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

468

To define a custom transition, you place a storyboard with one or more animations

inside the VisualTransition element. Here’s an example that creates an elastic compression

effect when the user moves the mouse off a button:

<VisualStateGroup.Transitions>

<VisualTransition To="Normal" From="MouseOver" GeneratedDuration="0:0:0.7">

<Storyboard>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ScaleTransform"

Storyboard.TargetProperty="ScaleX">

<LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />

<LinearDoubleKeyFrame KeyTime="0:0:0.7" Value="1" />

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</VisualTransition>

</VisualStateGroup.Transitions>

■ Note When you use a custom transition, you must still set the VisualTransition.GeneratedDuration property

to match the duration of your animation. Without this detail, the VisualStateManager can’t use your transition,

and it will apply the new state immediately. (The actual time value you use still has no effect on your custom

transition, because it applies only to automatically generated animations. See the end of this section to learn

how you can mix and match a custom transition with automatically generated animations.)

This transition uses a key-frame animation. The first key frame compresses the button

horizontally until it disappears from view, and the second key frame causes it to spring back

into sight over a shorter interval of time. The transition animation works by adjusting the scale

of this ScaleTransform object, which is defined in the control template:

<Grid RenderTransformOrigin="0.5,0.5">

<Grid.RenderTransform>

<ScaleTransform x:Name="ScaleTransform" ScaleX="1" />

</Grid.RenderTransform>

...

</Grid>

When the transition is complete, the transition animation is stopped, and the

animated properties return to their original values (or the values that are set by the current state

animation). In this example, the animation returns the ScaleTransform to its initial ScaleX value

of 1, so you don’t notice any change when the transition animation ends.

It’s logical to assume that a custom transition animation like this one replaces the

automatically generated transition that the VisualStateManager would otherwise use. However,

this isn’t necessarily the case. Instead, it all depends whether your custom transition animates

the same properties as the VisualStateManager.

If your transition animates the same properties as the new state animation, your

transition replaces the automatically generated transition. In the current example, the

transition bridges the gap between the MouseOver state and the Normal state. The new state,

Normal, uses a zero-length animation to change the button’s background color. Thus, if you

don’t supply a custom animation for your transition, the VisualStateManager creates an

animation that smoothly shifts the background color from the old state to the new state.

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

469

So what happens if you throw a custom transition into the mix? If you create a custom

transition animation that targets the background color, the VisualStateManager will use your

animation instead of its default transition animation. But that’s not what happens in this

example. Here, the custom transition doesn’t modify the color–instead, it animates a

transform. For that reason, the VisualStateManager still generates an automatic animation to

change the background color. It uses its automatically generated animation in addition to your

custom transition animation, and it runs them both at the same time, giving the generated

transition the duration that’s set by the VisualTransition.GeneratedDuration property. In this

example, that means the new color fades in over 0.7 seconds, and at the same time the custom

transition animation applies the compression effect.

Understanding Parts with the Slider Control

In the parts and states model, the states dominate. Many controls, like Button, use templates

that define multiple state groups but no parts. But in other controls, like Slider, parts allow you

to wire up elements in the control template to key pieces of control functionality.

To understand how parts work, you need to consider a control that uses them. Often,

parts are found in controls that contain small working parts. For example, the DatePicker

control uses parts to identify the drop-down button that opens the calendar display and the text

box that shows the currently selected date. The ScrollBar control uses parts to delineate the

draggable thumb, the track, and the scroll buttons. The Slider control uses much the same set of

parts, although its scroll buttons are placed over the track, and they’re invisible. This allows the

user to move the slider thumb by clicking either side of the track.

A control indicates that it uses a specific part with the TemplatePart attribute. Here are

the TemplatePart attributes that decorate the Slider control:

[TemplatePart(Name="HorizontalTemplate", Type=typeof(FrameworkElement))]

[TemplatePart(Name="HorizontalTrackLargeChangeIncreaseRepeatButton",

Type=typeof(RepeatButton))]

[TemplatePart(Name="HorizontalTrackLargeChangeDecreaseRepeatButton",

Type=typeof(RepeatButton))]

[TemplatePart(Name="HorizontalThumb", Type=typeof(Thumb))]

[TemplatePart(Name="VerticalTemplate", Type=typeof(FrameworkElement))]

[TemplatePart(Name="VerticalTrackLargeChangeIncreaseRepeatButton",

Type=typeof(RepeatButton))]

[TemplatePart(Name="VerticalTrackLargeChangeDecreaseRepeatButton",

Type=typeof(RepeatButton))]

[TemplatePart(Name="VerticalThumb", Type=typeof(Thumb))]

[TemplateVisualState(Name="Disabled", GroupName="CommonStates")]

[TemplateVisualState(Name="Unfocused", GroupName="FocusStates")]

[TemplateVisualState(Name="MouseOver", GroupName="CommonStates")]

[TemplateVisualState(Name="Focused", GroupName="FocusStates")]

[TemplateVisualState(Name="Normal", GroupName="CommonStates")]

public class Slider: RangeBase

{ ... }

The Slider is complicated by the fact that it can be used in two different orientations,

which require two separate templates that are coded side by side. Here’s the basic structure:

<ControlTemplate TargetType="Slider">

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

470

<!-- This Grid groups the two orientations together in the same template.-->

<Grid>

<!-- This Grid is used for the horizontal orientation. -->

<Grid x:Name="HorizontalTemplate">

...

</Grid>

<!-- This Grid is used for the vertical orientation. -->

<Grid x:Name="VerticalTemplate">

...

</Grid>

</Grid>

</ControlTemplate>

If Slider.Orientation is Horizontal, the Slider shows the HorizontalTemplate element

and hides the VerticalTemplate element (if it exists). Usually, both of these elements are layout

containers. In this example, each one is a Grid that contains the rest of the markup for that

orientation.

When you understand that two distinct layouts are embedded in one control template,

you’ll realize that there are two sets of template parts to match. In this example, you’ll consider

a Slider that’s always used in horizontal orientation and so only provides the corresponding

horizontal parts: HorizontalTemplate, HorizontalTrackLargeChangeIncreaseRepeatButton,

HorizontalTrackLargeChangeDecreaseRepeatButton, and HorizontalThumb.

Figure 13-6 shows how these parts work together. Essentially, the thumb sits in the

middle, on the track. On the left and right are two invisible buttons that allow you to quickly

scroll the thumb to a new value by clicking one side of the track and holding down the mouse

button.

Figure 13-6. The named parts in the HorizontalTemplate part for the Slider

The TemplatePart attribute indicates the name the element must have, which is

critical because the control code searches for that element by name. It also indicates the

element type, which may be something very specific (such as Thumb, in the case of the

HorizontalThumb part) or something much more general (for example, FrameworkElement, in

the case of the HorizontalTemplate part, which allows you to use any element).

The fact that an element is used as a part in a control template tells you nothing about

how that element is used. However, there are a few common patterns:

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

471

• The control handles events from a part. For example, the Slider code searches for the

thumb when it’s initialized and attaches event handlers that react when the thumb is

clicked and dragged.

• The control changes the visibility of a part. For example, depending on the orientation,

the Slider shows or hides the HorizontalTemplate and VerticalTemplate parts.

• If a part isn’t present, the control doesn’t raise an exception. Depending on the

importance of the part, the control may continue to work (if at all possible), or an

important part of its functionality may be missing. For example, when dealing with the

Slider, you can safely omit HorizontalTrackLargeChangeIncreaseRepeatButton and

HorizontalTrackLargeChangeDecreaseRepeatButton. Even without these parts, you can

still set the Slider value by dragging the thumb. But if you omit the HorizontalThumb

element, you’ll end up with a much less useful Slider.

Figure 13-7 shows a customized Slider control. Here, a custom control template

changes the appearance of the track (using a gently rounded Rectangle element) and the thumb

(using a semitransparent circle).

Figure 13-7. A customized Slider control

To create this effect, your custom template must supply a HorizontalTemplate part. In

that HorizontalTemplate part, you must also include the HorizontalThumb part. The

TemplatePart attribute makes it clear that you can’t replace the Thumb control with another

element. However, you can customize the control template of the Thumb to modify its visual

appearance, as in this example.

Here’s the complete custom control template:

<ControlTemplate TargetType="Slider">

<Grid>

<Grid x:Name="HorizontalTemplate">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

CHAPTER 13 ■ TEMPLATES AND CUSTOM CONTROLS

472

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="*" />

</Grid.ColumnDefinitions>

<!-- The track -->

<Rectangle Stroke="SteelBlue" StrokeThickness="1" Fill="AliceBlue"

Grid.Column="0" Grid.ColumnSpan="3" Height="7" RadiusX="3" RadiusY="3" />

<!-- The left RepeatButton -->

<RepeatButton x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton"

Grid.Column="0" Background="Transparent" Opacity="0" IsTabStop="False" />

<!-- The Thumb -->

<Thumb x:Name="HorizontalThumb" Height="28" Width="28" Grid.Column="1">

<Thumb.Template>

<ControlTemplate TargetType="Thumb">

<Ellipse x:Name="Thumb" Opacity="0.3" Fill="AliceBlue"

Stroke="SteelBlue" StrokeThickness="3" Stretch="Fill"></Ellipse>

</ControlTemplate>

</Thumb.Template>

</Thumb>

<!-- The right RepeatButton -->

<RepeatButton x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton"

Grid.Column="2" Background="Transparent" Opacity="0" IsTabStop="False" />

</Grid>

<!-- Add VerticalTemplate here if desired. -->

</Grid>

</ControlTemplate>

CREATING SLICK CONTROL SKINS

The examples you’ve seen in this chapter demonstrate everything you need to know about the

parts and states model. But they lack one thing: eye candy. For example, although you now

understand the concepts you need to create customized Button and Slider controls, you haven’t

seen how to design the graphics that make a truly attractive control. And although the simple

animated effects you’ve seen here—color changing, pulsing, and scaling—are respectable, they

certainly aren’t eye-catching. To get more dramatic results, you need to get creative with the

graphics and animation skills you’ve picked up in earlier chapters.

To get an idea of what’s possible, you should check out the Silverlight control examples

that are available on the Web, including the many different glass and glow buttons that

developers have created. You can also apply new templates using the expansive set of themes

that are included with the Silverlight Toolkit (http://silverlight.codeplex.com). If you want

to restyle your controls, you’ll find that these themes give you a wide range of slick, professional

choices. Best of all, themes work automatically thanks to a crafty tool called the

Tải ngay đi em, còn do dự, trời tối mất!