如何在WPF中拖放操作期间处理任意事件?

时间:2013-05-11 07:23:53

标签: wpf drag-and-drop

我想在拖放操作期间处理诸如OnMouseMove或MouseWheel之类的事件。

但是,据我所知this MSDN topic on Drag/Drop,在拖放操作期间触发的唯一事件是GiveFeedback,QueryContinueDrag,Drag Enter / Leave / Over以及它们的Preview *对应物。从本质上讲,处理这些事件可以让我获取鼠标位置,或者查看用户是按下Ctrl,Shift,Alt,Esc,还是按下或释放其中一个鼠标按钮。

但我想要的是在拖放操作期间处理其他事件,例如MouseWheel。具体来说,我想要做的是让用户滚动窗口的内容(使用鼠标滚轮),同时拖动它上面的东西。我已经尝试为这些其他事件编写处理程序,包括冒泡和隧道版本,以及将它们附加到控件层次结构的各个级别,但据我所知,它们都没有被触发。

我知道有一个部分解决方案(例如描述here),当鼠标位置靠近窗口的顶部或底部时,您可以使用DragOver滚动窗口的内容。但这不是我想要做的。

我遇到article,意味着可以在拖动操作期间处理(例如)OnMouseMove事件。我这样说是因为文章中的代码是上述方法的变体,但它处理的是OnMouseMove而不是DragOver。但是,我尝试采用这种方法,但仍然无法在拖动时触发OnMouseMove事件。我在下面添加了我的代码。它在F#中,所以我使用了FSharpx中的F# XAML type provider

MainWindow.xaml:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="500" Width="900">
    <DockPanel Name="panel1">
        <StatusBar Name="status1" DockPanel.Dock="Bottom">
            <TextBlock Name="statustext1" />
        </StatusBar>
    </DockPanel>
</Window>

Program.fs:

(* 
Added references: PresentationCore, PresentationFramework, System.Xaml, UIAutomationTypes, WindowsBase.
*)

// STAThread, DateTime
open System
// Application
open System.Windows
// TextBox
open System.Windows.Controls

// XAML type provider
open FSharpx

type MainWindow = XAML<"MainWindow.xaml">

type TextBox2 (status : TextBlock) as this =
    inherit TextBox () with
    member private this.preview_mouse_left_button_down (args : Input.MouseButtonEventArgs) = do
        this.CaptureMouse () |> ignore
        base.OnPreviewMouseLeftButtonDown args
// Fires while selecting text with the mouse, but not while dragging.
    member private this.preview_mouse_move (args : Input.MouseEventArgs) =
        if this.IsMouseCaptured then do status.Text <- sprintf "mouse move: %d" <| DateTime.Now.Ticks
        do base.OnPreviewMouseMove args
    member private this.preview_mouse_left_button_up (args : Input.MouseButtonEventArgs) = do
        if this.IsMouseCaptured then do this.ReleaseMouseCapture ()
        base.OnPreviewMouseLeftButtonUp args
    do
        this.PreviewMouseLeftButtonDown.Add this.preview_mouse_left_button_down
        this.PreviewMouseMove.Add this.preview_mouse_move
        this.PreviewMouseLeftButtonUp.Add this.preview_mouse_left_button_up

let load_window () =
    let win = MainWindow ()
    let t = new TextBox2 (win.statustext1)
    do
        t.TextWrapping <- TextWrapping.Wrap
        t.AcceptsReturn <- true
        t.Height <- Double.NaN
        win.panel1.Children.Add t |> ignore
    win.Root

[<STAThread>]
(new Application () ).Run(load_window () ) |> ignore

2 个答案:

答案 0 :(得分:1)

我认为您可以通过 PreviewDragEnter PreviewDragOver Drop 更有效地完成此操作。我写了一篇关于编写自己的拖放文本框的博客主题,这应该有助于您入门。您可以从那里添加滚动功能:

http://xcalibur37.wordpress.com/2011/12/10/wpf-drag-and-drop-textbox-for-windows-explorer-files/

代码:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        // Initialize UI
        InitializeComponent();

        // Loaded event
        this.Loaded += delegate
            {
                TextBox1.AllowDrop = true;
                TextBox1.PreviewDragEnter += TextBox1PreviewDragEnter;
                TextBox1.PreviewDragOver += TextBox1PreviewDragOver;
                TextBox1.Drop += TextBox1DragDrop;
            };
    }

    /// <summary>
    /// We have to override this to allow drop functionality.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void TextBox1PreviewDragOver(object sender, DragEventArgs e)
    {
        e.Handled = true;
    }

    /// <summary>
    /// Evaluates the Data and performs the DragDropEffect
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void TextBox1PreviewDragEnter(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            e.Effects = DragDropEffects.Copy;
        }
        else
        {
            e.Effects = DragDropEffects.None;
        }
    }

    /// <summary>
    /// The drop activity on the textbox.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void TextBox1DragDrop(object sender, DragEventArgs e)
    {
        // Get data object
        var dataObject = e.Data as DataObject;

        // Check for file list
        if (dataObject.ContainsFileDropList())
        {
            // Clear values
            TextBox1.Text = string.Empty;

            // Process file names
            StringCollection fileNames = dataObject.GetFileDropList();
            StringBuilder bd = new StringBuilder();
            foreach (var fileName in fileNames)
            {
                bd.Append(fileName + "\n");
            }

            // Set text
            TextBox1.Text = bd.ToString();
        }
    }
}

博客主题为您提供了每个部分的细分分析。

答案 1 :(得分:1)

我知道这是一个老问题,但是我必须做类似的事情,我的解决方案也可以解决此问题。我认为最好还是在这里链接它,以防万一。这不是最简单的解决方案,但效果很好。

我最终通过p / invoke使用hooks来获取本机窗口消息,然后再通过拖放操作消耗它们。通过使用WH_MOUSE挂钩,我能够拦截WM_MOUSEMOVE消息并跟踪鼠标,而没有WPF的Mouse和DragDrop事件。这应该适用于所有鼠标消息,包括WM_MOUSEWHEEL。

您可以在我最后发表自己的答案的地方查看我的问题。我包括了大多数源代码:
WPF - Track mouse during Drag & Drop while AllowDrop = False