Yet another blog about WPF, Surface, SL, MVVM, NUI....

2011

2010

2009

2008

Tag - custom

Entries feed - Comments feed

 

[UPDATED] How to call the method from the base of the base of the current class ? (base.base.MyMethod)

11 May 2011

Today I encountered a tricky need in some custom control. It was inheriting from the TabControl but I didn’t want all its feature. Especially I didn’t want it to update the SelectedContent dependency property because it was keeping a strong reference to a ViewModel and keeping it away from the garbage collector.

Continue reading...

 

How to create an hand writing to text control (ink recognizer)

25 October 2010

When building a (multi)touch application you may need one nice feature : translate hand-written text to real words. This open a whole new world full of possibilities like starting some actions when keywords are recognized or simply allow the users to write some text for later use. 

In this post we'll see all the step to create an hand writing to text control and how to tune it.

Continue reading...

 

How to create your own control library (how-to + tips)

3 October 2010

Reusability and factorizing are maybe the most commons things you want and use when you are developing applications. In WPF it often means creating controls library (i don’t mean UserControl) that will be easy to use in multiple applications.


In this post we'll see all the step to create a control library useable in differents projects.

The example to illustrate the theory will be to create an headered control.

 

PS: note that it already exists in the framework under the nice name of GroupBox.

Creating a control library

Foundation of the project

The first step is to use the VS2010 Wizard named "WPF Custom control library" to create the library which will contain the controls. We will name it, by excess of modesty : “AmazingsWPFControls” !

 

As you can see, VS has created some files and directory for us :

project tree

 

 

Let’s take them one by one ! The “Themes\generic.xaml” file is where the WPF engine, when it loads your control, looks for it’s theme aka. a style setting a template. Here is its content :

<Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
 BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

The “AssemblyInfo.cs” file which contains a specific attribute :" ThemeInfo”. It tells the framework where to look for the theme files. Usually it is used like this :

