Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle drag/drop without violating MVVM principals?

Tags:

Currently I have in my XAML

<TabControl       AllowDrop="True"     PreviewDragOver="DragOver"     PreviewDrop="Drop" /> 

All of my drag/drop code exists within the codebehind of my View, rather than within my ViewModel.

How can I handle drag/drop in my ViewModel without adding any dependencies on the View?

like image 689
Night Walker Avatar asked May 06 '11 19:05

Night Walker


2 Answers

There are libraries for this such as gong and similar snippets on various blog articles.

However, you shouldn't get too hung up on having absolutely no code-behind. For example, this is still MVVM in my book:

void ButtonClicked(object sender, EventArgs e) {     ((MyViewModel) this.DataContext).DoSomething(); } 

A command binding might be a better choice, but the logic is definitely in the viewmodel. With something like Drag and Drop, it's more variable where you want to draw the line. You can have code-behind interpret the Drag Args and call methods on the viewmodel when appropriate.

like image 186
default.kramer Avatar answered Sep 19 '22 14:09

default.kramer


Here is some code I wrote that allows you to drag and drop files onto a control without violating MVVM. It could easily be modified to pass the actual object instead of a file.

/// <summary> /// IFileDragDropTarget Interface /// </summary> public interface IFileDragDropTarget {     void OnFileDrop(string[] filepaths); }  /// <summary> /// FileDragDropHelper /// </summary> public class FileDragDropHelper {     public static bool GetIsFileDragDropEnabled(DependencyObject obj)     {         return (bool)obj.GetValue(IsFileDragDropEnabledProperty);     }      public static void SetIsFileDragDropEnabled(DependencyObject obj, bool value)     {         obj.SetValue(IsFileDragDropEnabledProperty, value);     }      public static bool GetFileDragDropTarget(DependencyObject obj)     {         return (bool)obj.GetValue(FileDragDropTargetProperty);     }      public static void SetFileDragDropTarget(DependencyObject obj, bool value)     {         obj.SetValue(FileDragDropTargetProperty, value);     }      public static readonly DependencyProperty IsFileDragDropEnabledProperty =             DependencyProperty.RegisterAttached("IsFileDragDropEnabled", typeof(bool), typeof(FileDragDropHelper), new PropertyMetadata(OnFileDragDropEnabled));      public static readonly DependencyProperty FileDragDropTargetProperty =             DependencyProperty.RegisterAttached("FileDragDropTarget", typeof(object), typeof(FileDragDropHelper), null);      private static void OnFileDragDropEnabled(DependencyObject d, DependencyPropertyChangedEventArgs e)     {         if (e.NewValue == e.OldValue) return;         var control = d as Control;         if (control != null) control.Drop += OnDrop;     }      private static void OnDrop(object _sender, DragEventArgs _dragEventArgs)     {         DependencyObject d = _sender as DependencyObject;         if (d == null) return;         Object target = d.GetValue(FileDragDropTargetProperty);         IFileDragDropTarget fileTarget = target as IFileDragDropTarget;         if (fileTarget != null)         {             if (_dragEventArgs.Data.GetDataPresent(DataFormats.FileDrop))             {                 fileTarget.OnFileDrop((string[])_dragEventArgs.Data.GetData(DataFormats.FileDrop));             }         }         else         {             throw new Exception("FileDragDropTarget object must be of type IFileDragDropTarget");         }     } } 

Usage:

<ScrollViewer AllowDrop="True" Background="Transparent" utility:FileDragDropHelper.IsFileDragDropEnabled="True" utility:FileDragDropHelper.FileDragDropTarget="{Binding}"/> 

Ensure the DataContext inherits from IFileDragDropTarget and implements the OnFileDrop.

public class MyDataContext : ViewModelBase, IFileDragDropTarget {     public void OnFileDrop(string[] filepaths)     {         //handle file drop in data context     } } 
like image 20
Asheh Avatar answered Sep 19 '22 14:09

Asheh