달력

12

« 2025/12 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2017. 2. 8. 01:20

Commanding Overview 프로그래밍/WPF2017. 2. 8. 01:20


https://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx



Commanding Overview


Commanding is an input mechanism in Windows Presentation Foundation (WPF) which provides input handling at a more semantic level than device input. Examples of commands are the Copy, Cut, and Paste operations found on many applications.

This overview defines what commands are in WPF, which classes are part of the commanding model, and how to use and create commands in your applications.

This topic contains the following sections:

What Are Commands?

Commands have several purposes. The first purpose is to separate the semantics and the object that invokes a command from the logic that executes the command. This allows for multiple and disparate sources to invoke the same command logic, and it allows the command logic to be customized for different targets. For example, the editing operations CopyCut, and Paste, which are found in many applications, can be invoked by using different user actions if they are implemented by using commands. An application might allow a user to cut selected objects or text by either clicking a button, choosing an item in a menu, or using a key combination, such as CTRL+X. By using commands, you can bind each type of user action to the same logic.

Another purpose of commands is to indicate whether an action is available. To continue the example of cutting an object or text, the action only makes sense when something is selected. If a user tries to cut an object or text without having anything selected, nothing would happen. To indicate this to the user, many applications disable buttons and menu items so that the user knows whether it is possible to perform an action. A command can indicate whether an action is possible by implementing the CanExecute method. A button can subscribe to the CanExecuteChanged event and be disabled if CanExecute returns false or be enabled if CanExecute returns true.

The semantics of a command can be consistent across applications and classes, but the logic of the action is specific to the particular object acted upon. The key combination CTRL+X invokes the Cut command in text classes, image classes, and Web browsers, but the actual logic for performing the Cut operation is defined by the application that performs the cut. A RoutedCommand enables clients to implement the logic. A text object may cut the selected text into the clipboard, while an image object may cut the selected image. When an application handles the Executed event, it has access to the target of the command and can take appropriate action depending on the target's type.

Simple Command Example in WPF

The simplest way to use a command in WPF is to use a predefined RoutedCommand from one of the command library classes; use a control that has native support for handling the command; and use a control that has native support for invoking a command. The Paste command is one of the predefined commands in the ApplicationCommands class. The TextBox control has built in logic for handling the Paste command. And the MenuItem class has native support for invoking commands.

The following example shows how to set up a MenuItem so that when it is clicked it will invoke the Paste command on a TextBox, assuming the TextBox has keyboard focus.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

Four Main Concepts in WPF Commanding

The routed command model in WPF can be broken up into four main concepts: the command, the command source, the command target, and the command binding:

  • The command is the action to be executed.

  • The command source is the object which invokes the command.

  • The command target is the object that the command is being executed on.

  • The command binding is the object which maps the command logic to the command.

In the previous example, the Paste command is the command, the MenuItem is the command source, the TextBox is the command target, and the command binding is supplied by the TextBox control. It is worth noting that it is not always the case that the CommandBinding is supplied by the control that is the command target class. Quite often the CommandBinding must be created by the application developer, or the CommandBinding might be attached to an ancestor of the command target.

Commands

Commands in WPF are created by implementing the ICommand interface. ICommand exposes two methods, Execute, and CanExecute, and an event, CanExecuteChangedExecute performs the actions that are associated with the command. CanExecute determines whether the command can execute on the current command target. CanExecuteChanged is raised if the command manager that centralizes the commanding operations detects a change in the command source that might invalidate a command that has been raised but not yet executed by the command binding. The WPF implementation of ICommand is the RoutedCommand class and is the focus of this overview.

The main sources of input in WPF are the mouse, the keyboard, ink, and routed commands. The more device-oriented inputs use a RoutedEvent to notify objects in an application page that an input event has occurred. A RoutedCommand is no different. The Execute and CanExecute methods of a RoutedCommand do not contain the application logic for the command, but rather they raise routed events that tunnel and bubble through the element tree until they encounter an object with a CommandBinding. The CommandBinding contains the handlers for these events and it is the handlers that perform the command. For more information on event routing in WPF, see Routed Events Overview.

The Execute method on a RoutedCommand raises the PreviewExecuted and the Executed events on the command target. The CanExecute method on a RoutedCommand raises the CanExecute and PreviewCanExecute events on the command target. These events tunnel and bubble through the element tree until they encounter an object which has a CommandBinding for that particular command.

WPF supplies a set of common routed commands spread across several classes: MediaCommandsApplicationCommandsNavigationCommandsComponentCommands, and EditingCommands. These classes consist only of the RoutedCommand objects and not the implementation logic of the command. The implementation logic is the responsibility of the object on which the command is being executed on.

Command Sources

A command source is the object which invokes the command. Examples of command sources are MenuItemButton, and KeyGesture.

Command sources in WPF generally implement the ICommandSource interface.

 

ICommandSource exposes three properties: CommandCommandTarget, and CommandParameter:

The WPF classes that implement ICommandSource are ButtonBaseMenuItemHyperlink, and InputBindingButtonBaseMenuItem, and Hyperlink invoke a command when they are clicked, and an InputBinding invokes a command when the InputGesture associated with it is performed.

The following example shows how to use a MenuItem in a ContextMenu as a command source for the Properties command.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;

Typically, a command source will listen to the CanExecuteChanged event. This event informs the command source that the ability of the command to execute on the current command target may have changed. The command source can query the current status of the RoutedCommand by using the CanExecute method. The command source can then disable itself if the command cannot execute. An example of this is a MenuItem graying itself out when a command cannot execute.

An InputGesture can be used as a command source. Two types of input gestures in WPF are the KeyGesture and MouseGesture. You can think of a KeyGesture as a keyboard shortcut, such as CTRL+C. A KeyGesture is comprised of a Key and a set of ModifierKeys. A MouseGesture is comprised of a MouseAction and an optional set of ModifierKeys.

In order for an InputGesture to act as a command source, it must be associated with a command. There are a few ways to accomplish this. One way is to use an InputBinding.

The following example shows how to create a KeyBinding between a KeyGesture and a RoutedCommand.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);

Another way to associate an InputGesture to a RoutedCommand is to add the InputGesture to the InputGestureCollection on the RoutedCommand.

The following example shows how to add a KeyGesture to the InputGestureCollection of a RoutedCommand.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);

CommandBinding

CommandBinding associates a command with the event handlers that implement the command.

The CommandBinding class contains a Command property, and PreviewExecutedExecutedPreviewCanExecute, and CanExecute events.

Command is the command that the CommandBinding is being associated with. The event handlers which are attached to the PreviewExecuted and Executed events implement the command logic. The event handlers attached to the PreviewCanExecute and CanExecute events determine if the command can execute on the current command target.

The following example shows how to create a CommandBinding on the root Window of an application. The CommandBinding associates the Open command with Executed and CanExecute handlers.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);

Next, the ExecutedRoutedEventHandler and a CanExecuteRoutedEventHandler are created. The ExecutedRoutedEventHandler opens a MessageBox that displays a string saying the command has been executed. The CanExecuteRoutedEventHandler sets the CanExecute property to true.

void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

CommandBinding is attached to a specific object, such as the root Window of the application or a control. The object that the CommandBinding is attached to defines the scope of the binding. For example, a CommandBinding attached to an ancestor of the command target can be reached by the Executed event, but a CommandBinding attached to a descendant of the command target cannot be reached. This is a direct consequence of the way a RoutedEvent tunnels and bubbles from the object that raises the event.

In some situations the CommandBinding is attached to the command target itself, such as with the TextBox class and the CutCopy, and Paste commands. Quite often though, it is more convenient to attach the CommandBinding to an ancestor of the command target, such as the main Window or the Application object, especially if the same CommandBinding can be used for multiple command targets. These are design decisions you will want to consider when you are creating your commanding infrastructure.

Command Target

The command target is the element on which the command is executed. With regards to a RoutedCommand, the command target is the element at which routing of the Executed and CanExecute starts. As noted previously, in WPF the CommandTarget property on ICommandSource is only applicable when the ICommand is a RoutedCommand. If the CommandTarget is set on an ICommandSource and the corresponding command is not a RoutedCommand, the command target is ignored.

The command source can explicitly set the command target. If the command target is not defined, the element with keyboard focus will be used as the command target. One of the benefits of using the element with keyboard focus as the command target is that it allows the application developer to use the same command source to invoke a command on multiple targets without having to keep track of the command target. For example, if a MenuItem invokes the Paste command in an application that has a TextBox control and a PasswordBox control, the target can be either the TextBox or PasswordBox depending on which control has keyboard focus.

The following example shows how to explicitly set the command target in markup and in code behind.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

The CommandManager

The CommandManager serves a number of command related functions. It provides a set of static methods for adding and removing PreviewExecutedExecutedPreviewCanExecute, and CanExecute event handlers to and from a specific element. It provides a means to register CommandBinding and InputBinding objects onto a specific class. The CommandManager also provides a means, through the RequerySuggested event, to notify a command when it should raise the CanExecuteChanged event.

The InvalidateRequerySuggested method forces the CommandManager to raise the RequerySuggested event. This is useful for conditions that should disable/enable a command but are not conditions that the CommandManager is aware of.

Command Library

WPF provides a set of predefined commands. The command library consists of the following classes: ApplicationCommandsNavigationCommandsMediaCommandsEditingCommands, and the ComponentCommands. These classes provide commands such as CutBrowseBack and BrowseForwardPlayStop, and Pause.

Many of these commands include a set of default input bindings. For example, if you specify that your application handles the copy command, you automatically get the keyboard binding "CTRL+C" You also get bindings for other input devices, such as Tablet PC pen gestures and speech information.

When you reference commands in the various command libraries using XAML, you can usually omit the class name of the library class that exposes the static command property. Generally, the command names are unambiguous as strings, and the owning types exist to provide a logical grouping of commands but are not necessary for disambiguation. For instance, you can specify Command="Cut" rather than the more verbose Command="ApplicationCommands.Cut". This is a convenience mechanism that is built in to the WPF XAML processor for commands (more precisely, it is a type converter behavior of ICommand, which the WPF XAML processor references at load time).

Creating Custom Commands

If the commands in the command library classes do not meet your needs, then you can create your own commands. There are two ways to create a custom command. The first is to start from the ground up and implement the ICommand interface. The other way, and the more common approach, is to create a RoutedCommand or a RoutedUICommand.

For an example of creating a custom RoutedCommand, see Create a Custom RoutedCommand Sample.



'프로그래밍 > WPF' 카테고리의 다른 글

Properties  (0) 2017.01.24
Graphics and Multimedia - Multimedia, Visual Layer  (0) 2017.01.14
Data Binding  (0) 2017.01.10
Graphics and Multimedia - Animation Overview  (0) 2017.01.07
Graphics and Multimedia  (0) 2016.11.24
:
Posted by 지훈2
2017. 1. 24. 17:20

Properties 프로그래밍/WPF2017. 1. 24. 17:20


  1. Dependency Properties Overview



Dependency Properties Overview


https://msdn.microsoft.com/en-us/library/ms752914(v=vs.110).aspx




Windows Presentation Foundation (WPF) provides a set of services that can be used to extend the functionality of a common language runtime (CLR) property. Collectively, these services are typically referred to as the WPF property system. A property that is backed by the WPF property system is known as a dependency property. This overview describes the WPF property system and the capabilities of a dependency property. This includes how to use existing dependency properties in XAML and in code. This overview also introduces specialized aspects of dependency properties, such as dependency property metadata, and how to create your own dependency property in a custom class.

Prerequisites

This topic assumes that you have some basic knowledge of the CLR and object-oriented programming. In order to follow the examples in this topic, you should also understand XAML and know how to write WPF applications. For more information, see Walkthrough: My First WPF Desktop Application.

Dependency Properties and CLR Properties

In WPF, properties are typically exposed as common language runtime (CLR) properties. At a basic level, you could interact with these properties directly and never know that they are implemented as a dependency property. However, you should become familiar with some or all of the features of the WPF property system, so that you can take advantage of these features.

