You are here: Home » .NET » WPF Drag and Drop in MVVM Pattern

WPF Drag and Drop in MVVM Pattern

WPF Drag and Drop Level two

Goal:

Apply drag and drop feature include in WPF in a more practicle way. Real enterprise application make use of code standard parttern, code encapsulation, code abstraction and porlymophism. All these are meant for better code management and maintainability, and this is what we’ll try to do for Drag and Drop feature.

Please follow the first article about basic drag and drop feature.

    

1) Abstract Drag and Drop function into its own classes  

2) Make use of MVVM parttern

2) Make use of Drag and Drop through the use of:

                   1) Attach Property

                2) Delegation of actions to ViewModel       

Assumption:

                   1 ) Window Presentation Foundation (WPF)

2) Familiar with MVVM pattern for Silverlight/WPF

3) Know or Read C#

4) Understand the Drag and Drop     basic from the first article

Implementation:

1) Model Interface

 – Create one common interface need to be shared among all models being used in the drag

  

public interface IDragDropModel

   {

    string DD_Id { get; set; }

    object DD_Value { get; set; }

    string DD_ColumnName { get; set; }

    string DD_Desc { get; set; }

    bool DD_IsColumnDrag { get; set; }

   }

   

2) ViewModel interface

 – Create a ViewModel interface that include common drag drop required actions items and properties

  

public interface IDragDropViewModel

   {

    Grid DD_DragCreateAdonerGrid(IDragDropModel dragItem);

    Grid DD_DragCreateAdonerGridColumn(string columnName);

    DragDataObject DD_CreateDragDataObject(IDragDropModel dragItem);

    void DD_DropData(object dropItem);

    bool DD_IsDragByColumn { get; set; }

    string DD_ColumnName { get; set; }

   }

  Method Description:

   1) DD_DragCreateAdonerGrid : create dragging grid template for row dragging

   2) DD_DragCreateAdonerGridColumn : create dragging grid template for column dragging

   3) DD_CreateDragDataObject : create the DragDataObject that include both the drag data and the drag adorner template

   4) DD_DropData : accept the drop data

   5) DD_IsDragByColumn : flag that tell viewmodel to prepare drag template accordingly to column or row

   6) DD_ColumnName : drag column name if the drag is dragged by column instead of row

   

3) Create DragDropManager class

 – This class will handle all drag and drop activity, and it will be attached to any control that need drag drop implementation

 – It will served as Attached Property for UI control

 – DropDropManager main activies:

  1) Accept source object

   – will cover next

  2) Accept target object

   – will cover next

  3) Accept UI ViewModel object which is implemented with IDragDropViewModel

  4) Track all all drag and drop activities on events for the host control such as below:

  

 targetElt.PreviewDragEnter += DropTarget_PreviewDragEnter;

    targetElt.PreviewDragOver += DropTarget_PreviewDragOver;

    targetElt.PreviewDragLeave += DropTarget_PreviewDragLeave;

    targetElt.PreviewDrop += DropTarget_PreviewDrop;

  5) Delegate Drag Adorner creation to host ViewModel

static void DragStarted(UIElement uiElt, UIElement uiEltOriginaSource)

     {

      _isMouseDown = false;

      Mouse.Capture(uiElt);

      DragSourceBase advisor = GetDragSource(uiElt as DependencyObject);

      IDragDropViewModel viewModel = GetViewModel(uiElt as DependencyObject);

      if (uiEltOriginaSource.GetType() == typeof(GridViewColumnHeader))

      {

       var theDependencyObject = uiEltOriginaSource as DependencyObject;

       List<Button> theList = theDependencyObject.FindVisualChildren<Button>().ToList();

       viewModel.DD_IsDragByColumn = true;

       var thisHeader = uiEltOriginaSource as GridViewColumnHeader;

       if (thisHeader.Column.Header == null && theList.Count > 0)

        viewModel.DD_ColumnName = theList.FirstOrDefault().Content.ToString();

       else

        viewModel.DD_ColumnName = thisHeader.Column.Header.ToString();

      }

      else if (uiEltOriginaSource.GetType() == typeof(Button))

      {

       viewModel.DD_IsDragByColumn = true;

       var theButton = uiEltOriginaSource as Button;

       viewModel.DD_ColumnName = theButton.Content.ToString();

      }

      else

       viewModel.DD_IsDragByColumn = false;

      //Get dataobject for draging

      DataObject data = advisor.GetDataObject(_dragUIElement, viewModel);

      data.SetData("OffsetPoint", _offsetPoint);

      DragDropEffects supportedEffects = advisor.SupportedEffects;

      // Perform DragDrop

      DragDropEffects effects = System.Windows.DragDrop.DoDragDrop(_dragUIElement, data, supportedEffects);

      advisor.FinishDrag(_dragUIElement, effects);

      // Clean up

      RemovePreviewAdorner(uiElt);

      Mouse.Capture(null);

      _dragUIElement = null;

     }

    

  6) Delegate Drop data handling to host ViewModel

    

