Introducing the December 2005 CTP: What's New in Windows Presentation Foundation

 

Karsten Januszewski
Microsoft Corporation

December 2005

Summary: In December 2005, Microsoft released a Community Technology Preview (CTP) of Windows Presentation Foundation, the new presentation subsystem for Windows that unifies development of documents, graphics, and applications into a common platform. In this article, we take a brief look at some of the new features that are part of the December CTP as well as the changes that have been made since the previous releases. (24 printed pages)

Download the code sample, NovCTP.msi.

Contents

Introduction
   Avalon December Community Technical Preview
   Note on Community Technical Previews
Exploring New Features
   CompositionTarget
   PropertyTriggers
   Improved 3-D Databinding
Breaking Changes
   Mini-Language Removal
   Removal of ~ (Tilde) Syntax
   Removal of WebServiceDataProvider and Changes to ObjectDataProvider
   .WBA Change to .XBAP
   Control Changes
   3-D Changes
   Change to Visuals
   Media Changes
   HitTesting Changes
   ItemsControl Changes
   Typography Changes to Subscript and Superscript
   Menu Changes
   DocumentStructures Changes
   Removal of MonthCalendar and DatePicker
   TimeManager Removed
Acknowledgements

Introduction

The Microsoft Windows Presentation Foundation (formerly code-named "Avalon") provides the foundation for building applications and high fidelity experiences in Windows Vista, blending together application UI, documents, and media content, while exploiting the full power of your computer. The functionality extends to the support for Tablet and other forms of input, a more modern imaging and printing pipeline, accessibility and UI automation infrastructure, data-driven UI and visualization, as well as the integration points for weaving the application experience into the Windows shell.

For information about downloading the CTP, please see the release notes.

For an overview of the Microsoft Windows Presentation Foundation (WPF), see Architectural Overview of the Windows Presentation Foundation Beta 1 Release.

Avalon December Community Technical Preview

There are some exciting new features in this release, which will be enumerated below. And for those who worked with previous CTP releases, there are changes. This article will touch on some of those changes, but it is not comprehensive.

Note on Community Technical Previews

Community Technical Preview (CTP) builds do not go through the same rigorous testing that beta builds undergo. While betas receive a much higher level of testing and feature work, CTPs are intended to expose developers to the latest working build. CTPs are therefore unsupported, prerelease software and there are some precautions you should take. Please read the latest Readme and Installation Guides before you download or install prerelease software.

Exploring New Features

CompositionTarget

The WPF animation engine is powerful. The ability to wire up animations to any property on any Avalon element opens up many exciting possibilities for creating dynamic user interfaces. While the animation engine is incredibly powerful, it doesn't meet all scenarios, especially when conditions (e.g., physics, collisions) affect the course of an animation on a finer time scale, when animations need to create visuals, or when post-layout animations need to take place. Thus, the introduction of the CompositionTarget API, which allows the ability to create custom animations based on a per-frame callback.

The CompositionTarget.Rendering event fires during Avalon's rendering. To use CompositionTarget.Rendering you simply register an EventHandler delegate to the static Rendering method on CompositionTarget. You can create any effect you desire in the handler method and it will get called once per frame. Each time that Avalon marshals the visual tree across a channel into the composition tree and/or makes updates to the composition tree, your animation will be updated. This occurs after layout has been computed; however, if you affect layout in your delegate it will be computed once more before rendering. For more on the architecture of Avalon, see Chris Anderson and Greg Schechter's PDC talk.

Adding or removing a rendering delegate while the event is firing will be delayed until after the event is finished firing. This is consistent with how MulticastDelegate-based events are handled in the CLR. Also note that rendering events are not guaranteed to be called in any particular order. If you have multiple delegates that rely on a particular order, you should register a single Rendering event and multiplex them in the correct order yourself.

Let's take a look at a simple sample. The code sample below will create an application that randomly changes the background color on a Page. It should be noted that in practice you could do this with a custom "random color" animation; however, this example is meant to simply show how to hook the event rather than best practices of what to do in the event.

        Random rand = new Random();

        public Page1()
            : base()
        {
            CompositionTarget.Rendering += UpdateColor;
        }

        protected void UpdateColor(object sender, EventArgs e)
        {
            //set a random color
            this.Background = new SolidColorBrush(
                                Color.FromRgb((byte)rand.Next(255),
                                              (byte)rand.Next(255),
                                              (byte)rand.Next(255)));
        }