The purpose of dependency properties is to provide a way to compute the value of a property based on the value of other inputs. These other inputs might include system properties such as themes and user preference, just-in-time property determination mechanisms such as data binding and animations/storyboards, multiple-use templates such as resources and styles, or values known through parent-child relationships with other elements in the element tree. In addition, a dependency property can be implemented to provide self-contained validation, default values, callbacks that monitor changes to other properties, and a system that can coerce property values based on potentially runtime information. Derived classes can also change some specific characteristics of an existing property by overriding dependency property metadata, rather than overriding the actual implementation of existing properties or creating new properties.

In the SDK reference, you can identify which property is a dependency property by the presence of the Dependency Property Information section on the managed reference page for that property. The Dependency Property Information section includes a link to the DependencyProperty identifier field for that dependency property, and also includes a list of the metadata options that are set for that property, per-class override information, and other details.

Dependency Properties Back CLR Properties

Dependency properties and the WPF property system extend property functionality by providing a type that backs a property, as an alternative implementation to the standard pattern of backing the property with a private field. The name of this type is DependencyProperty. The other important type that defines the WPF property system is DependencyObjectDependencyObject defines the base class that can register and own a dependency property

Following is a summation of the terminology that is used in this software development kit (SDK) documentation when discussing dependency properties:

  • Dependency property: A property that is backed by a DependencyProperty.

  • Dependency property identifier: A DependencyProperty instance, which is obtained as a return value when registering a dependency property, and then stored as a static member of a class. This identifier is used as a parameter for many of the APIs that interact with the WPF property system.

  • CLR "wrapper": The actual get and set implementations for the property. These implementations incorporate the dependency property identifier by using it in the GetValue and SetValue calls, thus providing the backing for the property using the WPF property system.

The following example defines the IsSpinning dependency property, and shows the relationship of the DependencyProperty identifier to the property that it backs.

public static readonly DependencyProperty IsSpinningProperty = 
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

The naming convention of the property and its backing DependencyProperty field is important. The name of the field is always the name of the property, with the suffix Property appended. For more information about this convention and the reasons for it, see Custom Dependency Properties.

Setting Property Values

You can set properties either in code or in XAML.

Setting Property Values in XAML

The following XAML example specifies the background color of a button as red. This example illustrates a case where the simple string value for a XAML attribute is type-converted by the WPF XAML parser into a WPF type (a Color, by way of a SolidColorBrush) in the generated code.

<Button Background="Red" Content="Button!"/>

XAML supports a variety of syntax forms for setting properties. Which syntax to use for a particular property will depend on the value type that a property uses, as well as other factors such as the presence of a type converter. For more information on XAML syntax for property setting, see XAML Overview (WPF) and XAML Syntax In Detail.

As an example of non-attribute syntax, the following XAML example shows another button background. This time rather than setting a simple solid color, the background is set to an image, with an element representing that image and the source of that image specified as an attribute of the nested element. This is an example of property element syntax.

<Button Content="Button!">
  <Button.Background>
    <ImageBrush ImageSource="wavy.jpg"/>
  </Button.Background>
</Button>

Setting Properties in Code

Setting dependency property values in code is typically just a call to the set implementation exposed by the CLR "wrapper". 

Button myButton = new Button();
myButton.Width = 200.0;

Getting a property value is also essentially a call to the get "wrapper" implementation:

double whatWidth;
whatWidth = myButton.Width;

You can also call the property system APIs GetValue and SetValue directly. This is not typically necessary if you are using existing properties (the wrappers are more convenient, and provide better exposure of the property for developer tools), but calling the APIs directly is appropriate for certain scenarios.

Properties can be also set in XAML and then accessed later in code, through code-behind. For details, see Code-Behind and XAML in WPF.

Property Functionality Provided by a Dependency Property

A dependency property provides functionality that extends the functionality of a property as opposed to a property that is backed by a field. Often, each such functionality represents or supports a specific feature of the overall WPF set of features:

Resources

A dependency property value can be set by referencing a resource. Resources are typically specified as the Resources property value of a page root element, or of the application (these locations enable the most convenient access to the resource). The following example shows how to define a SolidColorBrush resource.

<DockPanel.Resources>
  <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</DockPanel.Resources>

Once the resource is defined, you can reference the resource and use it to provide a property value:

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

This particular resource is referenced as a DynamicResource Markup Extension (in WPF XAML, you can use either a static or dynamic resource reference). To use a dynamic resource reference, you must be setting to a dependency property, so it is specifically the dynamic resource reference usage that is enabled by the WPF property system. For more information, see XAML Resources.

System_CAPS_noteNote

Resources are treated as a local value, which means that if you set another local value, you will eliminate the resource reference. For more information, see Dependency Property Value Precedence.

Data Binding

A dependency property can reference a value through data binding. Data binding works through a specific markup extension syntax in XAML, or the Binding object in code. With data binding, the final property value determination is deferred until run time, at which time the value is obtained from a data source.

The following example sets the Content property for a Button, using a binding declared in XAML. The binding uses an inherited data context and an XmlDataProvider data source (not shown). The binding itself specifies the desired source property by XPath within the data source.

<Button Content="{Binding XPath=Team/@TeamName}"/>
System_CAPS_noteNote

Bindings are treated as a local value, which means that if you set another local value, you will eliminate the binding. For details, see Dependency Property Value Precedence.

Dependency properties, or the DependencyObject class, do not natively support INotifyPropertyChanged for purposes of producing notifications of changes in DependencyObject source property value for data binding operations. For more information on how to create properties for use in data binding that can report changes to a data binding target, see Data Binding Overview.

Styles

Styles and templates are two of the chief motivating scenarios for using dependency properties. Styles are particularly useful for setting properties that define application user interface (UI). Styles are typically defined as resources in XAML. Styles interact with the property system because they typically contain "setters" for particular properties, as well as "triggers" that change a property value based on the real-time value for another property.

The following example creates a very simple style (which would be defined inside a Resources dictionary, not shown), then applies that style directly to the Style property for a Button. The setter within the style sets the Background property for a styled Button to green.

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>

For more information, see Styling and Templating.

Animations

Dependency properties can be animated. When an animation is applied and is running, the animated value operates at a higher precedence than any value (such as a local value) that the property otherwise has.

The following example animates the Background on a Button property (technically, the Background is animated by using property element syntax to specify a blank SolidColorBrush as the Background, then the Color property of that SolidColorBrush is the property that is directly animated).

<Button>I am animated
  <Button.Background>
    <SolidColorBrush x:Name="AnimBrush"/>
  </Button.Background>
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation
            Storyboard.TargetName="AnimBrush" 
            Storyboard.TargetProperty="(SolidColorBrush.Color)"
            From="Red" To="Green" Duration="0:0:5" 
            AutoReverse="True" RepeatBehavior="Forever" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

For more information on animating properties, see Animation Overview and Storyboards Overview.

Metadata Overrides

You can change certain behaviors of a dependency property by overriding the metadata for that property when you derive from the class that originally registers the dependency property. Overriding metadata relies on the DependencyProperty identifier. Overriding metadata does not require re-implementing the property. The metadata change is handled natively by the property system; each class potentially holds individual metadata for all properties that are inherited from base classes, on a per-type basis.

The following example overrides metadata for a dependency property DefaultStyleKey. Overriding this particular dependency property metadata is part of an implementation pattern that creates controls that can use default styles from themes.

public class SpinnerControl : ItemsControl
{
    static SpinnerControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl), 
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
    }
}

For more information about overriding or obtaining property metadata, see Dependency Property Metadata.

Property Value Inheritance

An element can inherit the value of a dependency property from its parent in the object tree.

System_CAPS_noteNote

Property value inheritance behavior is not globally enabled for all dependency properties, because the calculation time for inheritance does have some performance impact. Property value inheritance is typically only enabled for properties where a particular scenario suggests that property value inheritance is appropriate. You can determine whether a dependency property inherits by looking at the Dependency Property Information section for that dependency property in the SDK reference.

The following example shows a binding, and sets the DataContext property that specifies the source of the binding, which was not shown in the earlier binding example. Any subsequent bindings in child objects do not need to specify the source, they can use the inherited value from DataContext in the parent StackPanel object. (Alternatively, a child object could instead choose to directly specify its own DataContext or a Source in the Binding, and to deliberately not use the inherited value for data context of its bindings.)

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}">
  <Button Content="{Binding XPath=Team/@TeamName}"/>
</StackPanel>

For more information, see Property Value Inheritance.

WPF Designer Integration

A custom control with properties that are implemented as dependency properties will receive appropriate WPF Designer for Visual Studio support. One example is the ability to edit direct and attached dependency properties with the Properties window. For more information, see Control Authoring Overview.

Dependency Property Value Precedence

When you get the value of a dependency property, you are potentially obtaining a value that was set on that property through any one of the other property-based inputs that participate in the WPF property system. Dependency property value precedence exists so that a variety of scenarios for how properties obtain their values can interact in a predictable way.

Consider the following example. The example includes a style that applies to all buttons and their Background properties, but then also specifies one button with a locally set Background value.

System_CAPS_noteNote

The SDK documentation uses the terms "local value" or "locally set value" occasionally when discussing dependency properties. A locally set value is a property value that is set directly on an object instance in code, or as an attribute on an element in XAML.

In principle, for the first button, the property is set twice, but only one value applies: the value with the highest precedence. A locally set value has the highest precedence (except for a running animation, but no animation applies in this example) and thus the locally set value is used instead of the style setter value for the background on the first button. The second button has no local value (and no other value with higher precedence than a style setter) and thus the background in that button comes from the style setter.

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
     <Setter Property="Background" Value="Red"/>
    </Style>
  </StackPanel.Resources>
  <Button Background="Green">I am NOT red!</Button>
  <Button>I am styled red</Button>
</StackPanel>

Why Does Dependency Property Precedence Exist?

Typically, you would not want styles to always apply and to obscure even a locally set value of an individual element (otherwise, it would be very difficult to use either styles or elements in general). Therefore, the values that come from styles operate at a lower precedent than a locally set value. For a more thorough listing of dependency properties and where a dependency property effective value might come from, see Dependency Property Value Precedence.

System_CAPS_noteNote

There are a number of properties defined on WPF elements that are not dependency properties. By and large, properties were implemented as dependency properties only when there were needs to support at least one of the scenarios enabled by the property system: data binding, styling, animation, default value support, inheritance, attached properties, or invalidation.

Learning More About Dependency Properties

  • An attached property is a type of property that supports a specialized syntax in XAML. An attached property often does not have a 1:1 correspondence with a common language runtime (CLR) property, and is not necessarily a dependency property. The typical purpose of a attached property is to allow child elements to report property values to a parent element, even if the parent element and child element do not both possess that property as part of the class members listings. One primary scenario is to enable child elements to inform the parent how they should be presented in UI; for an example, see Dock or Left. For details, see Attached Properties Overview.

  • Component developers or application developers may wish to create their own dependency property, in order to enable capabilities such as data binding or styles support, or for invalidation and value coercion support. For details, see Custom Dependency Properties.

  • Dependency properties should generally be considered to be public properties, accessible or at least discoverable by any caller that has access to an instance. For more information, see Dependency Property Security.





Attached Properties Overview


https://msdn.microsoft.com/en-us/library/ms749011(v=vs.110).aspx




An attached property is a concept defined by XAML. An attached property is intended to be used as a type of global property that is settable on any object. In Windows Presentation Foundation (WPF), attached properties are typically defined as a specialized form of dependency property that does not have the conventional property "wrapper".

Prerequisites

This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on Windows Presentation Foundation (WPF) classes, and have read the Dependency Properties Overview. To follow the examples in this topic, you should also understand XAML and know how to write WPF applications.

Why Use Attached Properties