static void DropTarget_PreviewDrop(object sender, DragEventArgs e)

     {

      if (UpdateEffects(sender, e) == false) return;

      DropTargetBase advisor = GetDropTarget(sender as DependencyObject);

      Point dropPoint = e.GetPosition(sender as UIElement);

      // Calculate displacement for (Left, Top)

      Point offset = e.GetPosition(_theAdorner);

      dropPoint.X = dropPoint.X - offset.X;

      dropPoint.Y = dropPoint.Y - offset.Y;

      IDragDropViewModel viewModel = GetViewModel(sender as DependencyObject);

      advisor.OnDropCompleted(e.Data, dropPoint, viewModel);

      //advisor.OnDropCompleted(e.Data, dropPoint);

      RemovePreviewAdorner(sender as UIElement);

      _offsetPoint = new Point(0, 0);

     }

      Full Sample of datamanager class:

        

     public static class DragDropManager

     {

      private static UIElement _dragUIElement;

      private static bool _isMouseDown = false;

      private static Point _dragStartPoint;

      private static Point _offsetPoint;

      private static DragDropAdorner _theAdorner;

      #region Property Change handlers

      private static void OnDragSourceChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs args)

      {

       UIElement sourceElt = depObj as UIElement;

       if (args.NewValue != null && args.OldValue == null)

       {

        sourceElt.PreviewMouseLeftButtonDown += DragSource_PreviewMouseLeftButtonDown;

        sourceElt.PreviewMouseMove += DragSource_PreviewMouseMove;

        sourceElt.PreviewMouseUp += DragSource_PreviewMouseUp;

        // Set the Drag source UI

        DragSourceBase advisor = args.NewValue as DragSourceBase;

        advisor.SourceUI = sourceElt;

       }

       else if (args.NewValue == null && args.OldValue != null)

       {

        sourceElt.PreviewMouseLeftButtonDown -= DragSource_PreviewMouseLeftButtonDown;

        sourceElt.PreviewMouseMove -= DragSource_PreviewMouseMove;

        sourceElt.PreviewMouseUp -= DragSource_PreviewMouseUp;

       }

      }

      static void DragSource_PreviewMouseUp(object sender, MouseButtonEventArgs e)

      {

       _isMouseDown = false;

       Mouse.Capture(null);

      }

      private static void OnDropTargetChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs args)

      {

       UIElement targetElt = depObj as UIElement;

       if (args.NewValue != null && args.OldValue == null)

       {

        targetElt.PreviewDragEnter += DropTarget_PreviewDragEnter;

        targetElt.PreviewDragOver += DropTarget_PreviewDragOver;

        targetElt.PreviewDragLeave += DropTarget_PreviewDragLeave;

        targetElt.PreviewDrop += DropTarget_PreviewDrop;

        targetElt.AllowDrop = true;

        // Set the Drag source UI

        DropTargetBase advisor = args.NewValue as DropTargetBase;

        advisor.TargetUI = targetElt;

       }

       else if (args.NewValue == null && args.OldValue != null)

       {

        targetElt.PreviewDragEnter -= DropTarget_PreviewDragEnter;

        targetElt.PreviewDragOver -= DropTarget_PreviewDragOver;

        targetElt.PreviewDragLeave -= DropTarget_PreviewDragLeave;

        targetElt.PreviewDrop -= DropTarget_PreviewDrop;

        targetElt.AllowDrop = false;

       }

      }

      private static void OnViewModelChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs args)

      {

       //

      }

      #endregion

      /* ____________________________________________________________________

       *  Drop Target events

       * ____________________________________________________________________

       */

      static void DropTarget_PreviewDrop(object sender, DragEventArgs e)

      {

       if (UpdateEffects(sender, e) == false) return;

       DropTargetBase advisor = GetDropTarget(sender as DependencyObject);

       Point dropPoint = e.GetPosition(sender as UIElement);

       // Calculate displacement for (Left, Top)

       Point offset = e.GetPosition(_theAdorner);

       dropPoint.X = dropPoint.X - offset.X;

       dropPoint.Y = dropPoint.Y - offset.Y;

       //Target run onDrop Complete

       IDragDropViewModel viewModel = GetViewModel(sender as DependencyObject);

       advisor.OnDropCompleted(e.Data, dropPoint, viewModel);

       RemovePreviewAdorner(sender as UIElement);

       _offsetPoint = new Point(0, 0);

      }

      static void DropTarget_PreviewDragLeave(object sender, DragEventArgs e)

      {

       if (UpdateEffects(sender, e) == false) return;

       DropTargetBase advisor = GetDropTarget(sender as DependencyObject);

       Point mousePoint = MouseUtilities.GetMousePosition(advisor.TargetUI);

       //Console.WriteLine("Inside DropTarget_PreviewDragLeave1" + mousePoint.X.ToString() + "|" + mousePoint.Y.ToString());

       //giving a tolerance of 2 so that the adorner is removed when the mouse is moved fast.

       //this might still be small...in that case increase the tolerance

       if ((mousePoint.X < 2) || (mousePoint.Y < 2)||

        (mousePoint.X > ((FrameworkElement)(advisor.TargetUI)).ActualWidth - 2) ||

        (mousePoint.Y > ((FrameworkElement)(advisor.TargetUI)).ActualHeight - 2))

       {

        RemovePreviewAdorner(sender as UIElement);

       }

       e.Handled = true;

      }

      static void DropTarget_PreviewDragOver(object sender, DragEventArgs e)

      {

       if (UpdateEffects(sender, e) == false) return;

       // Update position of the preview Adorner

       Point position = e.GetPosition(sender as UIElement);           

       _theAdorner.Left = position.X - 20; //_offsetPoint.X;

       _theAdorner.Top = position.Y - 10;// -_offsetPoint.Y;

       App.DragDropCom.MousePosition = position;         

       e.Handled = true;

      }

      static void DropTarget_PreviewDragEnter(object sender, DragEventArgs e)

      {

       if (UpdateEffects(sender, e) == false) return;

       // Setup the preview Adorner

       UIElement feedbackUI = GetDropTarget(sender as DependencyObject).GetVisualFeedback(e.Data);

       _offsetPoint = GetOffsetPoint(e.Data);

       App.DragDropCom.AdornerPosition = new Point() { X = _offsetPoint.X, Y = _offsetPoint.Y };

       DropTargetBase advisor = GetDropTarget(sender as DependencyObject);

       Point mousePoint = MouseUtilities.GetMousePosition(advisor.TargetUI);

         // Console.WriteLine("Inside DropTarget_PreviewDragEnter" + mousePoint.X.ToString() + "|" + mousePoint.Y.ToString());

       //giving a tolerance of 2 so that the adorner is created when the mouse is moved fast.

       //this might still be small...in that case increase the tolerance

       if ((mousePoint.X < 2) || (mousePoint.Y < 2) ||

        (mousePoint.X > ((FrameworkElement)(advisor.TargetUI)).ActualWidth - 2) ||

        (mousePoint.Y > ((FrameworkElement)(advisor.TargetUI)).ActualHeight - 2) ||

         (_theAdorner == null))

       {

        CreatePreviewAdorner(sender as UIElement, feedbackUI);

       }

       e.Handled = true;

      }

      static Point GetOffsetPoint(IDataObject obj)

      {

       Point p = (Point)obj.GetData("OffsetPoint");

       return p;

      }

      static bool UpdateEffects(object uiObject, DragEventArgs e)

      {

       DropTargetBase advisor = GetDropTarget(uiObject as DependencyObject);

       if (advisor.IsValidDataObject(e.Data) == false) return false;

       if ((e.AllowedEffects & DragDropEffects.Move) == 0 &&

        (e.AllowedEffects & DragDropEffects.Copy) == 0)

       {

        e.Effects = DragDropEffects.None;

        return true;

       }

       if ((e.AllowedEffects & DragDropEffects.Move) != 0 &&

        (e.AllowedEffects & DragDropEffects.Copy) != 0)

       {

        if ((e.KeyStates & DragDropKeyStates.ControlKey) != 0)

        {

        }

        e.Effects = ((e.KeyStates & DragDropKeyStates.ControlKey) != 0) ?

         DragDropEffects.Copy : DragDropEffects.Move;

       }

       return true;

      }

      /* ____________________________________________________________________

       *  Drag Source events

       * ____________________________________________________________________

       */

      static void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

      {

       // Make this the new drag source

       DragSourceBase advisor = GetDragSource(sender as DependencyObject);

       if (advisor.IsDraggable(e.Source as UIElement) == false) return;

       _dragUIElement = e.Source as UIElement;

       _dragStartPoint = e.GetPosition(GetTopContainer());

       _offsetPoint = e.GetPosition(_dragUIElement);

       App.DragDropCom.AdornerPosition = new Point() { X = _offsetPoint.X, Y = _offsetPoint.Y };

       _isMouseDown = true;

      }

      static void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)

      {

       if (_isMouseDown && IsDragGesture(e.GetPosition(GetTopContainer())))

       {

        DragStarted(sender as UIElement, e.OriginalSource as UIElement);

       }

      }

      static void DragStarted(UIElement uiElt, UIElement uiEltOriginaSource)

      {

       _isMouseDown = false;

       Mouse.Capture(uiElt);

       DragSourceBase advisor = GetDragSource(uiElt as DependencyObject);

       IDragDropViewModel viewModel = GetViewModel(uiElt as DependencyObject);

       if (uiEltOriginaSource.GetType() == typeof(GridViewColumnHeader))

       {

        var theDependencyObject = uiEltOriginaSource as DependencyObject;

        List<Button> theList = theDependencyObject.FindVisualChildren<Button>().ToList();

        viewModel.DD_IsDragByColumn = true;

        var thisHeader = uiEltOriginaSource as GridViewColumnHeader;

        if (thisHeader.Column.Header == null && theList.Count > 0)

         viewModel.DD_ColumnName = theList.FirstOrDefault().Content.ToString();

        else

         viewModel.DD_ColumnName = thisHeader.Column.Header.ToString();

       }

       else if (uiEltOriginaSource.GetType() == typeof(Button))

       {

        viewModel.DD_IsDragByColumn = true;

        var theButton = uiEltOriginaSource as Button;

        viewModel.DD_ColumnName = theButton.Content.ToString();

       }

       else

        viewModel.DD_IsDragByColumn = false;

       //Get dataobject for draging

       DataObject data = advisor.GetDataObject(_dragUIElement, viewModel);

       data.SetData("OffsetPoint", _offsetPoint);

       DragDropEffects supportedEffects = advisor.SupportedEffects;

       // Perform DragDrop

       DragDropEffects effects = System.Windows.DragDrop.DoDragDrop(_dragUIElement, data, supportedEffects);

       advisor.FinishDrag(_dragUIElement, effects);

       // Clean up

       RemovePreviewAdorner(uiElt);

       Mouse.Capture(null);

       _dragUIElement = null;

      }

      static bool IsDragGesture(Point point)

      {

       bool hGesture = Math.Abs(point.X - _dragStartPoint.X) > SystemParameters.MinimumHorizontalDragDistance;

       bool vGesture = Math.Abs(point.Y - _dragStartPoint.Y) > SystemParameters.MinimumVerticalDragDistance;

       return (hGesture | vGesture);

      }

      /* ____________________________________________________________________

       *  Utility functions

       * ____________________________________________________________________

       */

      static UIElement GetTopContainer()

      {

         // return  LogicalTreeHelper.FindLogicalNode(Application.Current.MainWindow, "canvas") as UIElement;

       return Application.Current.MainWindow.Content as UIElement;

      }

      private static void CreatePreviewAdorner(UIElement adornedElt, UIElement feedbackUI)

      {

       // Clear if there is an existing preview adorner

       RemovePreviewAdorner(adornedElt);

       //AdornerLayer layer = AdornerLayer.GetAdornerLayer(GetTopContainer());

       AdornerLayer layer = AdornerLayer.GetAdornerLayer(adornedElt);

       _theAdorner = new DragDropAdorner(feedbackUI, adornedElt);

       layer.Add(_theAdorner);

      }

      private static void RemovePreviewAdorner(UIElement theTopContainer)

      {

       if (theTopContainer == null)

        theTopContainer = GetTopContainer();

       if (theTopContainer!=null && _theAdorner != null)

       {

        AdornerLayer.GetAdornerLayer(theTopContainer).Remove(_theAdorner);

        _theAdorner = null;

       }

      }

      #region Dependency Properties

      public static readonly DependencyProperty DragSourceProperty =

        DependencyProperty.RegisterAttached("DragSource", typeof(DragSourceBase), typeof(DragDropManager),

        new FrameworkPropertyMetadata(new PropertyChangedCallback(OnDragSourceChanged)));

      public static DragSourceBase GetDragSource(DependencyObject depObj)

      {

       return depObj.GetValue(DragSourceProperty) as DragSourceBase;

      }

      public static void SetDragSource(DependencyObject depObj, bool isSet)

      {

       depObj.SetValue(DragSourceProperty, isSet);

      }

      public static readonly DependencyProperty DropTargetProperty =

       DependencyProperty.RegisterAttached("DropTarget", typeof(DropTargetBase), typeof(DragDropManager),

       new FrameworkPropertyMetadata(new PropertyChangedCallback(OnDropTargetChanged)));

      public static void SetDropTarget(DependencyObject depObj, bool isSet)

      {

       depObj.SetValue(DropTargetProperty, isSet);

      }

      public static DropTargetBase GetDropTarget(DependencyObject depObj)

      {

       return depObj.GetValue(DropTargetProperty) as DropTargetBase;

      }

      public static readonly DependencyProperty ViewModelProperty =

       DependencyProperty.RegisterAttached("ViewModel", typeof(IDragDropViewModel), typeof(DragDropManager),

       new FrameworkPropertyMetadata(new PropertyChangedCallback(OnViewModelChanged)));

      public static void SetViewModel(DependencyObject depObj, bool isSet)

      {

       depObj.SetValue(ViewModelProperty, isSet);

      }

      public static IDragDropViewModel GetViewModel(DependencyObject depObj)

      {

       return depObj.GetValue(ViewModelProperty) as IDragDropViewModel;

      }

      #endregion

     }