As you can see above, registering UpdateColor will cause the page's background brush to be updated every time Avalon renders this page. Running this can be a bit harsh on the eyes, but it gives you a sense of how the API works. The complete source for this is included in the download associated with this article.

If you discover that your animation runs at different speeds on different computers, it may be caused by the fact that the animation is not frame-rate independent. Depending on the system you are running and the workload of that system, the Rendering event may be called a different number of times per second. To make your animation frame-rate independent, you should base your animations on real time. One technique to achieve this would be to create a ParallelTimeline to track the elapsed time between frames. In the AnimateMotion sample, which demonstrates a rectangle that follows a mouse lagging behind with some physics effects, a TimeTracker class is available that implements this technique.

Lastly, you may be interested in creating effects that involved the creation of visuals, as opposed to simply manipulating visuals that are already part of the tree. The problem you may encounter is where to actually put the visual. For example, consider a fireworks effect. This effect would act like a decorator and whenever the child element is clicked, a burst of "fireworks" would animate outwards and fall down with gravity.

You could make the fireworks with Rectangle shapes and radial gradients, adding them to the parent of the effect. However, if the parent is a decorator itself, it can only have a single child, so it would impose a limitation on where your effect can be used. Also, the performance of adding many shapes is not as good as adding visuals to a drawing context. As such, you might decide to render the fireworks in the effects OnRender method. This too has a problem; the visuals from OnRender appear before (under) the child visuals. What we really want is a decorator-like class that has a method similar to OnRender, but that puts the visuals after the child. An example of how to implement such a class can be found in OverlayRenderDecorator.cs in the CreateVisuals sample.

Aa480187.intronovctp01(en-us,MSDN.10).jpg

Aa480187.intronovctp02(en-us,MSDN.10).jpg

PropertyTriggers

Triggers now support the EnterActions and ExitActions collections, which effectively allow for animations to be initiated on any kind of trigger. Because these collections have been added to the TriggerBase class, all the derived Trigger classes now support these collections. Before this CTP, only EventTriggers supported starting animations through its Actions collection, which allowed you to begin a storyboard when a routed event was fired. Now, all triggers have the ability to initiate animations. The ability to begin storyboard on these other kinds of triggers is important, because there are many cases where you want an animation to begin, but there isn't necessarily an event fired that you can subscribe to.

For example, animations can now be initiated based on the values of properties. This can be seen in the sample below, which starts an animation when the IsMouseOver property changes to true. It kicks off another animation when the property returns to false.

  <Grid xmlns="https://schemas.microsoft.com/winfx/avalon/2005" 
xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" >
    <Grid.Resources>
      <Style x:Key="PropertyTriggerExampleButtonStyle" 
TargetType="{x:Type Button}">
        <Setter Property="Opacity" Value="0.25" />
        <Setter Property="Button.Background">
          <Setter.Value>
            <SolidColorBrush Color="Orange" />
          </Setter.Value>
        </Setter>
        <Style.Triggers>
          <Trigger Property="IsMouseOver" Value="True">
            <Trigger.EnterActions>
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimation Storyboard.TargetProperty="Opacity"
                    To="1" Duration="0:0:1" />
                </Storyboard>
              </BeginStoryboard>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimation Storyboard.TargetProperty="Opacity"
                    To="0.25" Duration="0:0:1" />
                </Storyboard>
              </BeginStoryboard>
            </Trigger.ExitActions>
          </Trigger>
        </Style.Triggers>
      </Style>
    </Grid.Resources>
    <Button Height="100" Width="100" Style="{StaticResource PropertyTriggerExampleButtonStyle}">
    </Button>
  </Grid>

Where this gets even more interesting is with DataTriggers. You can imagine databinding to a Web service, for example, and if a certain value were returned by the Web service, an animation could be triggered.

Also, using MultiTriggers and MultiDataTriggers would allow these animations to be conditionally begun based on more than one property or data value.

Improved 3-D Databinding

