Drag and Drop controls at runtime using Silverlight 2

Posted by: Suprotim Agarwal , on 9/7/2008, in Category Silverlight 2, 3, 4 and 5
Views: 64947
Abstract: In this article, we will see how to add drag-drop functionality on controls kept in the Silverlight plug-in, at runtime. We will explore how to add the MouseMove, MouseDown and MouseUp events to these controls. We will also see how to drag the Button control.
Drag and drop controls at runtime using Silverlight 2
 
Note: This article is written based on a pre-release version of Silverlight and could change in future.
In this article, we will see how to add drag-drop functionality on controls kept in the Silverlight plug-in, at runtime. If you are not familiar with hosting Silverlight 2 in ASP.NET, I would suggest you to read my previous articles which talks about the basics and prerequisites of creating Silverlight applications. Assuming you have some experience in creating Silverlight 2 applications, let us see the steps involved to add drag-drop functionality to Silverlight controls.
Step 1: Open Visual Studio 2008 > File > New Project > Select the language (C# or VB) > Select ‘Silverlight’ in the Project Types > from the templates, select ‘Silverlight Application’. Type a name ‘SilverlightDraggableControl’ (for VB.NET users - SilverlightDraggableControlVB) and location for the project and click ok.
Note: If you are unable to view the templates, you do not have Microsoft Silverlight Tools Beta 2 for Visual Studio 2008. Please check this article on how to install these templates.
Step 2: You will observe that a default page called ‘Page.xaml’ gets created. Set the Width and Height of the user control to 600 & 480 respectively. Remove the <Grid> and replace it with <Canvas> and set its background to LightCoral. The markup after these changes will look similar to the following:
<UserControl x:Class="SilverlightDraggableControl.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Width="600" Height="480">
    <Canvas x:Name="CanvasParent" Background="LightCoral">
    </Canvas>
 
</UserControl>
Step 3: Now add a few controls to the Page.Xaml. To keep things simple, we will add a TextBlock, a Rectangle and a Button control. After setting a few properties on the TextBlock, Rectangle and Button control, the markup will look similar to the following:
    <Canvas x:Name="CanvasParent" Background="LightCoral">
        <TextBlock Canvas.Left="5" x:Name="TextBlock1" Text="LabelA"></TextBlock>
        <Rectangle Canvas.Left="5" Canvas.Top="80" Height="50" Width="130" x:Name="Rectangle1" Fill="Wheat"></Rectangle>
       <Button x:Name="Btn1" Width="50" Height="50" Content="Button">                   
        </Button>       
    </Canvas>
Step 4: In this step, we will add drag-drop functionality to these controls. For this purpose, we will add the MouseMove, MouseDown and MouseUp events to these controls as shown below. Switch to Page.xaml.cs or Page.xaml.vb and add the following code:
C#
public partial class Page : UserControl
{
bool isDragDropInEffect = false;
Point pos;
 
public Page()
{
    InitializeComponent();
    // Loop through all the UIElements in the Canvas  
    // and attach Mouse Event Handlers to the Element
    foreach (UIElement uiEle in CanvasParent.Children)
    {
        uiEle.MouseMove += new MouseEventHandler(Element_MouseMove);
        uiEle.MouseLeftButtonDown += new MouseButtonEventHandler(Element_MouseLeftButtonDown);
        uiEle.MouseLeftButtonUp += new MouseButtonEventHandler(Element_MouseLeftButtonUp);
    }
}
 
void Element_MouseMove(object sender, MouseEventArgs e)
{
    if (isDragDropInEffect)
    {
        FrameworkElement currEle = sender as FrameworkElement;
        // Retrieving the item's current x and y position
        double xPos = e.GetPosition(null).X - pos.X;
        double yPos = e.GetPosition(null).Y - pos.Y;
 
        // Re-position Element
        currEle.SetValue(Canvas.TopProperty, yPos + (double)currEle.GetValue(Canvas.TopProperty));
        currEle.SetValue(Canvas.LeftProperty, xPos + (double)currEle.GetValue(Canvas.LeftProperty));
 
        // Reset the new position value
        pos = e.GetPosition(null);
    }
}
 
 
void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    FrameworkElement fEle = sender as FrameworkElement;
    isDragDropInEffect = true;
    // x and y coords of mouse pointer position
    pos = e.GetPosition(null);  
    // Enable mouse capture on element
    fEle.CaptureMouse();
    // Set the cursor to 'Hand' when mouse pointer is over element
    fEle.Cursor = Cursors.Hand;
}
 
 
void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    if (isDragDropInEffect)
    {
        FrameworkElement ele = sender as FrameworkElement;
        isDragDropInEffect = false;
        // Removes Mouse Capture from Element being dragged
        ele.ReleaseMouseCapture();
    }
}
 
}
 