4) Create Drag Source class

  – Since each UI control are different, it’s important to have a customized Drag Source class for each. Though they all should be implemented in similar fashion. This can be done from deriving from a Drag Source Base class below:

   

 public abstract class DragSourceBase

    {

     string supportedDataFormat = "NoneSpeficNow";

     private UIElement _sourceElt;

     public virtual UIElement SourceUI

     {

      get

      {

       return _sourceElt;

      }

      set

      {

       _sourceElt = value;

      }

     }

     public virtual DragDropEffects SupportedEffects

     {

      get { return DragDropEffects.Copy | DragDropEffects.Move; }

     }

     public virtual string SupportedDataFormat

     {

      get { return supportedDataFormat; }

      set { supportedDataFormat = value; }

     }

     public virtual DataObject GetDataObject(UIElement draggedElt)

     {

      string theData = "None";

      string serializedElt = XamlWriter.Save(draggedElt);

      DragDataObject theDragData = new DragDataObject() {

       SerialIzedUIElement = serializedElt,

       DataType = theData.GetType(),

       Data = theData

      };

      DataObject obj = new DataObject();

      obj.SetData(supportedDataFormat, theDragData);

      return obj;

     }

     public virtual DataObject GetDataObject(UIElement draggedElt, IDragDropViewModel theData)

     {

      string serializedElt = XamlWriter.Save(draggedElt);

      DragDataObject theDragData = new DragDataObject()

      {

       SerialIzedUIElement = serializedElt,

       Data = theData,

       DataType = theData.GetType()

      };

      DataObject obj = new DataObject();

      obj.SetData(supportedDataFormat, theDragData);

      return obj;

     }

     public abstract void FinishDrag(UIElement draggedElt, DragDropEffects finalEffects);

     public abstract bool IsDraggable(UIElement dragElt);

    }

  Example of ListView Drag source class that implemented the Drag Source based class as below:

    public class DragSourceListView : DragSourceBase

    {

     public DragSourceListView()

     {

     }

     public override void FinishDrag(UIElement draggedElt, DragDropEffects finalEffects)

     {

      ////this is to remove current item from current window

      //if ((finalEffects & DragDropEffects.Move) == DragDropEffects.Move)

      //{

      //    (SourceUI as ListView).Children.Remove(draggedElt);

      //}

     }

     public override bool IsDraggable(UIElement dragElt)

     {

      return (dragElt is ListView);

     }

     public override DataObject GetDataObject(UIElement draggedElt, IDragDropViewModel viewModel)

     {

      IDragDropViewModel theViewModel = viewModel;

      if (theViewModel != null)

      {

       //Get selected List item and selected item data

       ListView theListView = draggedElt as ListView;

       object theSelectedItem = theListView.SelectedItem;

       DragDataObject theDragData = viewModel.DD_CreateDragDataObject(theSelectedItem as IDragDropModel);

       DataObject obj = new DataObject();

       obj.SetData(SupportedFormat, theDragData);

       return obj;

      }

      else

      {

       DataObject obj = new DataObject();

       //obj.SetData(null, null);

       return obj;

      }

     }

    }

 