[assembly: ThemeInfo( ResourceDictionaryLocation.None,

//where theme specific resource dictionaries are located //(used if a resource is not found in the page, // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly

//where the generic resource dictionary is located //(used if a resource is not found in the page, // app, or any theme specific resource dictionaries) )]

 

The “CustomControl1.cs” which is an empty custom control the wizard created for you(how kind of him). Its content is interesting because it show you that you have to override the MetaData of the newly created control to force the WPF engine using the style defined in the generic.xaml file.

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

 

 

Each time you want to add a new custom control to the library, you will have to reproduce each of these steps. Of course, the wizard to add a new Custom Control can do it for you but its always a good thing to know how it works. In our case we’ve to rename CustomControl1 to HeaderedControl.

 

Here we are with the foundation of the library. Now we are going to add a template and behaviors to the control.

 

Choose the more appropriated base class

By default the wizard make the control a inherit from the Control class but there are differents options that you may consider for base class :

  • Control : This is a base class which lets you the most freedom but the most to do !
  • ContentControl : This is a good choice if the control will be used to represent a single piece of content.
  • HeaderedContentControl : This is a good choice to represent a single element with an header.
  • ItemsControl: This is a good choice if the control will be used to represent a list of items.

 

 

In our case the best choice should be to inherit from HeaderedContentControl  but we will prefer the ContentControl for demonstration purpose. Note that it meets the specifications: we will represent a single element with an header.

 

Adding a Template and some behaviors to the control

Setting the template of the control

Unlike UserControl there is no code-behind and you set the template of the control by creating a style in the generic.xaml file. This also means that it’s not possible to access the elements via their names because the control and its representation are linked together only at runtime. But don’t worry, you can still access the differents parts following the steps described in this post.

 

 

One of the best practice is to name the revelants parts of your control prefixed with “PART”. By revelants parts, I mean the elements which together build the behavior of your control : for example the track of the slider control is named “PART_Track”. This best practice make your control re-templatable by designer which will know that the controls prefixed by “PART_” are mandatory in the new template they are building. Finally, the parts are declared in the control class via an attribute “TemplatePart” defining the name and the aimed type of control. So in our example, we’ll add a part named “PART_Header which will be the header :

 

/// <summary>
/// A control displaying an header at the top of its content.
/// </summary>    
[TemplatePart(Name = "PART_Header", Type = typeof(Border))]
public class HeaderedControl : Control
{
  // ...
}

 

Adding a content to the control

To add and display a content two things are needed : a property containing the content and a place in the template to display it. The content can be of any type of your choice and is usually placed in the Content property for ContentControl and the Items property for ItemsControl. This is the default behavior but you can override it and tell which property is the content by using the ContentPropertyAttribute on the control’s class :

[ContentPropertyAttribute("Content")] public class HeaderedControl : ContentControl { // ... }

 

Then, in the template, you define where the content is displayed using a ContentPresenter that you can place where you wants depending of the need. We also use a TemplateBinding to bind the content of the class to the ContentPresenter. In our case we add the content under the header :

<Style TargetType="{x:Type local:HeaderedControl}">
   <Setter Property="Template">
       <Setter.Value>
           <ControlTemplate TargetType="{x:Type local:HeaderedControl}">
               <DockPanel>
                   <Border x:Name="PART_Header" DockPanel.Dock="Top" Background="{TemplateBinding Background}"
                           BorderBrush="{TemplateBinding BorderBrush}"
                           BorderThickness="{TemplateBinding BorderThickness}" />
                   <ContentPresenter Content="{TemplateBinding Content}" />
               </DockPanel>
           </ControlTemplate>
       </Setter.Value>
   </Setter>
</Style>

 

 

Adding a bindable property to the control

Adding a property to your control is quite useful if you want to permit some customization of its behavior. This is something very common that you use often without noticing it : every attribute you set in the XAML is simply a property of the controls you use.

 

In our case there is a lot which are already here because of our inheritance of FrameworkElement. But let’s said that we want to add another one all we have to do is do declare a new dependency property and its accessor as in the code behind. In our case we add two property : the header (of type Object) and the position of the header in the control. So we have this class :

public class HeaderedControl : ContentControl
{
  static HeaderedControl()
  {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedControl),
          new FrameworkPropertyMetadata(typeof(HeaderedControl)));
  }
 
  #region Header
 
  /// <summary>
  /// Header Dependency Property
  /// </summary>
  public static readonly DependencyProperty HeaderProperty =
      DependencyProperty.Register("Header", typeof(object), typeof(HeaderedControl),
          new FrameworkPropertyMetadata((object)null));
 
  /// <summary>
  /// Gets or sets the Header property. This dependency property 
  /// indicates the header to display.
  /// </summary>
  public object Header
  {
      get { return (object)GetValue(HeaderProperty); }
      set { SetValue(HeaderProperty, value); }
  }
 
  #endregion
 
  #region HeaderPosition
 
  /// <summary>
  /// HeaderPosition Dependency Property
  /// </summary>
  public static readonly DependencyProperty HeaderPositionProperty =
      DependencyProperty.Register("HeaderPosition", typeof(HeaderPosition), typeof(HeaderedControl),
          new FrameworkPropertyMetadata(HeaderPosition.Top));
 
  /// <summary>
  /// Gets or sets the HeaderPosition property. This dependency property 
  /// indicates ....
  /// </summary>
  public HeaderPosition HeaderPosition
  {
      get { return (HeaderPosition)GetValue(HeaderPositionProperty); }
      set { SetValue(HeaderPositionProperty, value); }
  }
 
  #endregion
 
 
  /// <summary>
  /// Defines where to place the header
  /// </summary>
  public enum HeaderPosition : int
  {
      /// <summary>
      /// The header is positioned on the left.
      /// </summary>
      Left = 0,
 
      /// <summary>
      /// The header is positioned at the top.
      /// </summary>
      Top = 1,
 
      /// <summary>
      ///  The header is positioned on the right.
      /// </summary>
      Right = 2,
 
      /// <summary>
      ///  The header is positioned at the bottom.
      /// </summary>
      Bottom = 3,
 
  }
}
 

Now let’s use these properties in the template to show and place the header at the right position. We will add another content presenter to display the header and we’ll use a converter to convert the header position into the correct DockPanel.Dock value (only the revelant XAML is showed) :

<ControlTemplate TargetType="{x:Type local:HeaderedControl}">
  <Border Background="{TemplateBinding Background}" 
          BorderBrush="{TemplateBinding BorderBrush}"
          BorderThickness="{TemplateBinding BorderThickness}">
      <DockPanel>
          <Border x:Name="PART_Header" 
              DockPanel.Dock="{Binding PositionOfTheHeader,
              RelativeSource={RelativeSource Mode=TemplatedParent},
              Converter={conv:HeaderPositionToDockPositionConverter}}">
              <ContentPresenter Content="{TemplateBinding Header}" />
          </Border>
          <ContentPresenter Content="{TemplateBinding Content}" />
      </DockPanel>
  </Border>