VB.NET
Public Partial Class Page
      Inherits UserControl
Private isDragDropInEffect As Boolean = False
Private pos As Point
 
Public Sub New()
      InitializeComponent()
      ' Loop through all the UIElements in the Canvas  
      ' and attach Mouse Event Handlers to the Element
      For Each uiEle As UIElement In CanvasParent.Children
            AddHandler uiEle.MouseMove, AddressOf Element_MouseMove
            AddHandler uiEle.MouseLeftButtonDown, AddressOf Element_MouseLeftButtonDown
            AddHandler uiEle.MouseLeftButtonUp, AddressOf Element_MouseLeftButtonUp
      Next uiEle
End Sub
 
Private Sub Element_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
      If isDragDropInEffect Then
            Dim currEle As FrameworkElement = TryCast(sender, FrameworkElement)
            ' Retrieving the item's current x and y position
            Dim xPos As Double = e.GetPosition(Nothing).X - pos.X
            Dim yPos As Double = e.GetPosition(Nothing).Y - pos.Y
 
            ' Re-position Element
            currEle.SetValue(Canvas.TopProperty, yPos + CDbl(currEle.GetValue(Canvas.TopProperty)))
            currEle.SetValue(Canvas.LeftProperty, xPos + CDbl(currEle.GetValue(Canvas.LeftProperty)))
 
            ' Reset the new position value
            pos = e.GetPosition(Nothing)
      End If
End Sub
 
 
Private Sub Element_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
      Dim fEle As FrameworkElement = TryCast(sender, FrameworkElement)
      isDragDropInEffect = True
      ' x and y coords of mouse pointer position
      pos = e.GetPosition(Nothing)
      ' Enable mouse capture on element
      fEle.CaptureMouse()
      ' Set the cursor to 'Hand' when mouse pointer is over element
      fEle.Cursor = Cursors.Hand
End Sub
 
 
Private Sub Element_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
      If isDragDropInEffect Then
            Dim ele As FrameworkElement = TryCast(sender, FrameworkElement)
            isDragDropInEffect = False
            ' Removes Mouse Capture from Element being dragged
            ele.ReleaseMouseCapture()
      End If
End Sub
 
End Class
 