DataBinding can only target DependencyProperties. In order to enable scenarios like databinding the height of a bar in a bar graph to the Y scale of a ScaleTransform3D, the X/Y/Z are now properties that are separate DependencyProperties of type double. This is consistent with the existing Offset, Scale, and Skew in 2-D transforms and with most other DependencyProperties in Avalon.

Old New
TranslateTransform3D.Offset TranslateTransform3D.OffsetX

TranslateTransform3D.OffsetY

TranslateTransform3D.OffsetZ

ScaleTransform3D.Scale

 

 

ScaleTransfrom3D.Center

ScaleTransform3D.ScaleX
ScaleTransform3D.ScaleY
ScaleTransform3D.ScaleZ
ScaleTransform3D.CenterX
ScaleTransform3D.CenterY
ScaleTransform3D.CenterZ
RotateTransform3D.Center RotateTransform3D.CenterX

RotateTransform3D.CenterY
RotateTransform3D.CenterZ

ScaleTransform.Center ScaleTransform.CenterX
ScaleTransform.CenterY
SkewTransform.Center SkewTransform.CenterX
SkewTransform.CenterY
RotateTransform.Center RotateTransform.CenterX
RotateTransform.CenterY

Here is an example of the types of databinding that can now be done with this new syntax. The sample binds a slider control to the OffsetX of the translation and the ScaleY of the scale. It also shows how the 3-D engine now supports databinding; in this case it databinds to the value of an XML datasource using the VisualBrush. Databinding is enabled by the use of the ModelVisual3D construct, which is detailed in the breaking changes below.

  <Grid xmlns="https://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005">
    <Grid.Resources>
      <Style x:Key="Horizontal" TargetType="{x:Type Slider}">
        <Setter Property = "IsSnapToTickEnabled" Value ="False"/>
        <Setter Property = "TickPlacement" Value ="BottomRight"/>
        <Setter Property = "TickFrequency" Value =".5"/>
        <Setter Property = "Minimum" Value ="-5"/>
        <Setter Property = "Maximum" Value ="5"/>
        <Setter Property = "Orientation" Value ="Horizontal"/>
        <Setter Property = "Value" Value ="1"/>
      </Style>
      <XmlDataProvider x:Key="ImageData">
        <data  >
          <text>TEXT.</text>
        </data>
      </XmlDataProvider>
      <MeshGeometry3D x:Key="meshData" 
                  Positions="-1 -1 0  1 -1 0  -1 1 0  1 1 0"
                  Normals="0 0 1  0 0 1  0 0 1  0 0 1"
                  TextureCoordinates="0 1  1 1  0 0  1 0   "
                  TriangleIndices="0 1 2  1 3 2" />
    </Grid.Resources>
    <StackPanel Orientation="Horizontal">
      <Label>Translate OffsetX</Label>
      <Slider Width="100" Style="{StaticResource Horizontal}" Name="sliderOffsetX"/>
      <Label>Scale ScaleY</Label>
      <Slider Width="100" Style="{StaticResource Horizontal}" Name="sliderScaleY"/>
    </StackPanel>
    <Grid>
      <Viewport3D Name="myViewport" >
        <ModelVisual3D>
          <ModelVisual3D.Content>
            <Model3DGroup >
              <Model3DGroup.Children>
                <DirectionalLight Color="#FFFFFFFF" Direction="-3,-4,-5" />
                <GeometryModel3D Geometry="{StaticResource meshData}">
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <VisualBrush >
                          <VisualBrush.Visual>
                            <TextBlock Text="{Binding Source={StaticResource ImageData}, XPath=data/text}" >
                            </TextBlock>
                          </VisualBrush.Visual>
                        </VisualBrush>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                  <GeometryModel3D.Transform>
                    <Transform3DGroup>
                      <ScaleTransform3D ScaleX="1" ScaleY="{Binding 
ElementName=sliderScaleY, Path=Value}" ScaleZ="1"/>
                      <TranslateTransform3D OffsetX="{Binding 