</ControlTemplate>
Adding a custom command to the control

Adding a command to our control is not as straightforward as I thought before to try Smile but here is the guideline is used to make it work fine :

  1. Create the RoutedCommand of your choice in the control class,
  2. Create a private method which will be called when the command is executed,
  3. Register a Class Command binding of this command in the static constructor of the control (you can think of it as a static command binding which will be called each time the command is executed),
  4. In the handler of the class command binding gets the sender of the command, cast it to your class and call the private method you created just before.

 

In our case there no really need of a command but lets say that we will add a command which sets the position of the header property to the “Top” value we will results with this code (against only the revelant part):

static HeaderedControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedControl),
   new FrameworkPropertyMetadata(typeof(HeaderedControl)));
 
//Instanciate the command
MoveHeaderToTopCommand = new RoutedUICommand("MoveHeaderToTop",
   "MoveHeaderToTop", typeof(HeaderedControl));
 
//Create the command binding
CommandBinding moveHeaderToTopCommandBinding =
   new CommandBinding(MoveHeaderToTopCommand, 
                  MoveHeaderToTopCommand_Executed,
                  MoveHeaderToTopCommand_CanExecute);
 
CommandManager.
   RegisterClassCommandBinding(typeof(HeaderedControl), 
                     moveHeaderToTopCommandBinding);
}
 
 
static void MoveHeaderToTopCommand_Executed(
        object sender, ExecutedRoutedEventArgs e)
{
    HeaderedControl headeredControl = 
                 sender as HeaderedControl;
    if (headeredControl != null)
       headeredControl.moveHeaderToTop();
}
 
