Silverlight 3 Drag Behavior
July 30, 2009 @ 10:35 am in Microsoft, Silverlight
By now most Silverlight developers should have stumbled on a great new feature of Silverlight 3 and Expression Blends, custom Triggers and Behaviors. I seriously don’t know how I lived without them!
In this blog post I am going to demonstrate a simple, yet useful, example of a behavior which allows a UIElement to become drag able. Figure 1 show the view of my XAML from within Blend. For this example I will create an image with a rounded border which has been placed within a Grid. In the center of my grid I have a Red Rectangle.
To create my Behavior I will jump in to VS2008 and create a new class that will reside within my solution. If my goal was to create a collection of generic behaviors and triggers I would likely create a Silverlight class assembly and deploy it to the library folder within Blend. This would allow my custom behaviors to be available to all Silverlight or WPF project. The following demonstrates the code I used to create by Drag behavior.
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Interactivity;
using System.Windows.Media.Imaging;
namespace DragBehaviors
{
public class Drag : Behavior
{
public static readonly DependencyProperty IsMovableProperty =
DependencyProperty.Register("IsMovable", typeof(bool),
typeof(Drag), new PropertyMetadata(null));
[Category("Target Properties")]
public bool IsMovable { get; set; }
private bool _isDragging = false;
private Point _offset;
private readonly TranslateTransform _elementTranslate = new TranslateTransform();
private TranslateTransform _imgTranslate = new TranslateTransform();
private Image _img = new Image();
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObjectLoaded;
AssociatedObject.HorizontalAlignment = HorizontalAlignment.Left;
AssociatedObject.VerticalAlignment = VerticalAlignment.Top;
AssociatedObject.MouseLeftButtonDown += AssociatedObjectMouseLeftButtonDown;
}
void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
{
AssociatedObject.RenderTransform = _elementTranslate;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseLeftButtonDown -= AssociatedObjectMouseLeftButtonDown;
}
private void AssociatedObjectMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (!_isDragging) return;
FrameworkElement parent = _img.Parent as FrameworkElement;
Point newPosition = e.GetPosition(parent);
// Move the dragon via the new position less the offset
_imgTranslate.X = (newPosition.X - _offset.X);
_imgTranslate.Y = (newPosition.Y - _offset.Y);
}
private void AssociatedObjectMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!_isDragging) return;
Panel panel = AssociatedObject.Parent as Panel;
// turn off drag
_isDragging = false;
// Free the Mouse
_img.ReleaseMouseCapture();
_img.MouseMove -= AssociatedObjectMouseMove;
_img.MouseLeftButtonUp -= AssociatedObjectMouseLeftButtonUp;
if (IsMovable)
{
_elementTranslate.X = _imgTranslate.X;
_elementTranslate.Y = _imgTranslate.Y;
}
_imgTranslate = new TranslateTransform();
_offset = new Point(0, 0);
AssociatedObject.Opacity = 1;
if (panel != null) panel.Children.Remove(_img);
_img = new Image();
}
private void AssociatedObjectMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_isDragging = true;
AssociatedObject.Opacity = .35;
WriteableBitmap bitmap = new WriteableBitmap(AssociatedObject, new TranslateTransform());
if (_img == null) return;
_img.Source = bitmap;
_img.HorizontalAlignment = HorizontalAlignment.Left;
_img.VerticalAlignment = VerticalAlignment.Top;
_img.Stretch = Stretch.None;
_img.Width = bitmap.PixelWidth;
_img.Height = bitmap.PixelHeight;
_imgTranslate.X = _elementTranslate.X;
_imgTranslate.Y = _elementTranslate.Y;
_img.RenderTransform = _imgTranslate;
_img.MouseMove += AssociatedObjectMouseMove;
_img.MouseLeftButtonUp += AssociatedObjectMouseLeftButtonUp;
Panel panel = AssociatedObject.Parent as Panel;
if (panel != null) panel.Children.Add(_img);
_offset = e.GetPosition(_img);
_img.CaptureMouse();
}
}
}For the most part the code should be self explanatory. One cool thing I did was that when I first begin dragging the UIElement, I convert it to a Bitmap and drag an image. The idea is that it will be more efficient to move a image around the screen then the actual collection of UIElements. Once I drop the image than I physically move the UIElement. Note that in its current state this only works correctly if the UIElement I am dragging is Left and Top aligned. This ensures that it is sitting at X=0, Y=0 in the panel it is residing. My guess is that it could be rewritten to support other alignments.
Once the Behavior class is constructed we simply add it to the control we want to drag.
<Border BorderThickness="1" BorderBrush="Black" CornerRadius="6"
HorizontalAlignment="Center" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<behaviors:Drag IsMovable="True"/>
</i:Interaction.Behaviors>
<Grid>
<Image Source="4.jpg" Width="100" Height="100" Stretch="UniformToFill"/>
<TextBlock Text="Image" FontWeight="Bold" FontSize="20"
Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
Code: DragBehavior.zip
Tagged as 

August 3rd, 2009 at 4:40 pm
Yes, using WriteableBitmap is a cool idea and it does seem to have very good performance for the drag.
August 5th, 2009 at 2:51 pm
Very cool, thanks for sharing!
November 27th, 2009 at 5:31 am
It is nice article
December 30th, 2009 at 7:20 pm
Really helpful.
Thank you for writing and Sharing !!!
Regards
March 5th, 2010 at 12:33 am
very very cool,thanks very much ! O(∩_∩)O~~~~