In the code above, we start the drag-drop operation by handling the MouseLeftButtonDown event. We capture the position of the mouse and also set the ‘isDragDropInEffect’ variable to true, indicating that the drag-drop operation has begun. The ‘CaptureMouse()’ method enables the element to capture the mouse input.
The MouseMove checks if the drag-drop operation is on. If it is, then the element is repositioned based on the difference in values of its earlier mouse position and the new position. All we are doing is adjusting the Canvas.Left and Canvas.Top properties inside the MouseMove event handler. The calculation determines how much the mouse has moved from the last MouseMove event.
Finally in the MouseLeftButtonUp, the drag-drop is terminated and the element is released of access to any mouse events by calling the ‘ReleaseMouseCapture’.
Note: Silverlight 2, due to its Sandbox security model does not allow access to Clipboard, hence the drag-drop does not rely on the Clipboard. It is done by the plug-in.
Step 5: Run the application. You will observe that you are able to move the controls around the plug-in.
Hey wait! I cannot move the Button control
Now there is an article by Jesse Liberty Who Ate My Mouse Down Events that explains this behavior where he mentions that in Beta 2, the Button control (and other similar controls) eats the mousedown event. The button control cannot be dragged due to a change in routed events in Beta 2.
Now a workaround to this would be to wrap the button in a Canvas control, where the canvas control would be slightly larger than the button control.
        <Canvas x:Name="btnCanvas" VerticalAlignment="Center" HorizontalAlignment="Center" Canvas.Left="65" Width="70" Height="70" Background="LightCoral">
                <Button x:Name="Btn1" Width="50" Height="50" Content="Button">                   
                </Button>
        </Canvas>
You can then drag the canvas, which will in turn drag the button too. Observe that the background of the Canvas is the same as that of the Root background. When you run the application, just place the mouse cursor below the button, where it changes into a ‘hand’ like cursor. You can now drag the button.
Another method, a cleaner one, as described by sladapter is to create a ‘DragDropPanel’ as shown over here. This Panel makes any control ‘draggable’ if the control is placed within the panel. I would suggest you to try out his control.
That was pretty much all about dragging and dropping controls in Silverlight 2. In the RTM release of Silverlight 2, I hope the approach for drag drop operations will become much more consistent to implement. I hope this article was useful and I thank you for viewing this article.
 If you liked the article,  Subscribe to my RSS Feed.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for ten consecutive times. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.

Get in touch with him on Twitter @suprotimagarwal or at LinkedIn



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by pepa on Tuesday, April 21, 2009 4:10 AM
Great work, thanks
Comment posted by z on Monday, May 18, 2009 3:58 AM
e.GetPoition not accessible
Comment posted by Satish Verma on Wednesday, November 4, 2009 2:34 AM
How to Resize control using Mouse in Silverlight
Comment posted by Mattan on Wednesday, June 16, 2010 5:02 AM
An easier way to do the event handling may be like this:

        void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (DragDropping)
            {
                FrameworkElement element = (FrameworkElement)sender;
                element.ReleaseMouseCapture();
                element.Cursor = Cursors.Arrow;
                DragDropping = false;
            }
        }

        void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)sender;
            element.CaptureMouse();
            element.Cursor = Cursors.Hand;
            AddPos = new Point(e.GetPosition(element).X, e.GetPosition(element).Y);
            DragDropping = true;
        }

        void Element_MouseMove(object sender, MouseEventArgs e)
        {
            if (DragDropping)
            {
                FrameworkElement element = (FrameworkElement)sender;
                element.SetValue(Canvas.LeftProperty, e.GetPosition(Canvas).X - AddPos.X);
                element.SetValue(Canvas.TopProperty, e.GetPosition(Canvas).Y - AddPos.Y);
            }
        }
Comment posted by Josh Watson on Wednesday, August 4, 2010 7:08 PM
A note about button dragging:

You can enable button dragging in Silverlight by extending the button control and overriding the OnMouseLeftButtonDown and OnMouseLeftButtonUp methods:

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);

            e.Handled = false;
        }

        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);

            e.Handled = false;
        }

This lets you drag the button using the methods outlined in this article.  I find this to be a better solution than putting it on a canvas and dragging the canvas because you can actually target the button instead of the canvas it is sitting on.
Comment posted by Musman on Wednesday, February 29, 2012 1:56 PM
@Autohor, good article but code doesnt work

@Mattan, Man your code works like a charm..
Comment posted by Vikram on Monday, June 11, 2012 6:08 AM
Hi, You're code work excellent, I have some clarification on this code, how do we move all the control inside the canvas control. I have two rectangle one inside another wanted the move the master rectangle with the rectange inside it