ElementName=sliderOffsetX, Path=Value}" OffsetY="-0" OffsetZ="1">
                      </TranslateTransform3D>
                      <RotateTransform3D>
                        <RotateTransform3D.Rotation>
                          <AxisAngleRotation3D  Axis="0,3,0" Angle="45" />
                        </RotateTransform3D.Rotation>
                      </RotateTransform3D>
                    </Transform3DGroup>
                  </GeometryModel3D.Transform>
                </GeometryModel3D>
              </Model3DGroup.Children>
            </Model3DGroup>
          </ModelVisual3D.Content>
        </ModelVisual3D>
        <Viewport3D.Camera>
          <PerspectiveCamera FarPlaneDistance="20"  UpDirection="0,1,0" 
NearPlaneDistance="1" Position="0,0,10" FieldOfView="45" />
        </Viewport3D.Camera>
      </Viewport3D>
    </Grid>
  </Grid>

Aa480187.intronovctp03(en-us,MSDN.10).jpg

Breaking Changes

Mini-Language Removal

The formats being removed are:

  • "HorizontalGradient color1 color2"
  • "VerticalGradient color1 color2"
  • "LinearGradient x1,y1 x2,y2 color1 color2"
  • "RadialGradient color1 color2"
  • "Image filename.extension"
  • The entire transform mini-language ("scale(1,2) rotate(45) ...")

For reference:

<Rectangle Fill="HorizontalGradient color1 color2" .../>

becomes:

<Rectangle ...>
  <Rectangle.Fill>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
      <LinearGradientBrush.GradientStops>
        <GradientStopCollection>
          <GradientStop Offset="0" Color="color1"/>
          <GradientStop Offset="1" Color="color2"/>
        </GradientStopCollection>
      </LinearGradientBrush.GradientStops>
    </LinearGradient>
  </Rectangle.Fill>
</Rectangle>

VerticalGradient is similar—just change EndPoint to "0,1", and LinearGradient simply sets StartPoint to x1,y1 and EndPoint to x2,y2. For RadialGradient, the default values for Center and Radii are used, so you needn't set them; similarly for ImageBrush—all values are default except Source="filename.extension".

For Transforms, you convert a string like "scale(1,2) rotate(45)..." to

<TransformGroup>
  <TransformGroup.Children>
    <ScaleTransform ScaleX="1" ScaleY="2"/>
    <RotateTransform Angle="45"/>
  </TransformGroup.Children>
</TransformGroup>

The existing mini-language for Matrix replaces the Transform language—that is, 6 doubles which represent m11, m12, m21, m22, offsetX, offsetY in an affine transform or "Identity".

There is a tool available in the download associated with this article that will ease the pain in porting your code to this new syntax.

Removal of ~ (Tilde) Syntax

WPF has removed support for ~/ used in relative Uris. Subsequent to this change, you will have to use the Pack Uri syntax in order to reference files at the site of origin. For instance, <Image Source="~/foo.jpg" /> changes to <Image Source="pack://siteoforigin:.../foo.jpg" />

Removal of WebServiceDataProvider and Changes to ObjectDataProvider

WebServiceDataSource is being deprecated, and its functionality is being merged into ObjectDataSource. ObjectDataProvider will no longer take TypeName, but instead take an ObjectType. In addition, ObjectDataProvider will no longer take Parameters in a string, but instead has parameter collections. Lastly, ObjectDataProvider can accept any object as its ObjectInstance, and will use the Data of a DataSourceProvider that's assigned to ObjectInstance.

Old way:

<Page>
  <Page.Resources>

    <ObjectDataProvider x:Key="myClass" TypeName="MyClass,MyAssembly" 
Parameters="some-string,2" />

    <WebServiceDataProvider x:Key="wsdp" Service="myServiceProxy,assembly" QueryMethod="wsMethod" />

New way:

<?Mapping XmlNamespace="local" ClrNamespace="System"?>
<?Mapping XmlNamespace="ws" ClrNamespace="Web.Service.Namespace" 
Assembly="assembly" ?>
<Page xmlns:local="local" xmlns:ws="ws">
  <Page.Resources>

    <ObjectDataProvider x:Key="myClass" ObjectType="{x:Type 
local:MyClass}">
      <ObjectDataProvider.ConstructorParameters>
        <x:string>some-string</x:string>
        <x:Int32>2</x:Int32>
      </ObjectDataProvider.ConstructorParameters>
    </ObjectDataProvider>

    <ObjectDataProvider x:Key="wsdp" ObjectType="{x:Type 