One purpose of an attached property is to allow different child elements to specify unique values for a property that is actually defined in a parent element. A specific application of this scenario is having child elements inform the parent element of how they are to be presented in the user interface (UI). One example is the DockPanel.Dock property. The DockPanel.Dock property is created as an attached property because it is designed to be set on elements that are contained within a DockPanel, rather than on DockPanel itself. The DockPanel class defines the static DependencyProperty field named DockProperty, and then provides the GetDock and SetDock methods as public accessors for the attached property.

Attached Properties in XAML

In XAML, you set attached properties by using the syntax AttachedPropertyProvider.PropertyName 

The following is an example of how you can set DockPanel.Dock in XAML:

<DockPanel>
  <CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>

Note that the usage is somewhat similar to a static property; you always reference the type DockPanel that owns and registers the attached property, rather than referring to any instance specified by name.

Also, because an attached property in XAML is an attribute that you set in markup, only the set operation has any relevance. You cannot directly get a property in XAML, although there are some indirect mechanisms for comparing values, such as triggers in styles (for details, see Styling and Templating).

Attached Property Implementation in WPF

In Windows Presentation Foundation (WPF), most of the attached properties that exist on WPF types that are related to UI presentation are implemented as dependency properties. Attached properties are a XAML concept, whereas dependency properties are a WPF concept. Because WPF attached properties are dependency properties, they support dependency property concepts such as property metadata, and default values from that property metadata.

How Attached Properties Are Used by the Owning Type

Although attached properties are settable on any object, that does not automatically mean that setting the property will produce a tangible result, or that the value will ever be used by another object. Generally, attached properties are intended so that objects coming from a wide variety of possible class hierarchies or logical relationships can each report common information to the type that defines the attached property. The type that defines the attached property typically follows one of these models:

  • The type that defines the attached property is designed so that it can be the parent element of the elements that will set values for the attached property. The type then iterates its child objects through internal logic against some object tree structure, obtains the values, and acts on those values in some manner.

  • The type that defines the attached property will be used as the child element for a variety of possible parent elements and content models.

  • The type that defines the attached property represents a service. Other types set values for the attached property. Then, when the element that set the property is evaluated in the context of the service, the attached property values are obtained through internal logic of the service class.

An Example of a Parent-Defined Attached Property

The most typical scenario where WPF defines an attached property is when a parent element supports a child element collection, and also implements a behavior where the specifics of the behavior are reported individually for each child element.

DockPanel defines the DockPanel.Dock attached property, and DockPanel has class-level code as part of its rendering logic (specifically, MeasureOverride and ArrangeOverride). A DockPanel instance will always check to see whether any of its immediate child elements have set a value for DockPanel.Dock. If so, those values become input for the rendering logic applied to that particular child element. Nested DockPanel instances each treat their own immediate child element collections, but that behavior is implementation-specific to how DockPanel processes DockPanel.Dock values. It is theoretically possible to have attached properties that influence elements beyond the immediate parent. If the DockPanel.Dock attached property is set on an element that has no DockPanel parent element to act upon it, no error or exception is raised. This simply means that a global property value was set, but it has no current DockPanel parent that could consume the information.

Attached Properties in Code

Attached properties in WPF do not have the typical CLR "wrapper" methods for easy get/set access. This is because the attached property is not necessarily part of the CLR namespace for instances where the property is set. However, a XAML processor must be able to set those values when XAML is parsed. To support an effective attached property usage, the owner type of the attached property must implement dedicated accessor methods in the form GetPropertyName and SetPropertyName. These dedicated accessor methods are also useful to get or set the attached property in code. From a code perspective, an attached property is similar to a backing field that has method accessors instead of property accessors, and that backing field can exist on any object rather than needing to be specifically defined.

The following example shows how you can set an attached property in code. In this example, myCheckBox is an instance of the CheckBox class.

DockPanel myDockPanel = new DockPanel();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myDockPanel.Children.Add(myCheckBox);
DockPanel.SetDock(myCheckBox, Dock.Top);

Similar to the XAML case, if myCheckBox had not already been added as a child element of myDockPanel by the third line of code, the fourth line of code would not raise an exception, but the property value would not interact with a DockPanel parent and thus would do nothing. Only a DockPanel.Dock value set on a child element combined with the presence of a DockPanel parent element will cause an effective behavior in the rendered application. (In this case, you could set the attached property, then attach to the tree. Or you could attach to the tree then set the attached property. Either action order provides the same result.)

Attached Property Metadata

When registering the property, FrameworkPropertyMetadata is set to specify characteristics of the property, such as whether the property affects rendering, measurement, and so on. Metadata for an attached property is generally no different than on a dependency property. If you specify a default value in an override to attached property metadata, that value becomes the default value of the implicit attached property on instances of the overriding class. Specifically, your default value is reported if some process queries for the value of an attached property through the Get method accessor for that property, specifying an instance of the class where you specified the metadata, and the value for that attached property was otherwise not set.

If you want to enable property value inheritance on a property, you should use attached properties rather than non-attached dependency properties. For details, see Property Value Inheritance.

Custom Attached Properties

When to Create an Attached Property

You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class. The most common scenario for this is layout. Examples of existing layout properties are DockPanel.DockPanel.ZIndex, and Canvas.Top. The scenario enabled here is that elements that exist as child elements to layout-controlling elements are able to express layout requirements to their layout parent elements individually, each setting a property value that the parent defined as an attached property.

Another scenario for using an attached property is when your class represents a service, and you want classes to be able to integrate the service more transparently.

Yet another scenario is to receive Visual Studio 2008 WPF Designer support, such as Properties window editing. For more information, see Control Authoring Overview.

As mentioned before, you should register as an attached property if you want to use property value inheritance.

How to Create an Attached Property

If your class is defining the attached property strictly for use on other types, then the class does not have to derive from DependencyObject. But you do need to derive from DependencyObject if you follow the overall WPF model of having your attached property also be a dependency property.

Define your attached property as a dependency property by declaring a public static readonly field of type DependencyProperty. You define this field by using the return value of the RegisterAttached method. The field name must match the attached property name, appended with the string Property, to follow the established WPF pattern of naming the identifying fields versus the properties that they represent. The attached property provider must also provide static GetPropertyName and SetPropertyName methods as accessors for the attached property; failing to do this will result in the property system being unable to use your attached property. 

System_CAPS_noteNote

If you omit the attached property's get accessor, data binding on the property will not work in design tools, such as Visual Studio and Expression Blend.

The Get Accessor

The signature for the GetPropertyName accessor must be:

public static object GetPropertyName(object target)

  • The target object can be specified as a more specific type in your implementation. For example, the DockPanel.GetDock method types the parameter as UIElement, because the attached property is only intended to be set on UIElement instances.

  • The return value can be specified as a more specific type in your implementation. For example, the GetDock method types it as Dock, because the value can only be set to that enumeration.

The Set Accessor

The signature for the SetPropertyName accessor must be:

public static void SetPropertyName(object target, object value)

  • The target object can be specified as a more specific type in your implementation. For example, the SetDock method types it as UIElement, because the attached property is only intended to be set on UIElement instances.

  • The value object can be specified as a more specific type in your implementation. For example, the SetDock method types it as Dock, because the value can only be set to that enumeration. Remember that the value for this method is the input coming from the XAML loader when it encounters your attached property in an attached property usage in markup. That input is the value specified as a XAML attribute value in markup. Therefore there must be type conversion, value serializer, or markup extension support for the type you use, such that the appropriate type can be created from the attribute value (which is ultimately just a string).

The following example shows the dependency property registration (using the RegisterAttached method), as well as the GetPropertyName and SetPropertyName accessors. In the example, the attached property name is IsBubbleSource. Therefore, the accessors must be named GetIsBubbleSource and SetIsBubbleSource.

public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
  "IsBubbleSource",
  typeof(Boolean),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
  element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
  return (Boolean)element.GetValue(IsBubbleSourceProperty);
}

Attached Property Attributes

WPF defines several .NET Framework attributes that are intended to provide information about attached properties to reflection processes, and to typical users of reflection and property information such as designers. Because attached properties have a type of unlimited scope, designers need a way to avoid overwhelming users with a global list of all the attached properties that are defined in a particular technology implementation that uses XAML. The .NET Framework attributes that WPF defines for attached properties can be used to scope the situations where a given attached property should be shown in a properties window. You might consider applying these attributes for your own custom attached properties also. The purpose and syntax of the .NET Framework attributes is described on the appropriate reference pages:

Learning More About Attached Properties

  • For more information on creating an attached property, see How to: Register an Attached Property.

  • For more advanced usage scenarios for dependency properties and attached properties, see Custom Dependency Properties.

  • You can also register a property as an attached property, and as a dependency property, but then still expose "wrapper" implementations. In this case, the property can be set either on that element, or on any element through the XAML attached property syntax. An example of a property with an appropriate scenario for both standard and attached usages is FrameworkElement.FlowDirection.





Dependency Property Callbacks and Validation


This topic describes how to create dependency properties using alternative custom implementations for property-related features such as validation determination, callbacks that are invoked whenever the property's effective value is changed, and overriding possible outside influences on value determination. This topic also discusses scenarios where expanding on the default property system behaviors by using these techniques is appropriate.

Prerequisites

This topic assumes that you understand the basic scenarios of implementing a dependency property, and how metadata is applied to a custom dependency property. See Custom Dependency Properties and Dependency Property Metadata for context.

Validation Callbacks

Validation callbacks can be assigned to a dependency property when you first register it. The validation callback is not part of property metadata; it is a direct input of the Register method. Therefore, once a validation callback is created for a dependency property, it cannot be overridden by a new implementation.

public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
    "CurrentReading",
    typeof(double),
    typeof(Gauge),
    new FrameworkPropertyMetadata(
        Double.NaN,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        new PropertyChangedCallback(OnCurrentReadingChanged),
        new CoerceValueCallback(CoerceCurrentReading)
    ),
    new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
  get { return (double)GetValue(CurrentReadingProperty); }
  set { SetValue(CurrentReadingProperty, value); }
}

The callbacks are implemented such that they are provided an object value. They return true if the provided value is valid for the property; otherwise, they return false. It is assumed that the property is of the correct type per the type registered with the property system, so checking type within the callbacks is not ordinarily done. The callbacks are used by the property system in a variety of different operations. This includes the initial type initialization by default value, programmatic change by invoking SetValue, or attempts to override metadata with new default value provided. If the validation callback is invoked by any of these operations, and returns false, then an exception will be raised. Application writers must be prepared to handle these exceptions. A common use of validation callbacks is validating enumeration values, or constraining values of integers or doubles when the property sets measurements that must be zero or greater.

Validation callbacks specifically are intended to be class validators, not instance validators. The parameters of the callback do not communicate a specific DependencyObject on which the properties to validate are set. Therefore the validation callbacks are not useful for enforcing the possible "dependencies" that might influence a property value, where the instance-specific value of a property is dependent on factors such as instance-specific values of other properties, or run-time state.

The following is example code for a very simple validation callback scenario: validating that a property that is typed as the Double primitive is not PositiveInfinity or NegativeInfinity.

public static bool IsValidReading(object value)
{
    Double v = (Double)value;
    return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
}

Coerce Value Callbacks and Property Changed Events

Coerce value callbacks do pass the specific DependencyObject instance for properties, as do PropertyChangedCallback implementations that are invoked by the property system whenever the value of a dependency property changes. Using these two callbacks in combination, you can create a series of properties on elements where changes in one property will force a coercion or reevaluation of another property.

A typical scenario for using a linkage of dependency properties is when you have a user interface driven property where the element holds one property each for the minimum and maximum value, and a third property for the actual or current value. Here, if the maximum was adjusted in such a way that the current value exceeded the new maximum, you would want to coerce the current value to be no greater than the new maximum, and a similar relationship for minimum to current.

The following is very brief example code for just one of the three dependency properties that illustrate this relationship. The example shows how the CurrentReading property of a Min/Max/Current set of related *Reading properties is registered. It uses the validation as shown in the previous section.