5) Create Target Drop class

 – Similar to Source class, since each UI control is different, it required that each UI control has its own Drop Target class, which derived from a based Target class as follow:

    

public abstract class DropTargetBase

     {

      double _opacity = .5;

      string supportedDataFormat = "NoneSpeficNow";

      private UIElement _targetUI;

      public virtual UIElement TargetUI

      {

       get

       {

        return _targetUI;

       }

       set

       {

        _targetUI = value;

       }

      }

      public virtual string SupportedDataFormat

      {

       get { return supportedDataFormat; }

       set { supportedDataFormat = value; }

      }

      public virtual bool IsValidDataObject(IDataObject obj)

      {

       return (obj.GetDataPresent(supportedDataFormat));

      }

      public abstract void OnDropCompleted(IDataObject obj, Point dropPoint, IDragDropViewModel viewModel);

      public virtual UIElement GetVisualFeedback(IDataObject obj)

      {

       var theDragData = this.GetDataObject(obj);

       UIElement elt = ExtractElement(obj);

       Type t = elt.GetType();

       double theWidth = (double)t.GetProperty("Width").GetValue(elt, null);

       if (theWidth.Equals(double.NaN) || theWidth < 0)

        theWidth = theDragData.UIElementWidth;

       double theHeight = (double)t.GetProperty("Height").GetValue(elt, null);

       if (theHeight.Equals(double.NaN) || theHeight < 0)

        theHeight = theDragData.UIElementHeight;

       Rectangle rect = new Rectangle();

       rect.Width = theWidth;

       rect.Height = theHeight;

       rect.Fill = new VisualBrush(elt);

       rect.Opacity = this._opacity;

       rect.IsHitTestVisible = false;

       return rect;

      }

      public virtual UIElement ExtractElement(IDataObject obj)

      {

       DragDataObject theData = this.GetDataObject(obj);

       string xamlString = theData.SerialIzedUIElement;

       //string xamlString = obj.GetData(supportedFormat) as string;

       XmlReader reader = XmlReader.Create(new StringReader(xamlString));

       UIElement elt = XamlReader.Load(reader) as UIElement;

       return elt;

      }

      protected DragDataObject GetDataObject(IDataObject obj)

      {

       return obj.GetData(supportedDataFormat) as DragDataObject;

      }

      public virtual object ExtractData(IDataObject obj)

      {

       DragDataObject theData = obj.GetData(supportedDataFormat) as DragDataObject;

       return Convert.ChangeType(theData.Data, theData.DataType);

      }

     }

   Example of ListView Drag target class that implemented the Drag Source based class as below:

     public class DropTargetListView : DropTargetBase

     {

      public DropTargetListView()

      {

      }

      public override void OnDropCompleted(IDataObject obj, Point dropPoint, IDragDropViewModel viewModel)

      {

       var theData = this.ExtractData(obj);

       if (theData != null)

       {

        viewModel.DD_DropData(theData);

       }

      }

     }

     

