如何在不违反 MVVM 原则的情况下处理拖放?

发布于 2024-11-05 06:33:33 字数 235 浏览 1 评论 0原文

目前,我的 XAML 中

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

所有拖/放代码都存在于视图的代码隐藏中,而不是在 ViewModel 中。

如何在 ViewModel 中处理拖放而不添加对 View 的任何依赖项?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

记忆里有你的影子 2024-11-12 06:33:33

有一些库可以实现此目的,例如 gong 以及各种博客文章中的类似片段。

然而,您不应该太执着于完全没有代码隐藏。例如,这仍然是我书中的 MVVM:

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

命令绑定可能是更好的选择,但逻辑肯定在视图模型中。借助拖放之类的功能,您想要绘制界限的位置变得更加可变。您可以在适当的时候让代码隐藏解释拖动参数并调用视图模型上的方法。

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.

音盲 2024-11-12 06:33:33

下面是我编写的一些代码,允许您将文件拖放到控件上而不违反 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
    }
}

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
    }
}
子栖 2024-11-12 06:33:33

这是一个比 Mustafa 的解决方案更通用、开箱即用、更简单的解决方案,具有单个 DependencyProperty

  1. 在项目中复制此接口
public interface IFilesDropped
{
    void OnFilesDropped(string[] files);
}
  1. 让 ViewModel 实现该接口
public class SomeViewModel : IFilesDropped
{
    public void OnFilesDropped(string[] files)
    {
        // Implement some logic here
    }
}
  1. 在项目中复制此通用扩展
public class DropFilesBehaviorExtension
{
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
        "IsEnabled", typeof(bool), typeof(DropFilesBehaviorExtension), new FrameworkPropertyMetadata(default(bool), OnPropChanged)
        {
            BindsTwoWayByDefault = false,
        });

    private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is FrameworkElement fe))
            throw new InvalidOperationException();
        if ((bool)e.NewValue)
        {
            fe.AllowDrop = true;
            fe.Drop += OnDrop;
            fe.PreviewDragOver += OnPreviewDragOver;
        }
        else
        {
            fe.AllowDrop = false;
            fe.Drop -= OnDrop;
            fe.PreviewDragOver -= OnPreviewDragOver;
        }
    }

    private static void OnPreviewDragOver(object sender, DragEventArgs e)
    {
        // NOTE: PreviewDragOver subscription is required at least when FrameworkElement is a TextBox
        // because it appears that TextBox by default prevent Drag on preview...
        e.Effects = DragDropEffects.Move;
        e.Handled = true;
    }

    private static void OnDrop(object sender, DragEventArgs e)
    {
        var dataContext = ((FrameworkElement)sender).DataContext;
        if (!(dataContext is IFilesDropped filesDropped))
        {
            if (dataContext != null)
                Trace.TraceError($"Binding error, '{dataContext.GetType().Name}' doesn't implement '{nameof(IFilesDropped)}'.");
            return;
        }

        if (!e.Data.GetDataPresent(DataFormats.FileDrop))
            return;

        if (e.Data.GetData(DataFormats.FileDrop) is string[] files)
            filesDropped.OnFilesDropped(files);
    }

    public static void SetIsEnabled(DependencyObject element, bool value)
    {
        element.SetValue(IsEnabledProperty, value);
    }

    public static bool GetIsEnabled(DependencyObject element)
    {
        return (bool)element.GetValue(IsEnabledProperty);
    }
}
  1. 启用 Drop files 行为到您选择的 UI 组件(这里是文本框)
<TextBox ns:DropFilesBehaviorExtension.IsEnabled ="True" />

快乐滴!

Here is a solution a bit more generic, out-of-the-box and easier than Mustafa's one, with a single DependencyProperty

  1. Copy this interface in your projet
public interface IFilesDropped
{
    void OnFilesDropped(string[] files);
}
  1. Make you ViewModel implement the interface
public class SomeViewModel : IFilesDropped
{
    public void OnFilesDropped(string[] files)
    {
        // Implement some logic here
    }
}
  1. Copy this generic extension in your project
