(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .
        Login with Facebook

Silverlight Drag, Drop, Import and Export

(2 votes)
Michael Washington
>
Michael Washington
Joined Jul 21, 2008
Articles:   1
Comments:   0
More Articles
8 comments   /   posted on Jul 21, 2008
Categories:   Line-of-Business , General

Note: This article is submitted by Michael Washington for Silverlight Contest: Write and Win.Thanks a lot, Michael! Hello All, Please drop a comment if you like it.

This article is compatible with the latest version of Silverlight.

The example is based on an earlier drag and drop code sample that was based on an example created by Keith Mahoney. In his example he shows how to drag and drop various types of elements into various types of Panels. In this example we only cover dragging and dropping an element onto a Canvas. We also cover importing and exporting content from one user control into another.

View Sample | Download Source Code

When you view the sample you see three movable windows (you grab the colored bar on the top of the window to drag it) with three boxes in the first window.

The "Drag Me" box can only be dragged inside the window, but the blank box and the "Drag Me Outside" box can be dragged anywhere on the screen.

To determine the different behavior of the boxes, the word "[draggable]" is added to the Tag property of the element that will be draggable outside the window. When an element with that Tag is detected, the element is removed from the Canvas it is currently on and placed on the root Canvas of the application while it is being dragged.

When the element is dropped a HitTest is performed to see if the mouse is currently in the bounds of a Canvas element that is in one of the windows. If it is in the bounds, the element is removed from the root Canvas and placed onto the Canvas that is in the respective window.

If the boxes are dragged inside another window, they will remain in the window and move with that window when that window is moved.

The window labeled "Drop Items Here" implements a "ImportContent" interface that allows content to be possibly imported and parsed rather than simply added to the window. When the blank box is dragged onto the window labeled "Drop Items Here", the box is simply added to the window.

However, when the box labeled "Drag Me Outside and Export Me" is dragged onto the window labeled "Drop Items Here", the TextBlocks in the box are parsed and their content is added to the TextBlock in the window.

The Code

The solution file contains a Silverlight project and a web project to display the Silverlight project output. The following describes the files in the SilverlightDragAndDrop project:

  • Page.xaml - This is the main control that is displayed by the web project. This control creates windows using the SilverlightWindowControl control and inserts the contents of the SilverlightCanvas controls. It also create handlers to handle the drag and drop of the elements and calls the ImportContent method if it exists on the panel an element is being dropped on.
  • SilverlightCanvas1.xaml  -  This control implements an ImportContent interface and a TextBox to display any imported content.
  • SilverlightCanvas2.xaml  -  This control simply contains a blank Canvas that elements can be placed on.
  • SilverlightObjects.xaml  -  This control contains three elements that can be dragged and dropped. 
  • SilverlightWindowControl.xaml  - This control is used by the Page.xaml control to create the windows the other controls are placed into.
  • Interfaces.cs - This contains an interface called ImportContent that the SilverlightCanvas1.xaml control implements to import content.

Creating the Windows

The Page.xaml control creates the three windows and a list of panels that elements can possibly be dragged onto. It will perform a HitTest to determine if an element is being dragged onto one of the panels.

 public Page()
 {
     InitializeComponent();
  
     // Window #1 - Contains objects
     SilverlightWindowControl objSilverlightWindowControl = new SilverlightWindowControl(50);
     // Pass an instance of this control to the child control. This will allow the child to 
     // wire-up mouse events to the parent. 
     SilverlightObjects SilverlightObjects = new SilverlightObjects(this);
      objSilverlightWindowControl.Window.Content = SilverlightObjects;
      Canvas.SetLeft(objSilverlightWindowControl, (double)50);
      Canvas.SetTop(objSilverlightWindowControl, (double)50);
   
      // SilverlightCanvas1 - Implements the "ImportContent" Interface
      SilverlightWindowControl objBlankSilverlightWindowControl1 = new SilverlightWindowControl(60);
      SilverlightCanvas1 objSilverlightCanvas1 = new SilverlightCanvas1();
      objBlankSilverlightWindowControl1.Window.Content = objSilverlightCanvas1;
      Canvas.SetLeft(objBlankSilverlightWindowControl1, (double)300);
     Canvas.SetTop(objBlankSilverlightWindowControl1, (double)50);
   
      // SilverlightCanvas2 - Does NOT implement the "ImportContent" Interface
      SilverlightWindowControl objBlankSilverlightWindowControl2 = new SilverlightWindowControl(60);
      SilverlightCanvas2 objSilverlightCanvas2 = new SilverlightCanvas2();
      objBlankSilverlightWindowControl2.Window.Content = objSilverlightCanvas2;
      Canvas.SetLeft(objBlankSilverlightWindowControl2, (double)300);
      Canvas.SetTop(objBlankSilverlightWindowControl2, (double)310);
   
      // Add Canvases to a collection that will be checked as possible drop points
      colPanels.Add(SilverlightObjects.LayoutRoot);
      colPanels.Add(objSilverlightCanvas1.LayoutRoot);
      colPanels.Add(objSilverlightCanvas2.LayoutRoot);
   
      // Add Windows to the Page
      this.LayoutRoot.Children.Add(objSilverlightWindowControl);
      this.LayoutRoot.Children.Add(objBlankSilverlightWindowControl1);
      this.LayoutRoot.Children.Add(objBlankSilverlightWindowControl2);
  }

The SilverlightObjects.xaml control uses an instance of the parent control to wire-up the mouse events to detect when an element is being dragged.

 public SilverlightObjects(Page objPage)
 {
     InitializeComponent();
  
     // Add delegates to the parent to allow it to handle the drag and drop
     FrameworkElement objFrameworkElement1 = (FrameworkElement)this.DragElement1;
     objFrameworkElement1.MouseLeftButtonDown += 
         new MouseButtonEventHandler(objPage.objFrameworkElement_MouseLeftButtonDown);
     FrameworkElement objFrameworkElement2 = (FrameworkElement)this.DragElement2;
      objFrameworkElement2.MouseLeftButtonDown += 
          new MouseButtonEventHandler(objPage.objFrameworkElement_MouseLeftButtonDown);
      FrameworkElement objFrameworkElement3 = (FrameworkElement)this.DragElement3;
      objFrameworkElement3.MouseLeftButtonDown += 
          new MouseButtonEventHandler(objPage.objFrameworkElement_MouseLeftButtonDown);
  }

Note, the only way to get an instance of the parent control when a control has been dynamically added is to pass an instance of that parent to the control when the control is added. The mouse events must be attached to, and handled by, the parent not the child control for the communication between windows to work.

When the mouse starts to drag an element, the element is checked to see if it's tag contains "[draggable]". If it does, the element is removed from the Canvas it is on and placed on the main Canvas.

 void objFrameworkElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
     //Start Drag
     FrameworkElement objFrameworkElement = (FrameworkElement)sender;
     objFrameworkElement.CaptureMouse();
  
     // Set the starting point for the drag
     StartingDragPoint = e.GetPosition(objFrameworkElement);
  
     // Remove the element from it's control and move it to the parent
      // if it's Tag contains the words [draggable]
      if (objFrameworkElement.Tag.ToString().Contains("[draggable]"))
      {
          Panel objParent = objFrameworkElement.Parent as Panel;
          objParent.Children.Remove(objFrameworkElement);
   
          this.LayoutRoot.Children.Add(objFrameworkElement);
          MoveToTop(objFrameworkElement);
          UpdateElementPosition(objFrameworkElement, e.GetPosition(this.LayoutRoot));
      }
   
      objFrameworkElement.MouseMove += new MouseEventHandler(objFrameworkElement_MouseMove);
      objFrameworkElement.MouseLeftButtonUp += new MouseButtonEventHandler(objFrameworkElement_MouseLeftButtonUp);
  }

