Как справиться с перетаскиванием без нарушения принципов MVVM?

В настоящее время у меня в XAML

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

Весь мой код перетаскивания существует в кодовом коде моего представления, а не в моей модели ViewModel.

Как я могу обрабатывать перетаскивание в моей ViewModel без добавления каких-либо зависимостей в представлении?

Ответ 1

Для этого есть библиотеки, такие как gong и подобные фрагменты в различных статьях блога.

Однако вы не должны слишком зависеть от того, что у вас абсолютно нет кода. Например, это все еще MVVM в моей книге:

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

Связывание команд может быть лучшим выбором, но логика определенно находится в модели viewmodel. Что-то вроде Drag and Drop, это более переменная, где вы хотите нарисовать линию. При необходимости вы можете интерпретировать код Drag Args и методы вызова на viewmodel, если это необходимо.

Ответ 2

Вот код, который я написал, который позволяет перетаскивать файлы на элемент управления, не нарушая MVVM. Его можно легко изменить, чтобы передать фактический объект вместо файла.

/// <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");
        }
    }
}

Использование:

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

Убедитесь, что DataContext наследуется от IFileDragDropTarget и реализует OnFileDrop.

public class MyDataContext : ViewModelBase, IFileDragDropTarget
{
    public void OnFileDrop(string[] filepaths)
    {
        //handle file drop in data context
    }
}

Ответ 3

Это может также помочь вам. Приложенная библиотека поведения команд позволяет вам преобразовывать любые события (события) в команду, которая будет более тесно соответствовать структуре MVVM.

http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/

Использование этого чрезвычайно просто. И сохранил мой бекон много раз.

Надеюсь, что это поможет

Ответ 4

Это просто дополнительный ответ, который портирует ответ @Asheh на VB.NET для разработчиков VB.

Imports System.Windows

Interface IFileDragDropTarget

    Sub OnFileDrop(ByVal filepaths As String())

End Interface

Public Class FileDragDropHelper

    Public Shared Function GetIsFileDragDropEnabled(ByVal obj As DependencyObject) As Boolean
        Return CBool(obj.GetValue(IsFileDragDropEnabledProperty))
    End Function

    Public Shared Sub SetIsFileDragDropEnabled(ByVal obj As DependencyObject, ByVal value As Boolean)
        obj.SetValue(IsFileDragDropEnabledProperty, value)
    End Sub

    Public Shared Function GetFileDragDropTarget(ByVal obj As DependencyObject) As Boolean
        Return CBool(obj.GetValue(FileDragDropTargetProperty))
    End Function

    Public Shared Sub SetFileDragDropTarget(ByVal obj As DependencyObject, ByVal value As Boolean)
        obj.SetValue(FileDragDropTargetProperty, value)
    End Sub

    Public Shared ReadOnly IsFileDragDropEnabledProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsFileDragDropEnabled", GetType(Boolean), GetType(FileDragDropHelper), New PropertyMetadata(AddressOf OnFileDragDropEnabled))

    Public Shared ReadOnly FileDragDropTargetProperty As DependencyProperty = DependencyProperty.RegisterAttached("FileDragDropTarget", GetType(Object), GetType(FileDragDropHelper), Nothing)

    Shared WithEvents control As Windows.Controls.Control
    Private Shared Sub OnFileDragDropEnabled(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If e.NewValue = e.OldValue Then Return
        control = TryCast(d, Windows.Controls.Control)
        If control IsNot Nothing Then
            AddHandler control.Drop, AddressOf OnDrop
        End If
    End Sub

    Private Shared Sub OnDrop(ByVal _sender As Object, ByVal _dragEventArgs As DragEventArgs)
        Dim d As DependencyObject = TryCast(_sender, DependencyObject)
        If d Is Nothing Then Return
        Dim target As Object = d.GetValue(FileDragDropTargetProperty)
        Dim fileTarget As IFileDragDropTarget = TryCast(target, IFileDragDropTarget)
        If fileTarget IsNot Nothing Then
            If _dragEventArgs.Data.GetDataPresent(DataFormats.FileDrop) Then
                fileTarget.OnFileDrop(CType(_dragEventArgs.Data.GetData(DataFormats.FileDrop), String()))
            End If
        Else
            Throw New Exception("FileDragDropTarget object must be of type IFileDragDropTarget")
        End If
    End Sub
End Class