public class DropFilesBehaviorExtension
{
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
        "IsEnabled", typeof(bool), typeof(DropFilesBehaviorExtension), new FrameworkPropertyMetadata(default(bool), OnPropChanged)
        {
            BindsTwoWayByDefault = false,
        });

    private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is FrameworkElement fe))
            throw new InvalidOperationException();
        if ((bool)e.NewValue)
        {
            fe.AllowDrop = true;
            fe.Drop += OnDrop;
            fe.PreviewDragOver += OnPreviewDragOver;
        }
        else
        {
            fe.AllowDrop = false;
            fe.Drop -= OnDrop;
            fe.PreviewDragOver -= OnPreviewDragOver;
        }
    }

    private static void OnPreviewDragOver(object sender, DragEventArgs e)
    {
        // NOTE: PreviewDragOver subscription is required at least when FrameworkElement is a TextBox
        // because it appears that TextBox by default prevent Drag on preview...
        e.Effects = DragDropEffects.Move;
        e.Handled = true;
    }

    private static void OnDrop(object sender, DragEventArgs e)
    {
        var dataContext = ((FrameworkElement)sender).DataContext;
        if (!(dataContext is IFilesDropped filesDropped))
        {
            if (dataContext != null)
                Trace.TraceError(
quot;Binding error, '{dataContext.GetType().Name}' doesn't implement '{nameof(IFilesDropped)}'.");
            return;
        }

        if (!e.Data.GetDataPresent(DataFormats.FileDrop))
            return;

        if (e.Data.GetData(DataFormats.FileDrop) is string[] files)
            filesDropped.OnFilesDropped(files);
    }

    public static void SetIsEnabled(DependencyObject element, bool value)
    {
        element.SetValue(IsEnabledProperty, value);
    }

    public static bool GetIsEnabled(DependencyObject element)
    {
        return (bool)element.GetValue(IsEnabledProperty);
    }
}
  1. Enable the Drop files behavior to the UI components of you choice (here a TextBox)
<TextBox ns:DropFilesBehaviorExtension.IsEnabled ="True" />

Happy drops !

清欢 2024-11-12 06:33:33

这只是一个附加答案,将 @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

This is just an additional answer that ports @Asheh's answer's to VB.NET for VB developers.

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
总以为 2024-11-12 06:33:33

这也可能对您有一些帮助。附加的命令行为库允许您将任何事件转换为更紧密地遵循 MVVM 框架的命令。

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

使用它非常简单。并多次保存了我的培根

希望这有帮助

This might also be of some help to you. The attached command behavior library allows you to convert any event(s) into a command which will more closely adhere to the MVVM framework.

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

Using this is extremely easy. And has saved my bacon numerous times

Hope this helps

墨离汐 2024-11-12 06:33:33

根据@Kino101的回答,我创建了一个WinUI 3版本。它也可以作为 要点 提供。

Helpers/DropFilesBehaviour.cs

using Microsoft.UI.Xaml;
using Windows.ApplicationModel.DataTransfer;

namespace App1.Helpers;

public interface IFilesDropped
{
    void OnFilesDropped(string[] files);
}

public class DropFilesBehavior
{
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
        "IsEnabled", typeof(bool), typeof(DropFilesBehavior), PropertyMetadata.Create(default(bool), OnIsEnabledChanged));

    public static readonly DependencyProperty FileDropTargetProperty = DependencyProperty.RegisterAttached(
        "FileDropTarget", typeof(IFilesDropped), typeof(DropFilesBehavior), null);

    public static void SetIsEnabled(DependencyObject element, bool value)
    {
        element.SetValue(IsEnabledProperty, value);
    }

    public static bool GetIsEnabled(DependencyObject element)
    {
        return (bool)element.GetValue(IsEnabledProperty);
    }

    public static void SetFileDropTarget(DependencyObject obj, IFilesDropped value)
    {
        obj.SetValue(FileDropTargetProperty, value);
    }

    public static IFilesDropped GetFileDropTarget(DependencyObject obj)
    {
        return (IFilesDropped)obj.GetValue(FileDropTargetProperty);
    }

    private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var fe = d as FrameworkElement ?? throw new InvalidOperationException();
        
        if ((bool)e.NewValue)
        {
            fe.AllowDrop = true;
            fe.Drop += OnDrop;
            fe.DragOver += OnDragOver;
        }
        else
        {
            fe.AllowDrop = false;
            fe.Drop -= OnDrop;
            fe.DragOver -= OnDragOver;
        }
    }

    private static void OnDragOver(object sender, DragEventArgs e)
    {
        e.AcceptedOperation = DataPackageOperation.Move; // or Link/Copy
        e.Handled = true;
    }

    private static void OnDrop(object sender, DragEventArgs e)
    {
        var dobj = (DependencyObject)sender;
        var target = dobj.GetValue(FileDropTargetProperty);
        var filesDropped = target switch
        {
            IFilesDropped fd => fd,
            null => throw new InvalidOperationException("File drop target is not set."),
            _ => throw new InvalidOperationException($"Binding error, '{target.GetType().Name}' doesn't implement '{nameof(IFilesDropped)}'."),
        };

        if (filesDropped == null)
        {
            return;
        }

        var files = e.DataView.GetStorageItemsAsync().GetAwaiter().GetResult();
        if (files.Count == 0)
        {
            return;
        }

        filesDropped.OnFilesDropped(files.Select(f => f.Path).ToArray());
    }
}