The code to move an element being dragged is the same whether or not the element has been moved to the main Canvas or not.

 #region MouseMove
 void objFrameworkElement_MouseMove(object sender, MouseEventArgs e)
 {
     FrameworkElement objFrameworkElement = (FrameworkElement)sender;
     Canvas objCanvas = (Canvas)objFrameworkElement.Parent;
     Point Point = e.GetPosition(objCanvas);
  
     UpdateElementPosition(objFrameworkElement, Point);
 }
  #endregion

When an element is dropped, this code is used to determine if the element will simply be placed inside a control, or if the ImportContent method will be called:

 #region MouseLeftButtonUp
 void objFrameworkElement_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
 {
     //Stop Drag
     FrameworkElement objFrameworkElement = (FrameworkElement)sender;
     objFrameworkElement.ReleaseMouseCapture();
  
     objFrameworkElement.MouseMove -= 
         new MouseEventHandler(objFrameworkElement_MouseMove);
     objFrameworkElement.MouseLeftButtonUp -= 
          new MouseButtonEventHandler(objFrameworkElement_MouseLeftButtonUp);
   
      // If it is an element marked [draggable]
      // try to drop it on a panel stored in the colPanels collection
      if (objFrameworkElement.Tag.ToString().Contains("[draggable]"))
      {
          Point tmpPoint = e.GetPosition(null);
          // Build a list of elements at the current mouse position
          List<UIElement> hits = VisualTreeHelper.FindElementsInHostCoordinates( tmpPoint, this ) as List<UIElement>;
          // Loop through all the Panels in the colPanels collection
          foreach(Panel objPanel in colPanels)
          {
              if (hits.Contains(objPanel))
              {
                  // Grab the position of the element being dragged in relation to it's position on the 
                  // main canvas and its position in relation to the panel it may be dropped on
                  Point mousePos1 = e.GetPosition(objPanel);
                  Point mousePos2 = e.GetPosition(objFrameworkElement);
   
                  // Remove the element from the main canvas
                  this.LayoutRoot.Children.Remove(objFrameworkElement);
   
                  // Import content
                  // Get a reference to the parent of the current panel
                  UserControl objUserControl = (UserControl)objPanel.Parent;
                  // See if that parent implements an interface called "ImportContent"
                  object objObject = objUserControl.GetType().GetInterface("ImportContent", true);
   
                  // If the object is not null then the parent object has a method called "ImportContent"
                  if (!(objObject == null))
                  {
                      // Create a parmeters array
                      object[] parameters = new object[1];
                      // Add the elemnt that is being dragged to the array
                      parameters.SetValue(objFrameworkElement, 0);
                      // invoke the "ImportContent" on the parent object passing the parameters array that 
                      // contains the element being dragged
                      bool boolImport = (bool)objUserControl.GetType().InvokeMember("ImportContent", 
                          BindingFlags.InvokeMethod, null, objUserControl, parameters);
   
                      // If the import was not successful simply add the element to the panel
                      if (!boolImport)
                      {
                          // Add the element to the panel
                          objPanel.Children.Add(objFrameworkElement);
                          Canvas.SetLeft(objFrameworkElement, mousePos1.X - mousePos2.X);
                          Canvas.SetTop(objFrameworkElement, mousePos1.Y - mousePos2.Y);
                      }
                  }
                  else
                  {
                      // The parent object does not implement the "ImportContent" Interface
                      // Add the element to the panel
                      objPanel.Children.Add(objFrameworkElement);
                      Canvas.SetLeft(objFrameworkElement, mousePos1.X - mousePos2.X);
                      Canvas.SetTop(objFrameworkElement, mousePos1.Y - mousePos2.Y);
                  }
                  break;
              }
          }
      }
  }
  #endregion