ws:myServiceProxy}" MethodName="wsMethod" />

.WBA Change to .XBAP

The WPF Web Browser Application file extension will change from ".wba" to ".xbap" and the MIME content type from "application/x-ms-wba" to "application/x-ms-xbap".

Control Changes

There are several changes to a number of controls in WPF:

  • Replaced TabStripPlacement enum with Dock

  • Removed BeginStoryboard.Placeholder event

  • Renamed DictionaryLocation to ResourceDictionaryLocation

    The System.Windows.DictionaryLocation enum is renamed to ResourceDictionaryLocation.

Any ThemeInfoAttributes will need to be modified:

Old:

[ThemeInfoAttribute(DictionaryLocation.*, DictionaryLocation.*)]

New:

[ThemeInfoAttribute(ResourceDictionaryLocation.*, 
ResourceDictionaryLocation.*)]
  • Remove Popup.HasDropShadow

    HasDropShadow property has been removed from popup.

  • Use PART_ names for ScrollBar and make IsStandAlone property Internal.

    Instead of specifying IsStandalone="false" on scrollbars in a ScrollViewer's template, now you should specify one of the following names: PART_HorizontalScrollBar or PART_VerticalScrollBar. ScrollBars outside of scrollviewer's templates will continue to work without modification.

Old:

      <!-- ScrollViewer's Control Template-->
                  <...>
                              <ScrollBar Orientation="Horizontal" 
IsStandalone="false" .../>
                              <ScrollBar Orientation="Vertical" 
IsStandalone="false" .../>
                  <...>
                        </ControlTemplate>

                        <!-- Standalone Scrollbar-- >
                        <ScrollBar IsStandalone="true"/>

New:

      <!-- ScrollViewer's Control Template-->
                  <…>
                              <ScrollBar Orientation="Horizontal" 
Name="PART_HorizontalScrollBar" .../>
                              <ScrollBar Orientation="Vertical" 
Name="PART_VerticalScrollBar" .../>
                  <...>
                        </ControlTemplate>

                        <!-- Standalone Scrollbar-- >
                        <ScrollBar/>

3-D Changes

The new Children collection hanging off of ViewPort3D is of type Visual3Dcollection and the Models collection has been removed. The primary type you will add to this Children collection is the new ModelVisual3D. ModelVisual3D has a Content property that takes a Model3D which of course Model3DGroup derives from. This is where all Model3DGroup objects will go, including lights and geometries. ModelVisual3D also has a Children property of type Visual3DCollection so that additional ModelVisual3D objects can be nested. 

In essence, this change is an additional layer between the viewport and the models, a visual layer.  Daniel Lehenbauer explains in a recent blog post exactly why this additional visual layer was added. He has a relatively deep discussion of the architectural underpinnings that governed the change.

Two things are gained from this change:

  1. Extensibility. Model3DGroup and GeometryModel3D are sealed, which meant you couldn't derive from them and create your classes. For example, you couldn't create your own cube class that could be instantiated directly in XAML.  But ModelVisual3D is not sealed, so the ability to inherit from it enables the ability to create your own classes that create reusable models.
  2. Databinding. With this change, databinding is directly enabled on properties of 3-D objects instead of the indirect databinding.

There have been additional API changes to the 3-D API.

The Up property has been renamed UpDirection and the LookAtPoint property will become LookDirection. The type will be a Vector3D instead of a Point3D. Camera will have a Transform property that transforms the Position, LookDirection, and UpDirection in the same way Model3D.Transform affects the position and direction of a SpotLight. This will enable "rotate this camera around the world"-like scenarios that are impossible to do with Point3D/Vector3D animations alone. This will also enable animations on MatrixCamera, which previously always required custom code to animate.

Lastly, Rotation3D is now abstract and its API is the new AxisAngleRotation3D. In XAML, this is a pure rename. Use AxisAngleRotation3D where you previously used Rotation3D.