public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
    "CurrentReading",
    typeof(double),
    typeof(Gauge),
    new FrameworkPropertyMetadata(
        Double.NaN,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        new PropertyChangedCallback(OnCurrentReadingChanged),
        new CoerceValueCallback(CoerceCurrentReading)
    ),
    new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
  get { return (double)GetValue(CurrentReadingProperty); }
  set { SetValue(CurrentReadingProperty, value); }
}

The property changed callback for Current is used to forward the change to other dependent properties, by explicitly invoking the coerce value callbacks that are registered for those other properties:

private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  d.CoerceValue(MinReadingProperty);
  d.CoerceValue(MaxReadingProperty);
}

The coerce value callback checks the values of properties that the current property is potentially dependent upon, and coerces the current value if necessary:

private static object CoerceCurrentReading(DependencyObject d, object value)
{
  Gauge g = (Gauge)d;
  double current = (double)value;
  if (current < g.MinReading) current = g.MinReading;
  if (current > g.MaxReading) current = g.MaxReading;
  return current;
}
System_CAPS_noteNote

Default values of properties are not coerced. A property value equal to the default value might occur if a property value still has its initial default, or through clearing other values with ClearValue.

The coerce value and property changed callbacks are part of property metadata. Therefore, you can change the callbacks for a particular dependency property as it exists on a type that you derive from the type that owns the dependency property, by overriding the metadata for that property on your type.

Advanced Coercion and Callback Scenarios

Constraints and Desired Values

The CoerceValueCallback callbacks will be used by the property system to coerce a value in accordance to the logic you declare, but a coerced value of a locally set property will still retain a "desired value" internally. If the constraints are based on other property values that may change dynamically during the application lifetime, the coercion constraints are changed dynamically also, and the constrained property can change its value to get as close to the desired value as possible given the new constraints. The value will become the desired value if all constraints are lifted. You can potentially introduce some fairly complicated dependency scenarios if you have multiple properties that are dependent on one another in a circular manner. For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. But if that coercion is active, and Maximum coerces to Minimum, it leaves Current in an unsettable state, because it is dependent on both and is constrained to the range between the values, which is zero. Then, if Maximum or Minimum are adjusted, Current will seem to "follow" one of the values, because the desired value of Current is still stored and is attempting to reach the desired value as the constraints are loosened.

There is nothing technically wrong with complex dependencies, but they can be a slight performance detriment if they require large numbers of reevaluations, and can also be confusing to users if they affect the UI directly. Be careful with property changed and coerce value callbacks and make sure that the coercion being attempted can be treated as unambiguously as possible, and does not "overconstrain".

Using CoerceValue to Cancel Value Changes

The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. This special case means that the property change that resulted in the CoerceValueCallback being called should be rejected by the property system, and that the property system should instead report whatever previous value the property had. This mechanism can be useful to check that changes to a property that were initiated asynchronously are still valid for the current object state, and suppress the changes if not. Another possible scenario is that you can selectively suppress a value depending on which component of property value determination is responsible for the value being reported. To do this, you can use the DependencyProperty passed in the callback and the property identifier as input for GetValueSource, and then process the ValueSource.














































'프로그래밍 > WPF' 카테고리의 다른 글

Commanding Overview  (0) 2017.02.08
Graphics and Multimedia - Multimedia, Visual Layer  (0) 2017.01.14
Data Binding  (0) 2017.01.10
Graphics and Multimedia - Animation Overview  (0) 2017.01.07
Graphics and Multimedia  (0) 2016.11.24
:
Posted by 지훈2


Multimedia Overview


https://msdn.microsoft.com/en-us/library/aa970915(v=vs.110).aspx



The multimedia features in Windows Presentation Foundation (WPF) enable you to integrate audio and video into your applications to enhance the user experience. This topic introduces the multimedia features of WPF.

Media API

The MediaElement and MediaPlayer classes are used to present audio or video content. These classes can be controlled interactively or by a clock. These classes can use on the Microsoft Windows Media Player 10 control for media playback. Which class you use, depends on the scenario.

MediaElement is a UIElement that is supported by the Layout and can be consumed as the content of many controls. It is also usable in Extensible Application Markup Language (XAML) as well as code. MediaPlayer, on the other hand, is designed for Drawing objects and lacks layout support. Media loaded using a MediaPlayer can only be presented using a VideoDrawing or by directly interacting with a DrawingContextMediaPlayer cannot be used in XAML.

For more information about drawing objects and drawing context, see Drawing Objects Overview.

System_CAPS_noteNote

When distributing media with your application, you cannot use a media file as a project resource. In your project file, you must instead set the media type to Content and set CopyToOutputDirectory to PreserveNewest or Always.

Media Playback Modes

System_CAPS_noteNote

Both MediaElement and MediaPlayer have similar members. The links in this section refer to the MediaElement class members. Unless specifically noted, members linked to in the MediaElement class can also be found in the MediaPlayer class.

To understand media playback in Windows Presentation Foundation (WPF), an understanding of the different modes in which media can be played is required. Both MediaElement and MediaPlayer can be used in two different media modes, independent mode and clock mode. The media mode is determined by the Clock property. When Clock is null, the media object is in independent mode. When the Clock is non-null, the media object is in clock mode. By default, media objects are in independent mode.

Independent Mode

In independent mode, the media content drives media playback. Independent mode enables the following options:

  • Media's Uri can be directly specified.

  • Media playback can be directly controlled.

  • Media's Position and SpeedRatio properties can be modified.

Media is loaded by either setting the MediaElement object's Source property or by calling the MediaPlayer object's Open method.

To control media playback in independent mode, the media object's control methods can be used. The control methods available are PlayPauseClose, and Stop. For MediaElement, interactive control using these methods is only available when the LoadedBehavior is set to Manual. These methods are unavailable when the media object is in clock mode.

See How to: Control a MediaElement (Play, Pause, Stop, Volume, and Speed) for an example of independent mode.

Clock Mode

In clock mode, a MediaTimeline drives media playback. Clock mode has the following characteristics:

  • Media's Uri is indirectly set through a MediaTimeline.

  • Media playback can be controlled by the clock. The media object's control methods cannot be used.

  • Media is loaded by setting a MediaTimeline object's Source property, creating the clock from the timeline, and assigning the clock to the media object. Media is also loaded this way when a MediaTimeline inside a Storyboard targets a MediaElement.

To control media playback in clock mode, the ClockController control methods must be used. A ClockController is obtained from the ClockController property of the MediaClock. If you attempt to use the control methods of either a MediaElement or MediaPlayer object while in clock mode, an InvalidOperationException will be thrown.

See the Animation Overview for more information about clocks and timelines.

See How to: Control a MediaElement by Using a Storyboard for an example of clock mode.

MediaElement Class

Adding media to an application is as simple as adding a MediaElement control to the user interface (UI) of the application and providing a Uri to the media you wish to include. All media types supported by Microsoft Windows Media Player 10 are supported in Windows Presentation Foundation (WPF). The following example shows a simple usage of the MediaElement in Extensible Application Markup Language (XAML).

<!-- This page shows a simple usage of MediaElement -->
<Page x:Class="MediaElementExample.SimpleUsage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="SimpleUsage"
    >
  <StackPanel Margin="20">
    <MediaElement Source="media/numbers-aud.wmv" />
  </StackPanel>
</Page>

In this sample, media is played automatically as soon as it is loaded. Once the media has finished playing, the media is closed and all media resources are release (including video memory). This is the default behavior of the MediaElement object and is controlled by the LoadedBehavior and UnloadedBehavior properties.

Controlling a MediaElement

The LoadedBehavior and UnloadedBehavior properties control the behavior of the MediaElement when IsLoaded is true or false, respectively. The MediaState the properties are set to affect the media playback behavior. For example, the default LoadedBehavior is Play and the default UnloadedBehavior is Close. This means that as soon as the MediaElement is loaded and the preroll is complete, the media begins to play. Once playback is complete, media is closed and all media resources are released.

The LoadedBehavior and UnloadedBehavior properties are not the only way to control media playback. In clock mode, the clock can control the MediaElement and the interactive control methods have control when the LoadedBehavior is ManualMediaElement handles this competition for control by evaluating the following priorities.

  1. UnloadedBehavior. In place when media is unloaded. This ensures that all media resources are released by default, even when a MediaClock is associated with the MediaElement.

  2. MediaClock. In place when media has a Clock. If media is unloaded, the MediaClock will take effect as long as the UnloadedBehavior is Manual. Clock mode always overrides the loaded behavior of the MediaElement.

  3. LoadedBehavior. In place when media is loaded.

  4. Interactive control methods. In place when LoadedBehavior is Manual. The control methods available are PlayPauseClose, and Stop.

Displaying a MediaElement

To display a MediaElement it must have content to render and it will have its ActualWidth and ActualHeight properties set to zero until content is loaded. For audio only content, these properties are always zero. For video content, once the MediaOpened event has been raised the ActualWidth and ActualHeight will report the size of the loaded media. This means that until media is loaded, the MediaElement will not take up any physical space in the user interface (UI) unless the Width or Height properties are set.

Setting both the Width and Height properties will cause the media to stretch to fill the area provided for the MediaElement. To preserve the media's original aspect ratio, either the Width or Height property should be set but not both. Setting both the Width and Height properties will cause the media to present in a fixed element size that may not be desirable.

To avoid having a fixed size element which, Windows Presentation Foundation (WPF) can preroll the media. This is done by setting the LoadedBehavior to either Play or Pause. In a Pause state, the media will preroll and will present the first frame. In a Play state, the media will preroll and begin to play.

MediaPlayer Class

Where as the MediaElement class is a framework element, the MediaPlayer class is designed to be used in Drawing objects. Drawing objects are used when you can sacrifice framework level features to gain performance benefits or when you need Freezable features. MediaPlayer enables you to take advantage of these features while providing media content in your applications. Like MediaElementMediaPlayer can be used in independent or clock mode but does not have the MediaElement object's unloaded and loaded states. This reduces the playback control complexity of the MediaPlayer.

Controlling MediaPlayer

Because MediaPlayer is stateless, there are only two ways to control media playback.

  1. Interactive control methods. In place when in independent mode (null Clock property).

  2. MediaClock. In place when media has a Clock.

Displaying a MediaPlayer

Technically, a MediaPlayer cannot be displayed since it has no physical representation. However, it can be used to present media in a Drawing using the VideoDrawing class. The following example demonstrates the use of a VideoDrawing to display media.

//
// Create a VideoDrawing.
//      
MediaPlayer player = new MediaPlayer();

player.Open(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative));

VideoDrawing aVideoDrawing = new VideoDrawing();

aVideoDrawing.Rect = new Rect(0, 0, 100, 100);

aVideoDrawing.Player = player;

// Play the video once.
player.Play();        

See the Drawing Objects Overview for more information about Drawing objects.





Audio and Video How-to Topics



How to: Control a MediaElement (Play, Pause, Stop, Volume, and Speed)


The following example shows how to control playback of media using a MediaElement. The example creates a simple media player that allows you to play, pause, stop, and skip back and forth in the media as well as adjust the volume and speed ratio.

Example

The code below creates the UI.

System_CAPS_noteNote

