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.
This article has been editorially reviewed by Suprotim Agarwal.
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!
Was this article worth reading? Share it with fellow developers too. Thanks!
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 Sixteen consecutive years. 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