The ImportContent Method

Each control determines what content it will import. The control must implement an interface called ImportContent that accepts a FrameworkElement as a single parameter and returns a bool:

 

 interface ImportContent
 {
     bool ImportContent(FrameworkElement objFrameworkElement);
 }

In this example the SilverlightCanvas1.xaml control is coded to only import text from any TextBlocks.

 #region ImportContent
 public bool ImportContent(FrameworkElement objFrameworkElement)
 {
     // This import method will only import text content contained
     // in TextBlocks that are placed on a Canvas
  
     StringBuilder StringBuilder = new StringBuilder();
     Canvas objCanvas = objFrameworkElement as Canvas;
  
     // If the element being imported is not a canvas return false
      if (objCanvas == null)
      {
          return false;
      }
   
      try
      {
          // Loop through all the UIElements in the Canvas 
          foreach (UIElement objUIElement in objCanvas.Children)
          {
              // Try to cast the UIElement as a TextBlock
              TextBlock objTextBlock = objUIElement as TextBlock;
              if (objTextBlock != null)
              {
                  // Add the contents of the TextBlock to the output
                  StringBuilder.Append(String.Format(" {0}", objTextBlock.Text));
              }
          }
      }
      catch
      {
          return false;
      }
   
     // Was content in the output ?
      if (StringBuilder.Length > 0)
      {
          // Add output to the Textbox display
          this.txtContent.Text = StringBuilder.ToString();
          return true;
      }
      else
      {
          return false;
      }
  } 
  #endregion

Summary

This example demonstrates a method for dealing with the "unknown". With SilverlightDesktop, modules can be created by different developers that have no knowledge of each other, yet using a method such as this, content can be exported and imported between them.


Subscribe

Comments

  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by hlt on Aug 10, 2008 21:08

    Can you please show me an example using listbox as the source and target windows?

  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by RL on Sep 17, 2008 18:15

    Is there a way to import usercontrol on to other user control using xaml on the client side.  I see you are doing it on the server side.

    This tutorial is awesome. 

    Thanks

  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by pawan ashish on 03:39

    i  have downloaded the code but the code does not have a method implemented for HitTest. when i build the solution on my system it gives build error. So can you tell me why the error is coming or am i  missing something

  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by pawan ashish on 03:47

    i  have downloaded the code but the code does not have a method implemented for HitTest. when i build the solution on my system it gives build error. So can you tell me why the error is coming or am i  missing something

  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by Christian on Apr 14, 2009 16:25
    Im getting the same bug .... HitTest error. So can you tell us why the error is coming or am i missing something ?
  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by RoL on 01:49
    Even I am getting the "does not contain a definition for HitTest" error... any update?
  • -_-

    RE: Silverlight Drag, Drop, Import and Export


    posted by Michael Washington on 08:18

    You can see a working version at:

    http://www.adefwebserver.com/DotNetNukeHELP/Misc/Silverlight/DragAndDropTest/Default.aspx

    with source code at:

    http://www.adefwebserver.com/DotNetNukeHELP/Misc/Silverlight/DragAndDropTest/

  • lnikolov

    RE: Silverlight Drag, Drop, Import and Export


    posted by lnikolov on Jan 20, 2011 14:45
    The article has been updated to the latest version of Silverlight and Visual Studio.

Add Comment

Login to comment:
  *      *       
Login with Facebook