The LoadedBehavior property of MediaElement must be set to Manual in order to be able to interactively stop, pause, and play the media.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.MediaElementExample" >

  <StackPanel Background="Black">

    <!-- To interactively stop, pause, and play the media, the LoadedBehavior 
           property of the MediaElement must be set to "Manual". -->
    <MediaElement Source="media\numbers.wmv" Name="myMediaElement" 
     Width="450" Height="250" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill" 
     MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded"/>

    <StackPanel HorizontalAlignment="Center" Width="450" Orientation="Horizontal">

      <!-- Play button. -->
      <Image Source="images\UI_play.gif" MouseDown="OnMouseDownPlayMedia" Margin="5" />

      <!-- Pause button. -->
      <Image Source="images\UI_pause.gif" MouseDown="OnMouseDownPauseMedia" Margin="5" />

      <!-- Stop button. -->
      <Image Source="images\UI_stop.gif" MouseDown="OnMouseDownStopMedia" Margin="5" />

      <!-- Volume slider. This slider allows a Volume range between 0 and 1. -->
      <TextBlock Foreground="White" VerticalAlignment="Center" Margin="5"  >Volume</TextBlock>
      <Slider Name="volumeSlider" VerticalAlignment="Center" ValueChanged="ChangeMediaVolume" 
       Minimum="0" Maximum="1" Value="0.5" Width="70"/>

      <!-- Volume slider. This slider allows you to change the speed of the media playback. -->
      <TextBlock Foreground="White" Margin="5"  VerticalAlignment="Center">Speed</TextBlock>
      <Slider Name="speedRatioSlider" VerticalAlignment="Center" ValueChanged="ChangeMediaSpeedRatio" 
       Value="1" Width="70" />

      <!-- Seek to slider. Ths slider allows you to jump to different parts of the media playback. -->
      <TextBlock Foreground="White" Margin="5"  VerticalAlignment="Center">Seek To</TextBlock>
      <Slider Name="timelineSlider" Margin="5" ValueChanged="SeekToMediaPosition" Width="70"/>

    </StackPanel>
  </StackPanel>
</Page>

Example

The code below implements the functionality of the sample UI controls. The PlayPause, and Stop methods are used to respectively play, pause and stop the media. Changing the Position property of the MediaElement allows you to skip around in the media. Finally, the Volume and SpeedRatio properties are used to adjust the volume and playback speed of the media.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Input;

namespace SDKSample
{

   public partial class MediaElementExample : Page
   {

      // Play the media.
      void OnMouseDownPlayMedia(object sender, MouseButtonEventArgs args)
      {

         // The Play method will begin the media if it is not currently active or 
         // resume media if it is paused. This has no effect if the media is
         // already running.
         myMediaElement.Play();

         // Initialize the MediaElement property values.
         InitializePropertyValues();

      }

      // Pause the media.
      void OnMouseDownPauseMedia(object sender, MouseButtonEventArgs args)
      {

         // The Pause method pauses the media if it is currently running.
         // The Play method can be used to resume.
         myMediaElement.Pause();

      }

      // Stop the media.
      void OnMouseDownStopMedia(object sender, MouseButtonEventArgs args)
      {

         // The Stop method stops and resets the media to be played from
         // the beginning.
         myMediaElement.Stop();

      }

      // Change the volume of the media.
      private void ChangeMediaVolume(object sender, RoutedPropertyChangedEventArgs<double> args)
      {
         myMediaElement.Volume = (double)volumeSlider.Value;
      }

      // Change the speed of the media.
      private void ChangeMediaSpeedRatio(object sender, RoutedPropertyChangedEventArgs<double> args)
      {
         myMediaElement.SpeedRatio = (double)speedRatioSlider.Value;
      }

      // When the media opens, initialize the "Seek To" slider maximum value
      // to the total number of miliseconds in the length of the media clip.
      private void Element_MediaOpened(object sender, EventArgs e)
      {
         timelineSlider.Maximum = myMediaElement.NaturalDuration.TimeSpan.TotalMilliseconds;
      }

      // When the media playback is finished. Stop() the media to seek to media start.
      private void Element_MediaEnded(object sender, EventArgs e)
      {
         myMediaElement.Stop();
      }

      // Jump to different parts of the media (seek to). 
      private void SeekToMediaPosition(object sender, RoutedPropertyChangedEventArgs<double> args)
      {
         int SliderValue = (int)timelineSlider.Value;

         // Overloaded constructor takes the arguments days, hours, minutes, seconds, miniseconds.
         // Create a TimeSpan with miliseconds equal to the slider value.
         TimeSpan ts = new TimeSpan(0, 0, 0, 0, SliderValue);
         myMediaElement.Position = ts;
      }

      void InitializePropertyValues()
      {
         // Set the media's starting Volume and SpeedRatio to the current value of the
         // their respective slider controls.
         myMediaElement.Volume = (double)volumeSlider.Value;
         myMediaElement.SpeedRatio = (double)speedRatioSlider.Value;
      }

   }
}






How to: Control a MediaElement by Using a Storyboard


This example shows how to control a MediaElement by using a MediaTimeline in a Storyboard.

Example

When you use a MediaTimeline in a Storyboard to control the timing of a MediaElement, the functionality is identical to the functionality of other Timeline objects, such as animations. For example, a MediaTimeline uses Timeline properties like the BeginTime property to specify when to start a MediaElement (start media playback). It also uses the Duration property to specify how long the MediaElement is active (duration of media playback). For more information about using Timeline objects with a Storyboard, see Storyboards Overview.

This example shows how to create a simple media player that uses a MediaTimeline to control playback. The media player includes play, pause, resume, and stop buttons. The player also has a Slider control that acts as a progress bar.

The following example creates the user interface (UI) for the media player.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  x:Class="SDKSample.MediaTimelineExample" >

  <StackPanel Background="Black">

    <MediaElement Name="myMediaElement" MediaOpened="Element_MediaOpened"
     Width="260" Height="150" Stretch="Fill" />

    <!-- Button controls for play, pause, resume, and stop. -->
  <StackPanel HorizontalAlignment="Center" Width="260" Orientation="Horizontal">
    <Image Name="PlayButton" Source="images\UI_play.gif" Margin="30,10,10,10" />
    <Image Name="PauseButton" Source="images\UI_pause.gif" Margin="10" />
    <Image Name="ResumeButton" Source="images\UI_resume.gif" Margin="10" />
    <Image Name="StopButton" Source="images\UI_stop.gif" Margin="10" />
  </StackPanel>

  <!-- Ths slider shows the progress of the media. -->
  <Slider Name="timelineSlider" Margin="5" Width="250" HorizontalAlignment="Center"/>

  <StackPanel.Triggers>
    <EventTrigger RoutedEvent="Image.MouseDown" SourceName="PlayButton">
      <EventTrigger.Actions>
        <BeginStoryboard Name= "myBegin">

          <Storyboard SlipBehavior="Slip">

            <!-- The MediaTimeline controls the timing of the video and acts like other Timeline objects.  
                 For example, although the video clip (numbers.wmv) lasts longer, playback ends after six  
                 seconds because that is the duration of the MediaTimeline (Duration="0:0:6"). -->
            <MediaTimeline Source="media\numbers.wmv" Storyboard.TargetName="myMediaElement"  
             BeginTime="0:0:0" Duration="0:0:6" CurrentTimeInvalidated="MediaTimeChanged" />

          </Storyboard>
        </BeginStoryboard>
      </EventTrigger.Actions>
    </EventTrigger>

    <!-- These triggers impliment the functionality of the Pause, Resume
         and Stop buttons.-->
    <EventTrigger RoutedEvent="Image.MouseDown" SourceName="PauseButton">
      <EventTrigger.Actions>
        <PauseStoryboard BeginStoryboardName="myBegin" />
      </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Image.MouseDown" SourceName="ResumeButton">
      <EventTrigger.Actions>
        <ResumeStoryboard BeginStoryboardName="myBegin" />
      </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Image.MouseDown" SourceName="StopButton">
      <EventTrigger.Actions>
        <StopStoryboard BeginStoryboardName="myBegin" />
      </EventTrigger.Actions>
    </EventTrigger>
  </StackPanel.Triggers>

</StackPanel>
</Page>

The following example creates the functionality for the progress bar.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace SDKSample
{

    public partial class MediaTimelineExample : Page
	{
        // When the media opens, initialize the "Seek To" slider maximum value
        // to the total number of miliseconds in the length of the media clip.
        private void Element_MediaOpened(object sender, EventArgs e)
        {
            timelineSlider.Maximum = myMediaElement.NaturalDuration.TimeSpan.TotalMilliseconds;
        }

        private void MediaTimeChanged(object sender, EventArgs e)
        {
            timelineSlider.Value = myMediaElement.Position.TotalMilliseconds;
        }

    }
}






How to: Trigger Media Playback with a User Event


This example shows how to synchronize media playback with an event.

Example

The following example uses the MediaElement control and the MediaTimeline class to play a sound that occurs when the user clicks a Button.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <StackPanel>

    <!-- The MediaElement control plays the sound. -->
    <MediaElement Name="myMediaElement" />

    <Button>Click to Hear a Sound!
      <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>

                <!-- Sound wave from this source is played when the button is clicked.-->
                <MediaTimeline Source="C:\WINDOWS\Media\ringin.wav" Storyboard.TargetName="myMediaElement"  />

              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
      </Button.Triggers>
    </Button>

  </StackPanel>
</Page>






How to: Repeat Media Playback


This example shows how to playback media indefinitely, that is, to set media so that it plays in an infinite loop.

Example

The following example uses MediaElement and MediaTimeline in a Storyboard to play a media clip in an infinite loop.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <StackPanel>

    <!-- The MediaElement control plays the sound. -->
    <MediaElement Name="myMediaElement" >
      <MediaElement.Triggers>
        <EventTrigger RoutedEvent="MediaElement.Loaded">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>

                <!-- The MediaTimeline has a RepeatBehavior="Forever" which makes the media play
                     over and over indefinitely.-->
                <MediaTimeline Source="media\tada.wav" Storyboard.TargetName="myMediaElement"  
                 RepeatBehavior="Forever" />

              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
      </MediaElement.Triggers>
    </MediaElement>

  </StackPanel>
</Page>






How to: Play Media with Animations


This example shows how to play media and animations at the same time by using the MediaTimeline and DoubleAnimationUsingKeyFrames classes in the same Storyboard.

Example

You can use one or more MediaTimeline objects in a Storyboard together with other Timeline objects, such as animations.

The following example sets the SlipBehavior property of the Storyboard to a value of Slip, which specifies that the animation does not progress until the media (video in this example) progresses. This functionality might be needed if media playback is delayed because of loading time.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Canvas>

    <!-- Upper right hand Canvas contains the animations. -->
    <Border BorderBrush="Black" BorderThickness="1"  Canvas.Left="250">
      <Canvas Width="250" Height="250" Background="White" >

        <!-- The two Path elements below create the purple and gold rings
             which are animated while the media is played. -->
        <Path Stroke="Purple" StrokeThickness="5">
          <Path.Data>
            <EllipseGeometry x:Name="MyEllipseGeometry" 
            Center="125,125" RadiusX="15" RadiusY="10" />
          </Path.Data>
        </Path>
        <Path Stroke="Gold" StrokeThickness="5">
          <Path.Data>
            <EllipseGeometry x:Name="MyEllipseGeometry2" 
            Center="125,125" RadiusX="10" RadiusY="15" />
          </Path.Data>
        </Path>
      </Canvas>
    </Border>

    <!-- Upper left hand Canvas contains the video. -->
    <Canvas Width="250" Height="250" Background="Green">
      <MediaElement Name="myvideo" Width="250" Height="250" 
       Canvas.Left="0" Canvas.Top="0">
        <MediaElement.Triggers>
          <EventTrigger RoutedEvent="MediaElement.Loaded">
            <EventTrigger.Actions>
              <BeginStoryboard>

                <!-- This Storyboard contains both media (video in this example) and animations. Note 
               the SlipBehavior value of "Slip" specifies that the animation does not progress
               until the media progresses. This might be desirable if media playback is delayed
               because of loading time. -->
                <Storyboard SlipBehavior="Slip">

                  <!-- The MediaTimeline controls the timing of the video and acts like other Timeline objects.  
                 For example, although the video clip (numbers.wmv) lasts longer, playback ends after six  
                 seconds because that is the duration of the MediaTimeline (Duration="0:0:6"). -->
                  <MediaTimeline Source="media\numbers.wmv" BeginTime="0:0:0" Duration="0:0:10"/>

                  <!-- The animations below animate the ellipses in the right hand pane. These animations are 
                 timed to correspond to the counting in the video. -->

                  <!-- Animate the RadiusY property of the purple ellipse. -->
                  <DoubleAnimationUsingKeyFrames
                    Storyboard.TargetName="MyEllipseGeometry"
                    Storyboard.TargetProperty="RadiusY"
                    RepeatBehavior="10x">
                    <DoubleAnimationUsingKeyFrames.KeyFrames>
                      <LinearDoubleKeyFrame Value="80" KeyTime="0:0:0.4" />
                      <SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:1" />
                    </DoubleAnimationUsingKeyFrames.KeyFrames>
                  </DoubleAnimationUsingKeyFrames>

                  <!-- Animate the RadiusX property of the gold ellipse. -->
                  <DoubleAnimationUsingKeyFrames
                    Storyboard.TargetName="MyEllipseGeometry2"
                    Storyboard.TargetProperty="RadiusX"
                    RepeatBehavior="10x">
                    <DoubleAnimationUsingKeyFrames.KeyFrames>
                      <LinearDoubleKeyFrame Value="80" KeyTime="0:0:0.4" />
                      <SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:1" />
                    </DoubleAnimationUsingKeyFrames.KeyFrames>
                  </DoubleAnimationUsingKeyFrames>

                </Storyboard>
              </BeginStoryboard>
            </EventTrigger.Actions>
          </EventTrigger>
        </MediaElement.Triggers>
      </MediaElement>
    </Canvas>

  </Canvas>