static void MoveHeaderToTopCommand_CanExecute(object sender, 
       CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
 
private void moveHeaderToTop()
{
this.PositionOfTheHeader = HeaderPosition.Top;
}
 

The command is then useable as any other WPF command in your application and you can even use parameters if needed.

 

Adding a Routed event to the control

Adding an event can be useful to notify the others part of your application that an action occurred in the control or to deliver informations.

 

To add an event here are the guideline :

  1. (optionnal) Creates the events args inherithing from RoutedEventArgs that you will use
  2. Declare the event handler of the event,
  3. Declare the RoutedEvent with the name of your choice,
  4. Creates the accesor for a simplier use,
  5. (optionnal) creates a raiseYourEvent method to raise the event easily.

 

 

Now lets assume we wants to raise an event when the header is clicked. For this we’ll subscribe to the Header “MouseDown” event in the OnApplyTemplate() method and raise a newly created event in its handler :

public override void OnApplyTemplate()
{
   base.OnApplyTemplate();
 
   PART_Header = this.GetTemplateChild("PART_Header") as Border;
   if (PART_Header == null)
       throw new ArgumentNullException(
           "Can't find PART_Header in the HeaderedControl template.");
   PART_Header.MouseDown += (a, b) => { RaiseHeaderClickedEvent(); };
}
 
 
/// <summary>
/// the event handler delegate
/// </summary>
public delegate void HeaderClickedEventHandler(object sender, 
    HeaderClickedEventArgs e);
 
/// <summary>
/// Create a custom routed event by first 
/// registering a RoutedEventID
/// This event uses the bubbling routing strategy
/// </summary>
public static readonly RoutedEvent HeaderClickedEvent = 
    EventManager.RegisterRoutedEvent(
   "HeaderClicked", RoutingStrategy.Bubble, 
   typeof(HeaderClickedEventHandler), typeof(HeaderedControl));
 
/// <summary>
/// Occurs when the header is clicked.
/// </summary>
public event RoutedEventHandler HeaderClicked
{
   add { AddHandler(HeaderClickedEvent, value); }
   remove { RemoveHandler(HeaderClickedEvent, value); }
}
 
/// <summary>
/// Raises the header clicked event.
/// </summary>
void RaiseHeaderClickedEvent()
{
   HeaderClickedEventArgs newEventArgs = 
    new HeaderClickedEventArgs(
            HeaderedControl.HeaderClickedEvent);
   RaiseEvent(newEventArgs);
}
 
/// <summary>
/// The header has been clicked event args
/// </summary>
public class HeaderClickedEventArgs : RoutedEventArgs
{
   /// <summary>
   /// Initializes a new instance of the
   /// <see cref="HeaderClickedEventArgs"/> class.
   /// </summary>
   /// <param name="routedEvent">The routed event.</param>
   public HeaderClickedEventArgs(RoutedEvent routedEvent) 
     : base(routedEvent) { }
}

 

 

 

Ease the use of your library by declaring an URL as an XML namespace

 

What is it ? Simply that when you will use the controls in another page you will not declare the XML namespace via an assembly name but via an URL easy to remember. Example :

<Window x:Class="AmazingWPFControls.Showcase.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
 xmlns:controls="http://blog.lexique-du-net.com/wpf/AmazingsWPFControls"
/>


Why to do it ?

So now all you have to do is to create a bright new control Smile

Interesting links



Shout it kick it on DotNetKicks.com

 

UserControl/Control : how to get a reference to an element of the template

14 September 2010

When you want to create your own custom control you have two choices : create an UserControl or inherit from one of the "Control's classes" (ContentControl, ItemsControls or Control itself). When doing so, you'll surely need to access to the visual parts of your template from the code to add to it a nice behavior.

In this post, we'll discover how to access the template children by using the FindName method even on UserControl.

You create a control

I won't explain you how to create a custom control, so here is its base code :

[TemplatePart(Name = "PART_MyGrid", Type = typeof(Grid))]
  public class MyCustomControl : ContentControl
  {
    private Grid myAimedGrid;
 
    static MyCustomControl()
    {
	  //Overrides the style by ours
      DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
          new FrameworkPropertyMetadata(typeof(MyCustomControl)));
    }
  }



And here is how we define its template for the generic visual theme in the "Themes\generic.xaml" file. Notice that we add a named Grid :"PART_MyGrid". We'll seek for it later from the code.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:FindNamesApplication.MyContentControl">
  <Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:MyCustomControl }">
          <Grid x:Name="PART_MyGrid" Background="Black" Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}">
            <ContentPresenter Content="{TemplateBinding Content}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>



Now how can we find the grid from the code behind ? Simply by accessing the template at the right moment : when the template is applied.
To do so we will override the OnApplyTemplate() method and access directly to the grid by its name with the FindName method. We can then act on it as we wish.

public override void OnApplyTemplate()
{
  //Effectively apply the template
  base.OnApplyTemplate();
 
  //Find the grid in the template once it's applied
  myAimedGrid = base.Template.FindName("PART_MyGrid", this) as Grid;
 
  //We can subscribe to its events
  myAimedGrid.PreviewMouseDown += 
     new MouseButtonEventHandler(myAimedGrid_PreviewMouseDown);
}
 
void myAimedGrid_PreviewMouseDown(object sender,
 System.Windows.Input.MouseButtonEventArgs e)
{
  //Proof 
  MessageBox.Show("Mouse preview Down on the grid !");
}



By the way, when you create a custom control which is focused on reusability you should imperatively declare its differents parts by using the TemplatePart attribute :

[TemplatePart(Name="PART_MyGrid",Type=typeof(Grid))]
public class CustomControl : ContentControl
{
// ....
}



You create an user control