If you interact with 3-D rotations from code there a few things you need to be aware of:

  • The return result of RotateTransform3D.Rotation is an abstract Rotation3D. You need to cast to AxisAngleRotation3D to get to the Axis/Angle properties.
  • The RotationTransform3D ctor that tacks an axis/angle has been removed. Instead, use the new RotateTransform3D(new AxisAngleRotation3D(axis, angle)).
  • The Rotation3D.Quaternion property has been removed; you can construct a Quaternion with the new Quaternion(aarot.Axis, aarot.Angle).

Change to Visuals

  • VisualTreeHelper (formerly VisualOperations) now serves only as a public "Getter" class and no longer provides "Set" methods. 

Media Changes

There have been some fundamental changes to the way audio and video is played in WPF. A new object has been introduced, called the MediaPlayer. The MediaPlayer object can be used in two different modes, depending on what is driving the player: Independent mode or Clock mode. When used in the Independent mode, the MediaPlayer object does not use the WPF timing system and directly plays the media. In Clock mode, the MediaPlayer can be thought of as a target for an animation, and thus it will have corresponding Timeline and Clock entries in the timing tree. Clock mode is useful if the media needs to be synched with other timelines; if no synching needs to occur, the overhead of creating a clock is not necessary and independent mode is sufficient.

The MediaPlayer.Clock property determines which mode the player is in: null implies Independent mode; non-null implies Clock mode.

The MediaElement is still in the API and is link between the MediaPlayer and the WPF framework, handling tasks like layout and composition of the actual media. The relationship between MediaPlayer and MediaElement is as follows: A MediaPlayer may be associated with a MediaElement, but a MediaElement always has an underlying MediaPlayer.

The MediaClock and MediaTimeline only have relevance if the MediaPlayer is used in Clock mode. They derive from the WPF animation system and work like all clocks and timelines, in that the timeline is a template for the clock; it is the MediaClock, if used, that actually drives the MediaPlayer.

The MediaPlayer is in Independent mode by default, but there are two ways to switch into Clock mode:

  1. Inside a Storyboard, when a MediaTimeline is targeted at a MediaElement, a MediaClock will be created and assigned to the MediaElement's associated player;
  2. By explicitly creating a MediaClock from a MediaTimeline and assigning it to a MediaPlayer object.

There are a few control options that are available only in Independent mode:

  • Setting a MediaPlayer.Source;
  • Using the Play, Pause, and Stop methods on MediaPlayer;
  • Changing MediaPlayer's Position and SpeedRatio properties.

If the user attempts to use these features while in Clock mode, an InvalidOperationException will be thrown. In this case, the Timing engine controls must be used for manipulating the media.

Setting the source of the media being played differs between independent mode and clock mode. This can be confusing, because there is a Source property on MediaElement, MediaPlayer, and MediaTimeline.

In Independent mode, the source can be set on either the MediaElement or the MediaPlayer. A change in MediaElement.Source will be reflected in MediaElement.Player.Source and vice-versa. Setting the source on MediaElement will immediately begin playing the audio/video; setting the source on the MediaPlayer, however, requires that media is actually started.

In clock mode, the source of the media must be set in the MediaTimeline. This has the effect of setting the source on the MediaPlayer. Note that the media will not start playing until the clock is assigned to a MediaPlayer.

The following code samples should help elucidate the new model.

Basic XAML Scenario

This would create a MediaPlayer, set the Source on the MediaElement, which sets MediaElement.Player.Source, which in turn will start to load the Media and play it.

<Canvas xmlns="https://schemas.microsoft.com/winfx/avalon/2005" 
xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005">
  <MediaElement Source = "ep2.wmv"/>
</Canvas>

Storyboard XAML Scenario

In this case, the MediaElement does not have a pre-specified Source. MediaElement.Player would still be created at load time, but with an empty Source. At BeginStoryboard, a MediaClock would be created on the MediaTimeline automatically, and it would be set onto myvideo.Player.Clock. Setting MediaPlayer.Clock also has the effect of setting MediaPlayer.Source.