6) Attach Drag Drop DataManager to UI control from XAML as follow:

   

<ListView x:Name="listViewTest" Grid.Row="1"

         Margin="4"

         Padding="2"

         SelectionMode="Single"

         ItemsSource="{Binding StudentList}"

         ItemContainerStyle="{StaticResource ItemContStyle}"

         local:DragDropManager.DragSource="{StaticResource listViewDragSource1}"

         local:DragDropManager.DropTarget="{StaticResource listViewDropTarget1}"

         local:DragDropManager.ViewModel="{StaticResource WindowViewModel}"

         >

    

   Full sample of a page as below:

   

<Window x:Class="DiTranNet.Window1"

     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

     xmlns:local="clr-namespace:DiTranNet.DragDropManager"

     xmlns:VM="clr-namespace:DiTranNet.ViewModels"

     Title="Window1" Height="300" Width="300">

    <Window.Resources>

     <VM:ViewModelWindow1 x:Key="WindowViewModel"/>

     <local:DragSourceGrid x:Key="sourceForDragOp"/>

     <local:DropTargetGrid x:Key="targetForDragOp"/>

     <local:DragSourceListView x:Key="listViewDragSource1"/>

     <local:DropTargetListView x:Key="listViewDropTarget1"/>

     <Style x:Key="ItemContStyle" TargetType="ListViewItem">

      <Style.Resources>

       <LinearGradientBrush x:Key="MouseOverBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">

        <GradientStop Color="#22000000" Offset="0" />

        <GradientStop Color="#44000000" Offset="0.4" />

        <GradientStop Color="#55000000" Offset="0.6" />

        <GradientStop Color="#33000000" Offset="0.9" />

        <GradientStop Color="#22000000" Offset="1" />

       </LinearGradientBrush>

      </Style.Resources>

      <Setter Property="Padding" Value="0,4" />

      <Setter Property="HorizontalContentAlignment" Value="Stretch" />

      <Setter Property="Border.BorderThickness" Value="0,0,0,0.5" />

      <Setter Property="Border.BorderBrush" Value="LightGray" />

     </Style>

    </Window.Resources>

    <Grid Background="Transparent"

       DataContext="{Binding Source={StaticResource WindowViewModel}}"

     x:Name="grid_diff">

     <Grid.RowDefinitions>

      <RowDefinition Height="40"/>

      <RowDefinition />

     </Grid.RowDefinitions>

     <TextBlock x:Name="textBlock_Status" Text="{Binding DragDropCom.StatusText, Source={x:Static Application.Current}}"/>

     <ListView x:Name="listViewTest" Grid.Row="1"

         Margin="4"

         Padding="2"

         SelectionMode="Single"

         ItemsSource="{Binding StudentList}"

         ItemContainerStyle="{StaticResource ItemContStyle}"

         local:DragDropManager.DragSource="{StaticResource listViewDragSource1}"

         local:DragDropManager.DropTarget="{StaticResource listViewDropTarget1}"

         local:DragDropManager.ViewModel="{StaticResource WindowViewModel}"

         >

      <ListView.View>

       <GridView>

        <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}" Width="80" />

        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="175" />

        <GridViewColumn Header="Desc" DisplayMemberBinding="{Binding Desc}" Width="340" />

       </GridView>

      </ListView.View>

     </ListView>

    </Grid>

   </Window>

   

   