在 ViewModel 中的使用,例如 ViewModels/MainViewModel.cs

using App1.Helpers;

namespace App1.ViewModels;

public partial class MainViewModel : IFilesDropped
{
    public MainViewModel()
    {
    }

    public void OnFilesDropped(string[] files)
    {
        Debug.Print("---");
        foreach (var file in files)
        {
            Debug.Print($"{file}");
        }
    }
}

在视图中的使用,例如 Views/MainPage.xaml

<Page
    x:Class="App1.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:helpers="using:App1.Helpers" 
    Background="{ThemeResource SolidBackgroundFillColorBaseBrush}"
    mc:Ignorable="d">

    <Grid x:Name="ContentArea" helpers:DropFilesBehavior.IsEnabled="True" helpers:DropFilesBehavior.FileDropTarget="{x:Bind ViewModel}">
                
    </Grid>
</Page>

Based on @Kino101's answer, I created a WinUI 3 version. It is also available as a gist.

Helpers/DropFilesBehaviour.cs

using Microsoft.UI.Xaml;
using Windows.ApplicationModel.DataTransfer;

namespace App1.Helpers;

public interface IFilesDropped
{
    void OnFilesDropped(string[] files);
}

public class DropFilesBehavior
{
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
        "IsEnabled", typeof(bool), typeof(DropFilesBehavior), PropertyMetadata.Create(default(bool), OnIsEnabledChanged));

    public static readonly DependencyProperty FileDropTargetProperty = DependencyProperty.RegisterAttached(
        "FileDropTarget", typeof(IFilesDropped), typeof(DropFilesBehavior), null);

    public static void SetIsEnabled(DependencyObject element, bool value)
    {
        element.SetValue(IsEnabledProperty, value);
    }

    public static bool GetIsEnabled(DependencyObject element)
    {
        return (bool)element.GetValue(IsEnabledProperty);
    }

    public static void SetFileDropTarget(DependencyObject obj, IFilesDropped value)
    {
        obj.SetValue(FileDropTargetProperty, value);
    }

    public static IFilesDropped GetFileDropTarget(DependencyObject obj)
    {
        return (IFilesDropped)obj.GetValue(FileDropTargetProperty);
    }

    private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var fe = d as FrameworkElement ?? throw new InvalidOperationException();
        
        if ((bool)e.NewValue)
        {
            fe.AllowDrop = true;
            fe.Drop += OnDrop;
            fe.DragOver += OnDragOver;
        }
        else
        {
            fe.AllowDrop = false;
            fe.Drop -= OnDrop;
            fe.DragOver -= OnDragOver;
        }
    }

    private static void OnDragOver(object sender, DragEventArgs e)
    {
        e.AcceptedOperation = DataPackageOperation.Move; // or Link/Copy
        e.Handled = true;
    }

    private static void OnDrop(object sender, DragEventArgs e)
    {
        var dobj = (DependencyObject)sender;
        var target = dobj.GetValue(FileDropTargetProperty);
        var filesDropped = target switch
        {
            IFilesDropped fd => fd,
            null => throw new InvalidOperationException("File drop target is not set."),
            _ => throw new InvalidOperationException(
quot;Binding error, '{target.GetType().Name}' doesn't implement '{nameof(IFilesDropped)}'."),
        };

        if (filesDropped == null)
        {
            return;
        }

        var files = e.DataView.GetStorageItemsAsync().GetAwaiter().GetResult();
        if (files.Count == 0)
        {
            return;
        }

        filesDropped.OnFilesDropped(files.Select(f => f.Path).ToArray());
    }
}

Usage in your ViewModel, e.g. ViewModels/MainViewModel.cs

using App1.Helpers;

namespace App1.ViewModels;

public partial class MainViewModel : IFilesDropped
{
    public MainViewModel()
    {
    }

    public void OnFilesDropped(string[] files)
    {
        Debug.Print("---");
        foreach (var file in files)
        {
            Debug.Print(
quot;{file}");
        }
    }
}

Usage in your view, e.g. Views/MainPage.xaml

<Page
    x:Class="App1.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:helpers="using:App1.Helpers" 
    Background="{ThemeResource SolidBackgroundFillColorBaseBrush}"
    mc:Ignorable="d">

    <Grid x:Name="ContentArea" helpers:DropFilesBehavior.IsEnabled="True" helpers:DropFilesBehavior.FileDropTarget="{x:Bind ViewModel}">
                
    </Grid>
</Page>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文