</Page>






How to: Use Transforms on a MediaElement


This example shows how to use a RotateTransform on a MediaElement.

Example

In the following markup, the MediaElement is rotated using a RotateTransform.

<MediaElement Source="media/numbers-aud.wmv">
  <MediaElement.LayoutTransform>
    <TransformGroup>
      <RotateTransform Angle="305" />
    </TransformGroup>
  </MediaElement.LayoutTransform>
</MediaElement>






Visual Layer Programming



Hit Testing in the Visual Layer


https://msdn.microsoft.com/en-us/library/ms752097(v=vs.110).aspx


This topic provides an overview of hit testing functionality provided by the visual layer. Hit testing support allows you to determine whether a geometry or point value falls within the rendered content of a Visual, allowing you to implement user interface behavior such as a selection rectangle to select multiple objects.

Hit Testing Scenarios

The UIElement class provides the InputHitTest method, which allows you to hit test against an element using a given coordinate value. In many cases, the InputHitTest method provides the desired functionality for implementing hit testing of elements. However, there are several scenarios in which you may need to implement hit testing at the visual layer.

  • Hit testing against non-UIElement objects: This applies if you are hit testing non-UIElement objects, such as DrawingVisual or graphics objects.

  • Hit testing using a geometry: This applies if you need to hit test using a geometry object rather than the coordinate value of a point.

  • Hit testing against multiple objects: This applies when you need to hit test against multiple objects, such as overlapping objects. You can get results for all visuals intersecting a geometry or point, not just the first one.

  • Ignoring UIElement hit testing policy: This applies when you need to ignore the UIElement hit testing policy, which takes into consideration such factors as whether an element is disabled or invisible.

System_CAPS_noteNote

For a complete code sample illustrating hit testing at the visual layer, see Hit Test Using DrawingVisuals Sample and Hit Test with Win32 Interoperation Sample.

Hit Testing Support

The purpose of the HitTest methods in the VisualTreeHelper class is to determine whether a geometry or point coordinate value is within the rendered content of a given object, such as a control or graphic element. For example, you could use hit testing to determine whether a mouse click within the bounding rectangle of an object falls within the geometry of a circle. You can also choose to override the default implementation of hit testing to perform your own custom hit test calculations.

The following illustration shows the relationship between a non-rectangular object's region and its bounding rectangle.

Diagram of valid hit test region

Diagram of valid hit test region

Hit Testing and Z-Order

The Windows Presentation Foundation (WPF) visual layer supports hit testing against all objects under a point or geometry, not just the top-most object. Results are returned in z-order. However, the visual object that you pass as the parameter to the HitTest method determines which portion of the visual tree that will be hit test. You can hit test against the entire visual tree, or any portion of it.

In the following illustration, the circle object is on top of both the square and triangle objects. If you are only interested in hit testing the visual object whose z-order value is top-most, you can set the visual hit test enumeration to return Stop from the HitTestResultCallback to stop the hit test traversal after the first item.

Diagram of the z-order of a visual tree

Diagram of the z-order of a visual tree

If you want to enumerate all visual objects under a specific point or geometry, return Continue from the HitTestResultCallback. This means you can hit test for visual objects that are beneath other objects, even if they are wholly obscured. See the sample code in the section "Using a Hit Test Results Callback" for more information.

System_CAPS_noteNote

A visual object that is transparent can also be hit test.

Using Default Hit Testing

You can identify whether a point is within the geometry of a visual object, by using the HitTest method to specify a visual object and a point coordinate value to test against. The visual object parameter identifies the starting point in the visual tree for the hit test search. If a visual object is found in the visual tree whose geometry contains the coordinate, it is set to the VisualHit property of a HitTestResult object. The HitTestResult is then returned from the HitTest method. If the point is not contained with the visual sub-tree you are hit testing, HitTest returns null.

System_CAPS_noteNote

Default hit testing always returns the top-most object in the z-order. In order to identify all visual objects, even those that may be partly or wholly obscured, use a hit test result callback.

The coordinate value you pass as the point parameter for the HitTest method has to be relative to the coordinate space of the visual object you are hit testing against. For example, if you have nested visual objects defined at (100, 100) in the parent's coordinate space, then hit testing a child visual at (0, 0) is equivalent to hit testing at (100, 100) in the parent's coordinate space.

The following code shows how to set up mouse event handlers for a UIElement object that is used to capture events used for hit testing.

// Respond to the left mouse button down event by initiating the hit test.
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Perform the hit test against a given portion of the visual object tree.
    HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);

    if (result != null)
    {
        // Perform action on hit visual object.
    }
}

How the Visual Tree Affects Hit Testing

The starting point in the visual tree determines which objects are returned during the hit test enumeration of objects. If you have multiple objects you want to hit test, the visual object used as the starting point in the visual tree must be the common ancestor of all objects of interest. For example, if you were interested in hit testing both the button element and drawing visual in the following diagram, you would have to set the starting point in the visual tree to the common ancestor of both. In this case, the canvas element is the common ancestor of both the button element and the drawing visual.

Diagram of a visual tree hierarchy

Diagram of a visual tree hierarchy

System_CAPS_noteNote

The IsHitTestVisible property gets or sets a value that declares whether a UIElement-derived object can possibly be returned as a hit test result from some portion of its rendered content. This allows you to selectively alter the visual tree to determine which visual objects are involved in a hit test.

Using a Hit Test Result Callback

You can enumerate all visual objects in a visual tree whose geometry contains a specified coordinate value. This allows you to identify all visual objects, even those that may be partly or wholly obscured by other visual objects. To enumerate visual objects in a visual tree use the HitTest method with a hit test callback function. The hit test callback function is called by the system when the coordinate value you specify is contained in a visual object.

During the hit test results enumeration, you should not perform any operation that modifies the visual tree. Adding or removing an object from the visual tree while it is being traversed can result in unpredictable behavior. You can safely modify the visual tree after the HitTest method returns. You may want to provide a data structure, such as an ArrayList, to store values during the hit test results enumeration.

// Respond to the right mouse button down event by setting up a hit test results callback.
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas, null,
        new HitTestResultCallback(MyHitTestResult),
        new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        Console.WriteLine("Number of Visuals Hit: " + hitResultsList.Count);
    }
}

The hit test callback method defines the actions you perform when a hit test is identified on a particular visual object in the visual tree. After you perform the actions, you return a HitTestResultBehavior value that determines whether to continue the enumeration of any other visual objects or not.

// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
    // Add the hit test result to the list that will be processed after the enumeration.
    hitResultsList.Add(result.VisualHit);

    // Set the behavior to return visuals at all z-order levels.
    return HitTestResultBehavior.Continue;
}
System_CAPS_noteNote

The order of enumeration of hit visual objects is by z-order. The visual object at the top-most z-order level is the first object enumerated. Any other visual objects enumerated are at decreasing z-order level. This order of enumeration corresponds to the rendering order of the visuals.

You can stop the enumeration of visual objects at any time in the hit test callback function by returning Stop.

// Set the behavior to stop enumerating visuals.
return HitTestResultBehavior.Stop;

Using a Hit Test Filter Callback

You can use an optional hit test filter to restrict the objects that are passed on to the hit test results. This allows you to ignore parts of the visual tree that you are not interested in processing in your hit test results. To implement a hit test filter, you define a hit test filter callback function and pass it as a parameter value when you call the HitTest method.

// Respond to the mouse wheel event by setting up a hit test filter and results enumeration.
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas,
                      new HitTestFilterCallback(MyHitTestFilter),
                      new HitTestResultCallback(MyHitTestResult),
                      new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}

If you do not want to supply the optional hit test filter callback function, pass a null value as its parameter for the HitTest method.

// Set up a callback to receive the hit test result enumeration,
// but no hit test filter enumeration.
VisualTreeHelper.HitTest(myCanvas,
                  null,  // No hit test filtering.
                  new HitTestResultCallback(MyHitTestResult),
                  new PointHitTestParameters(pt));
Pruning a visual tree using a hit test filter

Pruning a visual tree

The hit test filter callback function allows you to enumerate through all the visuals whose rendered content contains the coordinates you specify. However, you may want to ignore certain branches of the visual tree that you are not interested in processing in your hit test results callback function. The return value of the hit test filter callback function determines what type of action the enumeration of the visual objects should take. For example, if you return the value, ContinueSkipSelfAndChildren, you can remove the current visual object and its children from the hit test results enumeration. This means that the hit test results callback function will not see these objects in its enumeration. Pruning the visual tree of objects decreases the amount of processing during the hit test results enumeration pass. In the following code example, the filter skips labels and their descendants and hit tests everything else.

// Filter the hit test values for each object in the enumeration.
public HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
{
    // Test for the object value you want to filter.
    if (o.GetType() == typeof(Label))
    {
        // Visual object and descendants are NOT part of hit test results enumeration.
        return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
    }
    else
    {
        // Visual object is part of hit test results enumeration.
        return HitTestFilterBehavior.Continue;
    }
}
System_CAPS_noteNote

The hit test filter callback will sometimes be called in cases where the hit test results callback is not called.

Overriding Default Hit Testing

You can override a visual object’s default hit testing support by overriding the HitTestCore method. This means that when you invoke the HitTest method, your overridden implementation of HitTestCore is called. Your overridden method is called when a hit test falls within the bounding rectangle of the visual object, even if the coordinate falls outside the rendered content of the visual object.

// Override default hit test support in visual object.
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
    Point pt = hitTestParameters.HitPoint;

    // Perform custom actions during the hit test processing,
    // which may include verifying that the point actually
    // falls within the rendered content of the visual.

    // Return hit on bounding rectangle of visual object.
    return new PointHitTestResult(this, pt);
}

There may be times when you want to hit test against both the bounding rectangle and the rendered content of a visual object. By using the PointHitTestParameters parameter value in your overridden HitTestCore method as the parameter to the base method HitTestCore, you can perform actions based on a hit of the bounding rectangle of a visual object, and then perform a second hit test against the rendered content of the visual object.

// Override default hit test support in visual object.
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
    // Perform actions based on hit test of bounding rectangle.
    // ...

    // Return results of base class hit testing,
    // which only returns hit on the geometry of visual objects.
    return base.HitTestCore(hitTestParameters);
}





Using DrawingVisual Objects


This topic provides an overview of how to use DrawingVisual objects in the WPF visual layer.

This topic contains the following sections.

DrawingVisual Object

The DrawingVisual is a lightweight drawing class that is used to render shapes, images, or text. This class is considered lightweight because it does not provide layout or event handling, which improves its performance. For this reason, drawings are ideal for backgrounds and clip art.

DrawingVisual Host Container

In order to use DrawingVisual objects, you need to create a host container for the objects. The host container object must derive from the FrameworkElement class, which provides the layout and event handling support that the DrawingVisual class lacks. The host container object does not display any visible properties, since its main purpose is to contain child objects. However, the Visibility property of the host container must be set to Visible; otherwise, none of its child elements will be visible.