Now the hardest part of the post : you create an user control as a reusable part of your application. To do so you create the C# file and the XAML file and as you want it to be customized, you set it's ContentTemplate as below :

/// <summary>
/// Interaction logic for MyCustomUserControl.xaml
/// </summary>
public partial class MyCustomUserControl : UserControl
{
  private Grid myAimedGrid;
 
  public MyCustomUserControl()
  {
    InitializeComponent();
  }
}

The XAML file :

<UserControl x:Class="FindNamesApplication.MyUserControl.MyCustomUserControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300"
        d:DesignWidth="300">
    <UserControl.ContentTemplate>
        <DataTemplate>
            <Grid x:Name="PART_MyGrid" Background="Black" Width="{TemplateBinding Width}"
                    Height="{TemplateBinding Height}">
                <ContentPresenter Content="{TemplateBinding Content}" />
            </Grid>
        </DataTemplate>
    </UserControl.ContentTemplate>
</UserControl>



Then, as you have seen before you override the OnApplyTemplate and get the child with the FindName methods : this won't do the job ! Actually, all you will get is 'null' or an InvalidOperationException sometimes.

Why ? Because by setting the controlTemplate, you define a DataTemplate which is then used by our UserControl to be applied on it's internal ContentPresenter. So by using findName on the UserControl we search the element named "PART_MyGrid" in the template of the UserControl and not in the template created by us and actually used.

So the solution is to seek the element on the correct element which is the ContentPresenter of the template of the UserControl. To do so we'll find it using the VisualTreeHelper to get the ContentPresenter and then use the FindName method with it as a parameter. Here is the code :

public override void OnApplyTemplate()
{
  base.OnApplyTemplate();
 
  //The ContentPresenter is the second child of the UserControl...
  ContentPresenter presenter = (ContentPresenter)
    (VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0));
 
  //Be sure that the template is applied on the presenter
  presenter.ApplyTemplate();
 
  //get the grid from the presenter
  myAimedGrid =
    presenter.ContentTemplate.FindName("PART_MyGrid", presenter) as Grid;
 
  //We can subscribe to its events
  myAimedGrid.PreviewMouseDown
    += new MouseButtonEventHandler(myAimedGrid_PreviewMouseDown);
}
 
void myAimedGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
  //Proof 
  MessageBox.Show("Mouse preview Down on the grid !");
}



Interesting links

Here are some links to go further on the subject :



Conclusion

As we can see, nothing is impossible and once seen, it's quite easy to implement these differents solutions... Have a great code ! The source solution is linked to the post.

Shout it kick it on DotNetKicks.com



 

Binding on a Property which is not a DependencyProperty

5 April 2010

A lot of controls expose properties which are not DependencyProperties and then you can’t put a binding on it. On some other cases, you only have a getter as accessor and you can’t put a binding on it too…

This is for example the case for the ribbon’s group of the office ribbon or the converter’s parameter.

If you ever tried to do so, you surely had an exception throwned :

A 'Binding' cannot be set on the 'SetCEDEJDED' property of type 'Tralala'.
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.


In this post we will discover a work-around…

The main idea is to use a kind of proxy/observer (a definition can be found in this post) which will reflect every change on the source object to the target object and vice versa.

Here are the main parts of the solution ..

Specification: the XAML code we'll use

Here is the code snippet which describe how we will use our proxy in the XAML. There will be no code-behind.

<Window x:Class="BindOnNonDependencyProperty.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:us="clr-namespace:BindOnNonDependencyProperty"
    Title="BindOnNonDependencyProperty" >
  <DockPanel >
    <TextBox x:Name="myTextBox" DockPanel.Dock="Top"  />
    <TextBox x:Name="monTextBlockCible"  DockPanel.Dock="Top"  />
    <us:ExtendedBinding Source="{Binding ElementName=myTextBox,Path=Text,Mode=TwoWay}"
              Target="{Binding ElementName=monTextBlockCible,Path=Text,Mode=TwoWay}"
              />
  </DockPanel>
</Window>



The correct base class for our proxy/observer