FULL SOURCE CODE FOR SAMPLE PROJECT IS ATTACHED.

Note:

To not reinvent the wheel, this drag and drop framework has been implemented by somebody. We only take it to another level by tie it to MVVM pattern and Action delegation to ViewModel

EXTRA:

– There are time when you want to drop item on a column header of the target, and want to know which column header it is after the drop, you can do so in the event handler for the drop as below:

static void DropTarget_PreviewDrop(object sender, DragEventArgs e)
{
	var parent = sender as UIElement;
	Point dropPoint = e.GetPosition(parent);

    //Find drop column header
    HitTestResult hitTestResult = VisualTreeHelper.HitTest(parent, dropPoint);
    var theHeader = hitTestResult.VisualHit.FindVisualParent<GridViewColumnHeader>(parent);

	//Find name of the header using extension method 
	string HeaderName;
	 if (theHeader != null)
	{
		//Is dropped on header
		HeaderName = theHeader.FindGridViewColumnHeaderName();
	}
	else
	{
		//Is not dropped on header
		HeaderName = "";
	}
}

//Extension method for finding column header name
public static string FindGridViewColumnHeaderName(this GridViewColumnHeader theHeader)
{
	string theName = "";
	if (theHeader != null && theHeader.Column.Header == null)
	{
		var theButtonList = theHeader.FindVisualChildren<Button>().ToList();
		if (theButtonList.Count() > 0)
			theName = theButtonList.FirstOrDefault().Content.ToString();
	}
	else
		theName = theHeader.Column.Header.ToString();
	return theName;
}
//Extension method for finding children of certain type
public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject depObj) where T : DependencyObject
{
	if (depObj != null)
	{
		for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
		{
			DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
			if (child != null && child is T)
			{
				yield return (T)child;
			}

			foreach (T childOfChild in FindVisualChildren<T>(child))
			{
				yield return childOfChild;
			}
		}
	}
}

 

FULL SOURCE CODE

WpfDragDrop2.zip

 

Happy coding…