<Canvas xmlns="https://schemas.microsoft.com/winfx/avalon/2005" 
xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005">

  <Canvas.Triggers>
    <EventTrigger RoutedEvent="Button.Click" SourceName="playButton">
      <EventTrigger.Actions>
        <BeginStoryboard Name= "myBegin">
          <Storyboard>
            <MediaTimeline Source="ep2.wmv" Storyboard.TargetName="myvideo"/>  
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Button.Click" SourceName="pauseButton">
      <EventTrigger.Actions>
        <PauseStoryboard BeginStoryboardName="myBegin" />
      </EventTrigger.Actions>
    </EventTrigger>
  </Canvas.Triggers>

  <StackPanel>
    <MediaElement Name="myvideo"/>
    <Button Name="playButton" >Play</Button>
    <Button Name="pauseButton" >Pause</Button>
  </StackPanel>

</Canvas>

Under the new APIs, preroll can be achieved through code by setting MPs.Source to the desired Uri. This can be done at page loaded, for example. Once a BeginStoryboard is called and ME.Player.Source is set, the MP would know that it's the same Uri and would retain the opened file.

Audio Scenario

<Canvas xmlns="https://schemas.microsoft.com/winfx/avalon/2005" 
xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005">
  <MediaElement Source="sound.wav"/>
</Canvas>

Changing Volume Properties

<Canvas xmlns="https://schemas.microsoft.com/winfx/avalon/2005" 
xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005">
  <MediaElement Source="ep2.wmv" Volume="0.2" Balance="-1"/>
</Canvas> 

Manipulating MediaElement with Transforms, Clip, and Opacity

<Canvas xmlns="https://schemas.microsoft.com/winfx/avalon/2005">

  <MediaElement Source="ep2.wmv" Opacity="0.7" >
    <MediaElement.LayoutTransform>
      <TransformGroup>
        <RotateTransform Center="200,100" Angle="305" />
      </TransformGroup>
    </MediaElement.LayoutTransform>
  </MediaElement>

  <MediaElement Source=" ep2.wmv" Opacity="0.5" >
    <MediaElement.Clip>
      <EllipseGeometry Center="200,100" RadiusX="150" RadiusY="100" />
    </MediaElement.Clip>
    <MediaElement.LayoutTransform>
      <TransformGroup>
        <SkewTransform AngleX="15" AngleY="15" />
        <RotateTransform Center="25,50" Angle="45" />
      </TransformGroup>
    </MediaElement.LayoutTransform>
  </MediaElement>

</Canvas>

Audio Scenario Using the Timing Engine

MediaPlayer player;

void startClick(object sender, RoutedEventArgs e)
{
  MediaTimeline mt = new MediaTimeline(new Uri("sound.wav"));
  MediaClock mc = mt.CreateClock();
  player.Clock = mc; // starts prerolling
}

void beginClick(object sender, RoutedEventArgs e)
{
  player.Clock.ClockController.Begin();
}
void stopClick(object sender, RoutedEventArgs e)
{
  player.Clock.ClockController.Stop();
}
void pauseClick(object sender, RoutedEventArgs e)
{
  player.Clock.ClockController.Pause();
}
void resumeClick(object sender, RoutedEventArgs e)
{
  player.Clock.ClockController.Resume();
}
void volumeIncClick(object sender, RoutedEventArgs e)
{
  player.Volume+=0.25;
}
void volumeDecClick(object sender, RoutedEventArgs e)
{
  player.Volume-=0.25;
}

Audio Scenario Without Using the Timing Engine

MediaPlayer player;

void startClick(object sender, RoutedEventArgs e)
{
  player.Clock = null; // this will ensure no clocks are driving this player
  player.Source = new Uri("sound.wav"); // starts prerolling
}

void beginClick(object sender, RoutedEventArgs e)
{
  player.Position = 0; 
  player.Play();
}
void stopClick(object sender, RoutedEventArgs e)
{
  player.Stop();
}
void pauseClick(object sender, RoutedEventArgs e)
{
  player.Pause();
}
void resumeClick(object sender, RoutedEventArgs e)
{
  player.Play();
}
void volumeIncClick(object sender, RoutedEventArgs e)
{
  player.Volume+=0.25;
}
void volumeDecClick(object sender, RoutedEventArgs e)
{
  player.Volume-=0.25;
}

Creating a Drawing Using the Timing Engine

Video can also be used in a Drawing, which can then be used in a DrawingBrush. This is an example where a MediaPlayer is used without a MediaElement:

private static Drawing MakeVideo()
{
  MediaTimeline mt = new MediaTimeline(new Uri("ep.wmv"));
  MediaClock mc = mt.CreateClock();
  MediaPlayer mp = new MediaPlayer();
  mp.Clock = mc;
  DrawingGroup dg = new DrawingGroup();
  using (DrawingContext ctx = dg.Open())
  {
    ctx.DrawVideo(mp, new Rect(0, 0, 500, 30));
  }

  return dg;
}

Creating a Drawing Without Using the Timing Engine

Video can also be used in a Drawing, which can then be used in a DrawingBrush.

private static Drawing MakeVideo()
{
  MediaPlayer mp = new MediaPlayer();
  mp.Source = new Uri("ep.wmv");
  DrawingGroup dg = new DrawingGroup();
  using (DrawingContext ctx = dg.Open())
  {
    ctx.DrawVideo(mp, new Rect(0, 0, 500, 30));
  }

  return dg;
}

Creating a VisualBrush

<Canvas xmlns="https://schemas.microsoft.com/winfx/avalon/2005">
  <Button Height="300" Width="500">
    <Button.Background>
      <VisualBrush>
        <VisualBrush.Visual>
          <MediaElement Source="ep2.wmv" />
        </VisualBrush.Visual>
      </VisualBrush>
    </Button.Background>
  </Button>
</Canvas>

HitTesting Changes

2-D Changes

Old New
HitTestFilter HitTestFilterCallback
HitTestResultDelegate HitTestResultCallback
HitTestResult.Visual HitTestResult.VisualHit
PointHitTestResult.Point PointHitTestResult.PointHit
GeometryHitTestResult.Geometry (Use transform services and/or VisualOperations.GetTransform() to transform the hit testing geometry.)

3-D Changes

Old New
Ray3DHitTestResult RayHitTestResult

All the above changes are purely renames without any change in behavior except for the removal of the GeometryHitTestResult.Geometry API. This property used to return the hit testing geometry transformed into the local space of the hit visual. There are still two ways to get this information with the Visual API:

  1. Use TransformToAncestor(hitVisual, startVisual), attempt to cast to MatrixTransform and apply the result to your hit geometry. This method will include the transforms applied by ImageEffects. However, if you have an ImageEffect applied with a non-linear transformation the cast may fail.
  2. Walk Visual parents from hitVisual to startVisual, concatenating the Visual transforms. This approach ignores ImageEffects.

ItemsControl Changes

Description

A plain ItemsControl (as opposed to a derived class like ListBox) now generates containers of type ContentPresenter, not ContentControl. Some consequences are:

  • The ItemContainerStyle (if any) must specify a target type of ContentPresenter, and the properties named in its setters must be applicable to ContentPresenter.
  • Any code that walks the visual tree looking for a ContentControl should be changed to look for a ContentPresenter instead.

Typography Changes to Subscript and Superscript

Removal of Subscript and Superscript Elements

Old:

<Superscript>Hello</Superscript>
<Subscript>Hello</Subscript>

New:

<Span Typography.Variants="Superscript">Hello</Span>
<Span Typography.Variants="Subscript">Hello</Span>

There have been some basic changes to the MenuItems:

<MenuItem Mode="Separator" /> is replaced with <Separator />.

<MenuItem Mode="Checkable" /> is replaced with <MenuItem IsCheckable="true" />.

DocumentStructures Changes

Break was renamed to StoryBreak.

Figure was renamed to FigureStructure.

List was renamed to ListStructure.

ListItem was renamed to ListItemStructure.

Paragraph was renamed to ParagraphStructure.

Section was renamed to SectionStructure.

Table was renamed to TableStructure.

TableCell was renamed to TableCellStructure.

TableRow was renamed to TableRowStructure.

TableRowGroup was renamed to TableRowGroupStructure.

Removal of MonthCalendar and DatePicker

The MonthCalendar and DatePicker controls have been deprecated.

TimeManager Removed

The TimeManager has been removed.

Acknowledgements

Thanks to Adam Smith for the mini-language fix tool, the Avalon animation team for the CompositionTarget samples, and the Avalon media team for the media samples.