We will call it ExtendedBinding and it must be inherithing from DependencyObject at last to be abble to own DependencyProperty. But the only way to add a DependencyObject into our XAML is to add it into a resourceDictonary.

This is a drawnback because, by doing it, it will no more be into the control's tree and then it will be impossible to make a binding on one of it's property. Note that it's still possible to use it as a Target from another place in our XAML but you can't do a Binding on one of it's properties. This code will not work :

<Windows.Resources>
   <MyDependencyObject x:Key="myKey" MyProperty="{Binding Tralala, ElementName=myTarget}" />
</Windows.Resources>



Then to put it insode the control's tree, we only had to make it an UIElement will you say... No because in the actual version of the Framework you won't have inheritance of the DataContext and the use of the 'ElementName' binding will be prohibited. Hopefully, there is a solution, our proxy have to inherit from FrameworkElement and everything will work fine !

The DependencyProperties

We will add two dependencyProperties, one will be the target and the second will be the source.

These DP will be customize by using the FrameworkPropertyMetadata two enables these features :

  • Binding will be done using the TwoWay mode,
  • The UpdateSourceTrigger used will be the PropertyChanged event.


How it works

The core of our proxy is to override the DependencyObject's OnPropertyChanged method. Each change on the source or the target will update it's counterparty.

We have to take care not to fall into a loop : when we will update the target or the source we'll also raise a PropertyChanged event and we must ignore this one....


Final code

public class ExtendedBinding : FrameworkElement
  {
    #region Source DP
    //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Source
    {
      get { return GetValue(ExtendedBinding.SourceProperty); }
      set { SetValue(ExtendedBinding.SourceProperty, value); }
    }
    #endregion
 
    #region Target DP
      //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty TargetProperty =
      DependencyProperty.Register("Target", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Target
    {
      get { return GetValue(ExtendedBinding.TargetProperty); }
      set { SetValue(ExtendedBinding.TargetProperty, value); }
    }
    #endregion
 
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
      base.OnPropertyChanged(e);
      if (e.Property.Name == ExtendedBinding.SourceProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Target = Source;
      }
      else if (e.Property.Name == ExtendedBinding.TargetProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Source = Target;
      }
    }
 
  }



Shout it kick it on DotNetKicks.com



 

CREATE, LAUNCH and CONTROL a WPF animation FROM CODE

7 July 2009

The problem

Sometimes you need to animate your specific object and for this purpose there is the WPF animation.

Here are the prerequireds :

  • The property you want to animate must be a DependencyProperty,
  • The property must so be a part of a DependencyObject,
  • Your object must implement IAnimatable to be able to launch the animation.


The difficulty resides in being able to create an animation, create a storyboard and set the corrects values for the attached properties - TargetName and TargetProperty - on the animation. All of this directly in the code.

You can then control the animation, the usual way than in XAML.

My solution/checklist

Here is how I did it, step by step.

First I make my business object derives from FrameWorkElement. Why ?
Because, this make my object a dependency object. Also, my object will implements IAnimatable ( FrameWorkElement heritate from UIElement which implements IAnimatable). And finally, it gets a NameScope which will be used later.
So here is my object:

public class Puppet: FrameworkElement {    }



Next I will had a DependencyProperty to be animated. Here I animate a value of type Point. I create all the usual things for the dependencyProperty and also add a storyboard (I will always use the same):

public class Puppet: FrameworkElement
{
   public static DependencyProperty ContactMovementProperty =
   DependencyProperty.Register("ContactMovement", typeof(Point), typeof(Puppet));
 
   public Point ContactMovement
   {
       get { return (Point)GetValue(SatelliteNavigator.ContactMovementProperty); }
       set { SetValue(SatelliteNavigator.ContactMovementProperty, value); }
    }
 
    private Storyboard stboard = new Storyboard();
}



Then, let's say I will create and launch the animation directly in the constructor. Here is the code added :

public class()
{
   //Maybe it was running before (if not set in the constructor)
   //stboard.Stop();
 
    double xFinal = 36;
    double yFinal = 36;
    PointAnimation animation = new PointAnimation();
 