When you create a host container object for visual objects, you need to store the visual object references in a VisualCollection. Use the Add method to add a visual object to the host container. In the following example, a host container object is created, and three visual objects are added to its VisualCollection.

// Create a host visual derived from the FrameworkElement class.
// This class provides layout, event handling, and container support for
// the child visual objects.
public class MyVisualHost : FrameworkElement
{
    // Create a collection of child visual objects.
    private VisualCollection _children;

    public MyVisualHost()
    {
        _children = new VisualCollection(this);
        _children.Add(CreateDrawingVisualRectangle());
        _children.Add(CreateDrawingVisualText());
        _children.Add(CreateDrawingVisualEllipses());

        // Add the event handler for MouseLeftButtonUp.
        this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
    }
System_CAPS_noteNote

For the complete code sample from which the preceding code example was extracted, see Hit Test Using DrawingVisuals Sample.

Creating DrawingVisual Objects

When you create a DrawingVisual object, it has no drawing content. You can add text, graphics, or image content by retrieving the object's DrawingContext and drawing into it. A DrawingContext is returned by calling the RenderOpen method of a DrawingVisual object.

To draw a rectangle into the DrawingContext, use the DrawRectangle method of the DrawingContext object. Similar methods exist for drawing other types of content. When you are finished drawing content into the DrawingContext, call the Close method to close the DrawingContext and persist the content.

In the following example, a DrawingVisual object is created, and a rectangle is drawn into its DrawingContext.

// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
    DrawingVisual drawingVisual = new DrawingVisual();

    // Retrieve the DrawingContext in order to create new drawing content.
    DrawingContext drawingContext = drawingVisual.RenderOpen();

    // Create a rectangle and draw it in the DrawingContext.
    Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
    drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);

    // Persist the drawing content.
    drawingContext.Close();

    return drawingVisual;
}

Creating Overrides for FrameworkElement Members

The host container object is responsible for managing its collection of visual objects. This requires that the host container implement member overrides for the derived FrameworkElement class.

The following list describes the two members you must override:

  • GetVisualChild: Returns a child at the specified index from the collection of child elements.

  • VisualChildrenCount: Gets the number of visual child elements within this element.

In the following example, overrides for the two FrameworkElement members are implemented.

// Provide a required override for the VisualChildrenCount property.
protected override int VisualChildrenCount
{
    get { return _children.Count; }
}

// Provide a required override for the GetVisualChild method.
protected override Visual GetVisualChild(int index)
{
    if (index < 0 || index >= _children.Count)
    {
        throw new ArgumentOutOfRangeException();
    }

    return _children[index];
}

Providing Hit Testing Support

The host container object can provide event handling even if it does not display any visible properties—however, its Visibility property must be set to Visible. This allows you to create an event handling routine for the host container that can trap mouse events, such as the release of the left mouse button. The event handling routine can then implement hit testing by invoking the HitTest method. The method's HitTestResultCallback parameter refers to a user-defined procedure that you can use to determine the resulting action of a hit test.

In the following example, hit testing support is implemented for the host container object and its children.

// Capture the mouse event and hit test the coordinate point value against
// the child visual objects.
void MyVisualHost_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    // Retreive the coordinates of the mouse button event.
    System.Windows.Point pt = e.GetPosition((UIElement)sender);

    // Initiate the hit test by setting up a hit test result callback method.
    VisualTreeHelper.HitTest(this, null, new HitTestResultCallback(myCallback), new PointHitTestParameters(pt));
}

// If a child visual object is hit, toggle its opacity to visually indicate a hit.
public HitTestResultBehavior myCallback(HitTestResult result)
{
    if (result.VisualHit.GetType() == typeof(DrawingVisual))
    {
        if (((DrawingVisual)result.VisualHit).Opacity == 1.0)
        {
            ((DrawingVisual)result.VisualHit).Opacity = 0.4;
        }
        else
        {
            ((DrawingVisual)result.VisualHit).Opacity = 1.0;
        }
    }

    // Stop the hit test enumeration of objects in the visual tree.
    return HitTestResultBehavior.Stop;
}





Tutorial: Hosting Visual Objects in a Win32 Application


Windows Presentation Foundation (WPF) provides a rich environment for creating applications. However, when you have a substantial investment in Win32 code, it might be more effective to add WPF functionality to your application rather than rewrite your code. To provide support for Win32 and WPF graphics subsystems used concurrently in an application, WPF provides a mechanism for hosting objects in a Win32 window.

This tutorial describes how to write a sample application, Hit Test with Win32 Interoperation Sample, that hosts WPF visual objects in a Win32 window.

Requirements

This tutorial assumes a basic familiarity with both WPF and Win32 programming. For a basic introduction to WPF programming, see Walkthrough: My First WPF Desktop Application1. For an introduction to Win32 programming, see any of the numerous books on the subject, in particularProgramming Windows by Charles Petzold.

System_CAPS_noteNote

This tutorial includes a number of code examples from the associated sample. However, for readability, it does not include the complete sample code. For the complete sample code, see Hit Test with Win32 Interoperation Sample.

Creating the Host Win32 Window

The key to hosting WPF objects in a Win32 window is the HwndSource class. This class wraps the WPF objects in a Win32 window, allowing them to be incorporated into your user interface (UI) as a child window.

The following example shows the code for creating the HwndSource object as the Win32 container window for the visual objects. To set the window style, position, and other parameters for the Win32 window, use the HwndSourceParameters object.

// Constant values from the "winuser.h" header file.
internal const int WS_CHILD = 0x40000000,
                   WS_VISIBLE = 0x10000000;

internal static void CreateHostHwnd(IntPtr parentHwnd)
{
    // Set up the parameters for the host hwnd.
    HwndSourceParameters parameters = new HwndSourceParameters("Visual Hit Test", _width, _height);
    parameters.WindowStyle = WS_VISIBLE | WS_CHILD;
    parameters.SetPosition(0, 24);
    parameters.ParentWindow = parentHwnd;
    parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);

    // Create the host hwnd for the visuals.
    myHwndSource = new HwndSource(parameters);

    // Set the hwnd background color to the form's background color.
    myHwndSource.CompositionTarget.BackgroundColor = System.Windows.Media.Brushes.OldLace.Color;
}
System_CAPS_noteNote

The value of the ExtendedWindowStyle property cannot be set to WS_EX_TRANSPARENT. This means that the host Win32 window cannot be transparent. For this reason, the background color of the host Win32 window is set to the same background color as its parent window.

Adding Visual Objects to the Host Win32 Window

Once you have created a host Win32 container window for the visual objects, you can add visual objects to it. You will want to ensure that any transformations of the visual objects, such as animations, do not extend beyond the bounds of the host Win32 window's bounding rectangle.

The following example shows the code for creating the HwndSource object and adding visual objects to it.

System_CAPS_noteNote

The RootVisual property of the HwndSource object is set to the first visual object added to the host Win32 window. The root visual object defines the top-most node of the visual object tree. Any subsequent visual objects added to the host Win32 window are added as child objects.

public static void CreateShape(IntPtr parentHwnd)
{
    // Create an instance of the shape.
    MyShape myShape = new MyShape();

    // Determine whether the host container window has been created.
    if (myHwndSource == null)
    {
        // Create the host container window for the visual objects.
        CreateHostHwnd(parentHwnd);

        // Associate the shape with the host container window.
        myHwndSource.RootVisual = myShape;
    }
    else
    {
        // Assign the shape as a child of the root visual.
        ((ContainerVisual)myHwndSource.RootVisual).Children.Add(myShape);
    }
}

Implementing the Win32 Message Filter

The host Win32 window for the visual objects requires a window message filter procedure to handle messages that are sent to the window from the application queue. The window procedure receives messages from the Win32 system. These may be input messages or window-management messages. You can optionally handle a message in your window procedure or pass the message to the system for default processing.

The HwndSource object that you defined as the parent for the visual objects must reference the window message filter procedure you provide. When you create the HwndSource object, set the HwndSourceHook property to reference the window procedure.

parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);

The following example shows the code for handling the left and right mouse button up messages. The coordinate value of the mouse hit position is contained in the value of the lParam parameter.

// Constant values from the "winuser.h" header file.
internal const int WM_LBUTTONUP = 0x0202,
                   WM_RBUTTONUP = 0x0205;

internal static IntPtr ApplicationMessageFilter(
    IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle messages passed to the visual.
    switch (message)
    {
        // Handle the left and right mouse button up messages.
        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            System.Windows.Point pt = new System.Windows.Point();
            pt.X = (uint)lParam & (uint)0x0000ffff;  // LOWORD = x
            pt.Y = (uint)lParam >> 16;               // HIWORD = y
            MyShape.OnHitTest(pt, message);
            break;
    }

    return IntPtr.Zero;
}

Processing the Win32 Messages

The code in the following example shows how a hit test is performed against the hierarchy of visual objects contained in the host Win32 window. You can identify whether a point is within the geometry of a visual object, by using the HitTest method to specify the root visual object and the coordinate value to hit test against. In this case, the root visual object is the value of the RootVisual property of the HwndSource object.

// Constant values from the "winuser.h" header file.
public const int WM_LBUTTONUP = 0x0202,
                 WM_RBUTTONUP = 0x0205;

// Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked.
public static void OnHitTest(System.Windows.Point pt, int msg)
{
    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Determine whether to change the color of the circle or to delete the shape.
    if (msg == WM_LBUTTONUP)
    {
        MyWindow.changeColor = true;
    }
    if (msg == WM_RBUTTONUP)
    {
        MyWindow.changeColor = false;
    }

    // Set up a callback to receive the hit test results enumeration.
    VisualTreeHelper.HitTest(MyWindow.myHwndSource.RootVisual,
                             null,
                             new HitTestResultCallback(CircleHitTestResult),
                             new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}

For more information on hit testing against visual objects, see Hit Testing in the Visual Layer.





How-to Topics





How to: Get the Offset of a Visual


These examples show how to retrieve the offset value of a visual object that is relative to its parent, or any ancestor or descendant.

Example

The following markup example shows a TextBlock that is defined with Margin value of 4.

<TextBlock Name="myTextBlock" Margin="4" Text="Hello, world" />

The following code example shows how to use the GetOffset method to retrieve the offset of the TextBlock. The offset values are contained within the returned Vector value.

// Return the offset vector for the TextBlock object.
Vector vector = VisualTreeHelper.GetOffset(myTextBlock);

// Convert the vector to a point value.
Point currentPoint = new Point(vector.X, vector.Y);

The offset takes into account the Margin value. In this case, X is 4, and Y is 4.

The returned offset value is relative to the parent of the Visual. If you want to return an offset value that is not relative to the parent of a Visual, use the TransformToAncestor method.

Getting the Offset Relative to an Ancestor

The following markup example shows a TextBlock that is nested within two StackPanel objects.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <StackPanel Margin="16">
    <StackPanel Margin="8">
      <TextBlock Name="myTextBlock" Margin="4" Text="Hello, world" />
    </StackPanel>
  </StackPanel>
</Window>

The following illustration shows the results of the markup.

Offset values of objects

TextBlock nested within two StackPanels

The following code example shows how to use the TransformToAncestor method to retrieve the offset of the TextBlock relative to the containing Window. The offset values are contained within the returned GeneralTransform value.

// Return the general transform for the specified visual object.
GeneralTransform generalTransform1 = myTextBlock.TransformToAncestor(this);

// Retrieve the point value relative to the parent.
Point currentPoint = generalTransform1.Transform(new Point(0, 0));

The offset takes into account the Margin values for all objects within the containing Window. In this case, X is 28 (16 + 8 + 4), and Y is 28.

The returned offset value is relative to the ancestor of the Visual. If you want to return an offset value that is relative to the descendant of a Visual, use the TransformToDescendant method.

Getting the Offset Relative to a Descendant

The following markup example shows a TextBlock that is contained within a StackPanel object.

<StackPanel Name="myStackPanel" Margin="8">
  <TextBlock Name="myTextBlock" Margin="4" Text="Hello, world" />
</StackPanel>

The following code example shows how to use the TransformToDescendant method to retrieve the offset of the StackPanel relative to its child TextBlock. The offset values are contained within the returned GeneralTransform value.

// Return the general transform for the specified visual object.
GeneralTransform generalTransform1 = myStackPanel.TransformToDescendant(myTextBlock);

// Retrieve the point value relative to the child.
Point currentPoint = generalTransform1.Transform(new Point(0, 0));

The offset takes into account the Margin values for all objects. In this case, X is -4, and Y is -4. The offset values are negative values, since the parent object is negatively offset relative to its child object.





How to: Enumerate Drawing Content of a Visual


The Drawing object provide an object model for enumerating the contents of a Visual.

Example

The following example uses the GetDrawing method to retrieve the DrawingGroup value of a Visual and enumerate it.

System_CAPS_noteNote

When you are enumerating the contents of the visual, you are retrieving Drawing objects, and not the underlying representation of the render data as a vector graphics instruction list. For more information, see WPF Graphics Rendering Overview.

public void RetrieveDrawing(Visual v)
{
    DrawingGroup dGroup = VisualTreeHelper.GetDrawing(v);
    EnumDrawingGroup(dGroup);

}

 // Enumerate the drawings in the DrawingGroup.
 public void EnumDrawingGroup(DrawingGroup drawingGroup)
 {
     DrawingCollection dc = drawingGroup.Children;

     // Enumerate the drawings in the DrawingCollection.
     foreach (Drawing drawing in dc)
     {
         // If the drawing is a DrawingGroup, call the function recursively.
         if (drawing.GetType() == typeof(DrawingGroup))
         {
             EnumDrawingGroup((DrawingGroup)drawing);
         }
         else if (drawing.GetType() == typeof(GeometryDrawing))
         {
             // Perform action based on drawing type.  
         }
         else if (drawing.GetType() == typeof(ImageDrawing))
         {
             // Perform action based on drawing type.
         }
         else if (drawing.GetType() == typeof(GlyphRunDrawing))
         {
             // Perform action based on drawing type.
         }
         else if (drawing.GetType() == typeof(VideoDrawing))
         {
             // Perform action based on drawing type.
         }
     }
 }





How to: Hit Test Geometry in a Visual


This example shows how to perform a hit test on a visual object that is composed of one or more Geometry objects.

Example

The following example shows how to retrieve the DrawingGroup from a visual object that uses the GetDrawing method. A hit test is then performed on the rendered content of each drawing in the DrawingGroup to determine which geometry was hit.

System_CAPS_noteNote

In most cases, you would use the HitTest method to determine whether a point intersects any of the rendered content of a visual.

// Determine if a geometry within the visual was hit.
static public void HitTestGeometryInVisual(Visual visual, Point pt)
{
    // Retrieve the group of drawings for the visual.
    DrawingGroup drawingGroup = VisualTreeHelper.GetDrawing(visual);
    EnumDrawingGroup(drawingGroup, pt);
}

// Enumerate the drawings in the DrawingGroup.
static public void EnumDrawingGroup(DrawingGroup drawingGroup, Point pt)
{
    DrawingCollection drawingCollection = drawingGroup.Children;

    // Enumerate the drawings in the DrawingCollection.
    foreach (Drawing drawing in drawingCollection)
    {
        // If the drawing is a DrawingGroup, call the function recursively.
        if (drawing.GetType() == typeof(DrawingGroup))
        {
            EnumDrawingGroup((DrawingGroup)drawing, pt);
        }
        else if (drawing.GetType() == typeof(GeometryDrawing))
        {
            // Determine whether the hit test point falls within the geometry.
            if (((GeometryDrawing)drawing).Geometry.FillContains(pt))
            {
                // Perform action based on hit test on geometry.
            }
        }

    }
}

The FillContains method is an overloaded method that allows you to hit test by using a specified Point or Geometry. If a geometry is stroked, the stroke can extend outside the fill bounds. In which case, you may want to call StrokeContains in addition to FillContains.

You can also provide a ToleranceType that is used for the purposes of Bezier flattening.

System_CAPS_noteNote

This sample does not take into account any transforms or clipping that may be applied to the geometry. In addition, this sample will not work with a styled control, since it does not have any drawings directly associated with it.






How to: Hit Test Using Geometry as a Parameter


This example shows how to perform a hit test on a visual object using a Geometry as a hit test parameter.

Example

The following example shows how to set up a hit test using GeometryHitTestParameters for the HitTest method. The Point value that is passed to the OnMouseDown method is used to create a Geometry object in order to expand the range of the hit test.

// Respond to the mouse button down event by setting up a hit test results callback.
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Expand the hit test area by creating a geometry centered on the hit test point.
    EllipseGeometry expandedHitTestArea = new EllipseGeometry(pt, 10.0, 10.0);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myControl, null,
        new HitTestResultCallback(MyHitTestResultCallback),
        new GeometryHitTestParameters(expandedHitTestArea));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}

The IntersectionDetail property of GeometryHitTestResult provides information about the results of a hit test that uses a Geometry as a hit test parameter. The following illustration shows the relationship between the hit test geometry (the blue circle) and the rendered content of the target visual object (the red square).

Diagram of IntersectionDetail used in hit testing

Intersection between hit test geometry and target visual object

The following example shows how to implement a hit test callback when a Geometry is used as a hit test parameter. The result parameter is cast to a GeometryHitTestResult in order to retrieve the value of the IntersectionDetail property. The property value allows you to determine if the Geometry hit test parameter is fully or partially contained within the rendered content of the hit test target. In this case, the sample code is only adding hit test results to the list for visuals that are fully contained within the target boundary.

// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResultCallback(HitTestResult result)
{
    // Retrieve the results of the hit test.
    IntersectionDetail intersectionDetail = ((GeometryHitTestResult)result).IntersectionDetail;

    switch (intersectionDetail)
    {
        case IntersectionDetail.FullyContains:

            // Add the hit test result to the list that will be processed after the enumeration.
            hitResultsList.Add(result.VisualHit);

            return HitTestResultBehavior.Continue;

        case IntersectionDetail.Intersects:

            // Set the behavior to return visuals at all z-order levels.
            return HitTestResultBehavior.Continue;

        case IntersectionDetail.FullyInside:

            // Set the behavior to return visuals at all z-order levels.
            return HitTestResultBehavior.Continue;

        default:
            return HitTestResultBehavior.Stop;
    }
}
System_CAPS_noteNote

The HitTestResult callback should not be called when the intersection detail is Empty.







How to: Hit Test Using a Win32 Host Container


You can create visual objects within a Win32 window by providing a host window container for the visual objects. To provide event handling for the contained visual objects you process the messages passed to the host window container’s message filter loop. Refer to Tutorial: Hosting Visual Objects in a Win32 Application for more information on how to host visual objects in a Win32 window.

Example

The following code shows how to set up mouse event handlers for a Win32 window that is used as a host container for visual objects.

// Constant values from the "winuser.h" header file.
internal const int WM_LBUTTONUP = 0x0202,
                   WM_RBUTTONUP = 0x0205;

internal static IntPtr ApplicationMessageFilter(
    IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle messages passed to the visual.
    switch (message)
    {
        // Handle the left and right mouse button up messages.
        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            System.Windows.Point pt = new System.Windows.Point();
            pt.X = (uint)lParam & (uint)0x0000ffff;  // LOWORD = x
            pt.Y = (uint)lParam >> 16;               // HIWORD = y
            MyShape.OnHitTest(pt, message);
            break;
    }

    return IntPtr.Zero;
}

The following example shows how to set up a hit test in response to trapping specific mouse events.

// Constant values from the "winuser.h" header file.
public const int WM_LBUTTONUP = 0x0202,
                 WM_RBUTTONUP = 0x0205;

// Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked.
public static void OnHitTest(System.Windows.Point pt, int msg)
{
    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Determine whether to change the color of the circle or to delete the shape.
    if (msg == WM_LBUTTONUP)
    {
        MyWindow.changeColor = true;
    }
    if (msg == WM_RBUTTONUP)
    {
        MyWindow.changeColor = false;
    }

    // Set up a callback to receive the hit test results enumeration.
    VisualTreeHelper.HitTest(MyWindow.myHwndSource.RootVisual,
                             null,
                             new HitTestResultCallback(CircleHitTestResult),
                             new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}

The HwndSource object presents Windows Presentation Foundation (WPF) content within a Win32 window. The value of the RootVisual property of theHwndSource object represents the top-most node in the visual tree hierarchy.

For the complete sample on hit testing objects using a Win32 host container, see Hit Test with Win32 Interoperation Sample.






How to: Render on a Per Frame Interval Using CompositionTarget


The WPF animation engine provides many features for creating frame-based animation. However, there are application scenarios in which you need finer-grained control over rendering on a per frame basis. The CompositionTarget object provides the ability to create custom animations based on a per-frame callback.

CompositionTarget is a static class which represents the display surface on which your application is being drawn. The Rendering event is raised each time the application's scene is drawn. The rendering frame rate is the number of times the scene is drawn per second.

System_CAPS_noteNote

For a complete code sample using CompositionTarget, see Using the CompositionTarget Sample.

Example

The Rendering event fires during the WPF rendering process. The following example shows how you register an EventHandler delegate to the static Rendering method on CompositionTarget.

// Add an event handler to update canvas background color just before it is rendered.
CompositionTarget.Rendering += UpdateColor;

You can use your rendering event handler method to create custom drawing content. This event handler method gets called once per frame. Each time that WPF marshals the persisted rendering data in the visual tree across to the composition scene graph, your event handler method is called. In addition, if changes to the visual tree force updates to the composition scene graph, your event handler method is also called. Note that your event handler method is called after layout has been computed. However, you can modify layout in your event handler method, which means that layout will be computed once more before rendering.

The following example shows how you can provide custom drawing in a CompositionTarget event handler method. In this case, the background color of the Canvas is drawn with a color value based on the coordinate position of the mouse. If you move the mouse inside the Canvas, its background color changes. In addition, the average frame rate is calculated, based on the current elapsed time and the total number of rendered frames.

// Called just before frame is rendered to allow custom drawing.
protected void UpdateColor(object sender, EventArgs e)
{
    if (_frameCounter++ == 0)
    {
        // Starting timing.
        _stopwatch.Start();
    }

    // Determine frame rate in fps (frames per second).
    long frameRate = (long)(_frameCounter / this._stopwatch.Elapsed.TotalSeconds);
    if (frameRate > 0)
    {
        // Update elapsed time, number of frames, and frame rate.
        myStopwatchLabel.Content = _stopwatch.Elapsed.ToString();
        myFrameCounterLabel.Content = _frameCounter.ToString();
        myFrameRateLabel.Content = frameRate.ToString();
    }

    // Update the background of the canvas by converting MouseMove info to RGB info.
    byte redColor = (byte)(_pt.X / 3.0);
    byte blueColor = (byte)(_pt.Y / 2.0);
    myCanvas.Background = new SolidColorBrush(Color.FromRgb(redColor, 0x0, blueColor));
}

You may discover that your custom drawing runs at different speeds on different computers. This is because your custom drawing 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. For information on determining the graphics hardware capability and performance for a device that runs a WPF application, see Graphics Rendering Tiers.

Adding or removing a rendering EventHandler 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 Common Language Runtime (CLR). Also note that rendering events are not guaranteed to be called in any particular order. If you have multiple EventHandler delegates that rely on a particular order, you should register a single Rendering event and multiplex the delegates in the correct order yourself.
















'프로그래밍 > WPF' 카테고리의 다른 글

Commanding Overview  (0) 2017.02.08
Properties  (0) 2017.01.24
Data Binding  (0) 2017.01.10
Graphics and Multimedia - Animation Overview  (0) 2017.01.07
Graphics and Multimedia  (0) 2016.11.24
:
Posted by 지훈2