    animation.From = ContactMovement;
    animation.To = new Point(xFinal , yFinal );
    animation.Duration = TimeSpan.FromMilliseconds(e.Velocity.Length*7 / deceleration);
    animation.AccelerationRatio = 0f;
    animation.DecelerationRatio = 0.4f;
    String puppetName= "puppet";
 
    NameScope nams = new NameScope();
    NameScope.SetNameScope(this, nams);
    this.RegisterName(puppetName, this);
 
    Storyboard.SetTargetName(animation, puppetName);
    Storyboard.SetTargetProperty(animation, new PropertyPath(Puppet.ContactMovementProperty));
 
    stboard.Children.Add(animation);
    stboard.Begin(this);
 
}


What is done ? First I create the animation with random values for our example.
Then I create a NameScope and set it to our object. Why ? Because it's not created by the runtime for our object and we need one. It will be used by the animation to retrieve the object to animate.

This is done by registering the Puppet object in the namescope and giving the same TargetName of the attachedProperty of the animation(Storyboard.SetTargetName).

Then we clear the children animation of the storyboard (maybe it was not empty) and launch the storyboard by giving it the Puppet (object in which the animated object is).



We can then use the usual methods on storyboard to control the play of the media. For example :

stboard.Stop();


Conclusion

As you can see this is not as easy as writing some XAML lines but it's not impossible !

Any questions ?

kick it on DotNetKicks.com


Shout it


 

Create your VideoPlayer with JavaFX, part one.

9 December 2008

I noticed in this post that the mediaComponent pointed out in the JavaFX demos was not still available and that we will so make our own...

Lets start !

What will we do in our first part ? :

  1. Create our custom node MyMediaComponent
  2. Add the video in our component
  3. Enjoy ourselfs!



Create our custom node: MyMediaComponent

The first thing to do is quite easy : create a new JavaFX class in Nebeans and make it extends the CustomNode class from the javafx.scene package. This class is provided to enable you making your own nodes.

You will then have to ovveride the "create():Node" method :

public override function create():Node{
        return Group{
            content: []
        }
    }

We will add more things in the content when we will build them.
First we will add some var to our class, They will contains some informations about our component :

/** X attribute of our component. */
 public var x : Integer = 0 ;
 /** Y attribute of our component. */
 public var y : Integer = 0 ;
 
/** A description of the video. */
public var vidDescription : String = "" ;
/** The title of the video. */
public var vidTitle : String = "" ;
/** Start volume of the video. */
public var startVolume : Number = 0.5;
/** Do where display the meta informations on the video */
public var showInfoBar : Boolean = true;
/** The url of the video. */
public var mediaURL : String ="" ;
/** The Width in wich where will make the video fit. */
public var vidWith : Integer = 384;
/** The height in wich where will make the video fit. */
public var vidHeight : Integer = 288 ;



Add the video in our component

We will add the video in our component. To add this features we will use three differents Object in the JavaFX API:

  • Media : contains the basic informations about our media, Note that it can be a video or an audio media...
  • MediaPlayer: this object is used to perform the differents actions on our video; play/pause. This component also contains the informations about the status of the current video: playing/paused/buffering, etc ... We give it the media.
  • MediaView: this object is the node in which the video is displayed. We give it the mediaPlayer.



Lets add it in our class and use the differents var we created just before :

var media:Media = Media{
        source: mediaURL
    };
    var mediaPlayer: MediaPlayer = MediaPlayer{
        media:media
        autoPlay:true
    };
 
    var mediaView : MediaView = MediaView{
        x:x
        y:y
        mediaPlayer: bind mediaPlayer
        fitHeight:vidHeight
        fitWidth:vidWith
      };

The autoPlay attribute tells that the video is played as soon as it can be by the player (when enough data is available when buffered).


Now that we have created the different component we will add the mediaView in the group returned by our create() function:

public override function create():Node{
        return Group{
            content: [mediaView ]
        }
    }


Now by adding our component in your application you will display the video and play it. This is a first step in the birth of our videoComponent: Video player, results of part one.


In the next part you will learn how to display the informations on the video currently played when hovering the video and how to add some triggers (play/pause the video by clicking on it).

+++