Поделиться через


Обзор перетаскивания и сброса

В этом разделе представлен обзор поддержки перетаскивания в приложениях Windows Presentation Foundation (WPF). Перетаскивание обычно относится к методу передачи данных, который включает в себя использование мыши (или другого указывающего устройства) для выбора одного или нескольких объектов, перемещения этих объектов к желаемому месту назначения в пользовательском интерфейсе и их отпускания.

Поддержка перетаскивания в WPF

Операции перетаскивания обычно включают две стороны: источник перетаскивания, из которого происходит перетаскиваемый объект, и целевой объект перетаскивания, который получает перемещенный объект. Источник перетаскивания и целевой объект перетаскивания может быть элементами пользовательского интерфейса в одном приложении или другом приложении.

Тип и количество объектов, которые можно управлять перетаскиванием, полностью произвольным. Например, файлы, папки и выбор содержимого являются некоторыми из наиболее распространенных объектов, управляемых с помощью операций перетаскивания.

Конкретные действия, выполняемые во время операции перетаскивания, зависят от приложения и часто определяются контекстом. Например, перетаскивание выбранных файлов из одной папки в другую на одном устройстве хранения перемещает файлы по умолчанию, а перетаскивание файлов из UNC-папки в локальную папку копирует файлы по умолчанию.

Средства перетаскивания, предоставляемые WPF, предназначены для обеспечения высокой гибкости и настройки для поддержки различных сценариев перетаскивания. Перетаскивание поддерживает управление объектами в одном приложении или между различными приложениями. Перетаскивание между приложениями WPF и другими приложениями Windows также полностью поддерживается.

В WPF любой UIElement или ContentElement может участвовать в перетаскивании объектов. События и методы, необходимые для операций перетаскивания, определяются в классе DragDrop. Классы UIElement и ContentElement содержат псевдонимы для присоединённых событий DragDrop, чтобы эти события отображались в списке членов класса, когда UIElement или ContentElement наследуются в качестве базового элемента. Обработчики событий, подключенные к этим событиям, подключаются к базовому присоединенному событию DragDrop и получают тот же экземпляр данных события. Дополнительные сведения см. в событии UIElement.Drop.

Важный

Перетаскивание OLE не работает в зоне Интернета.

Передача данных

Перетаскивание — это часть более общей области передачи данных. Передача данных включает операции перетаскивания, копирования и вставки. Операция перетаскивания аналогична операции копирования и вставки или вырезания, которая используется для передачи данных из одного объекта или приложения в другой с помощью системного буфера обмена. Для обоих типов операций требуется:

  • Исходный объект, предоставляющий данные.

  • Способ временного хранения передаваемых данных.

  • Целевой объект, получающий данные.

В операции копирования и вставки системный буфер обмена используется для временного хранения передаваемых данных; в операции перетаскивания DataObject используется для хранения данных. Концептуально объект данных состоит из одной или нескольких пар Object, содержащей фактические данные, и соответствующий идентификатор формата данных.

Источник перетаскивания инициирует операцию перетаскивания путем вызова статического метода DragDrop.DoDragDrop и передачи переданных данных в него. При необходимости метод DoDragDrop автоматически упаковывает данные в DataObject. Для большего контроля над форматом данных можно упаковать данные в DataObject перед передачей в метод DoDragDrop. Целевой объект удаления отвечает за извлечение данных из DataObject. Дополнительные сведения о работе с данными и объектами данных см. в разделе .

Источником и целью операции перетаскивания являются элементы пользовательского интерфейса; однако данные, которые фактически передаются, обычно не имеют визуального представления. Можно написать код, чтобы предоставить визуальное представление перетаскиваемых данных, например при перетаскивании файлов в проводнике Windows. По умолчанию обратная связь предоставляется пользователю путем изменения курсора для представления эффекта, который операция перетаскивания и отпускания будет иметь на данные, например, будут ли данные перемещены или скопированы.

Эффекты перетаскивания

Операции перетаскивания могут оказывать разные эффекты на передаваемые данные. Например, можно скопировать данные или переместить данные. WPF определяет перечисление DragDropEffects, которое можно использовать для указания эффекта операции "перетаскивание и сброс". В источнике перетаскивания можно указать эффекты, которые источник будет разрешать в методе DoDragDrop. В целевом объекте удаления можно указать эффект, который целевой объект намерен в свойстве Effects класса DragEventArgs. Когда целевой объект для сброса задает предполагаемый эффект в событии DragOver, эта информация передается источнику перетаскивания в событии GiveFeedback. Источник перетаскивания использует эту информацию, чтобы сообщить пользователю, какое воздействие целевой объект предполагает оказать на данные. При удалении данных целевой объект удаления указывает его фактический эффект в событии Drop. Эти сведения передаются обратно источнику перетаскивания в качестве возвращаемого значения метода DoDragDrop. Если целевой объект перетаскивания возвращает эффект, который не находится в списке источников перетаскивания allowedEffects, операция перетаскивания отменяется без каких-либо операций передачи данных.

Важно помнить, что в WPF значения DragDropEffects используются только для обеспечения связи между источником перетаскивания и целевым объектом перетаскивания в отношении эффектов операции перетаскивания. Фактический эффект операции "перетаскивания" зависит от того, напишите ли вы соответствующий код в своём приложении.

Например, целевой объект удаления может указать, что эффект удаления данных на нем заключается в перемещении данных. Однако для перемещения данных его необходимо добавить в целевой элемент и удалить из исходного элемента. Исходный элемент может указывать на то, что он позволяет перемещать данные, но если код не предоставляется для удаления данных из исходного элемента, конечный результат будет заключаться в том, что данные копируются и не перемещаются.

События перетаскивания

Операции перетаскивания поддерживают модель на основе событий. Источник перетаскивания и целевой объект перетаскивания используют стандартный набор событий для обработки операций перетаскивания. В следующих таблицах суммируются стандартные события перетаскивания и отпускания. Это связанные события в классе DragDrop. Дополнительные сведения о прикрепленных событиях см. в обзоре прикрепленных событий.

Перетаскивание исходных событий

Событие Сводка
GiveFeedback Это событие происходит непрерывно во время операции перетаскивания и позволяет источнику перетаскивания предоставлять пользователю обратную связь. Эта обратная связь обычно предоставляется путем изменения внешнего вида указателя мыши, чтобы указать эффекты, разрешенные целевым объектом удаления. Это бурное событие.
QueryContinueDrag Это событие возникает при изменении состояния клавиатуры или кнопки мыши во время операции перетаскивания и позволяет источнику перетаскивания отменить операцию перетаскивания в зависимости от состояний клавиши или кнопки. Это бурное событие.
PreviewGiveFeedback Версия GiveFeedbackс туннелированием.
PreviewQueryContinueDrag Версия QueryContinueDragс туннелированием.

Удаление целевых событий

Событие Сводка
DragEnter Это событие возникает, когда объект перетаскивается в границу зоны сброса. Это бурное событие.
DragLeave Это событие возникает, когда элемент перетаскивается за границы зоны целевого объекта. Это бурное событие.
DragOver Это событие происходит непрерывно, пока объект перетаскивается в пределах области цели для сброса. Это бурное событие.
Drop Это событие происходит, когда объект сбрасывается на целевую область. Это бурное событие.
PreviewDragEnter Версия DragEnterс туннелированием.
PreviewDragLeave Версия DragLeaveс туннелированием.
PreviewDragOver Версия DragOverс туннелированием.
PreviewDrop Версия Dropс туннелированием.

Чтобы обрабатывать события перетаскивания для экземпляров объекта, добавьте обработчики для событий, перечисленных в предыдущих таблицах. Чтобы обрабатывать события перетаскивания на уровне класса, переопределите соответствующие методы virtual On*Event и On*PreviewEvent. Дополнительные сведения см. в разделе Обработка маршрутизируемых событий базовыми классами управления.

Реализация перетаскивания и отпускания

Элемент пользовательского интерфейса может быть источником перетаскивания, целевым объектом перетаскивания или обоими. Чтобы реализовать простую операцию перетаскивания, необходимо написать код для запуска операции перетаскивания и обработки удаленных данных. Вы можете улучшить пользовательский опыт перетаскивания, обрабатывая необязательные события перетаскивания.

Чтобы реализовать базовое перетаскивание, выполните следующие задачи:

  • Определите элемент, который будет источником перемещения. Источник перетаскивания может быть UIElement или ContentElement.

  • Создайте в источнике перетаскивания обработчик события, который инициирует операцию перетаскивания. Это событие обычно является событием MouseMove.

  • В обработчике событий источника перетаскивания вызовите метод DoDragDrop, чтобы инициировать операцию перетаскивания. В вызове DoDragDrop укажите источник перетаскивания, передаваемые данные и допустимые эффекты.

  • Определите элемент, который будет объектом сброса. Целевой объект для перемещения может быть UIElement или ContentElement.

  • В целевом объекте удаления задайте для свойства AllowDrop значение true.

  • В целевом объекте удаления создайте обработчик событий Drop для обработки удаленных данных.

  • В обработчике событий Drop извлеките данные из DragEventArgs с помощью методов GetDataPresent и GetData.

  • В обработчике событий Drop используйте данные для выполнения требуемой операции перетаскивания.

Вы можете улучшить реализацию перетаскивания, создав настраиваемую DataObject и обрабатывая необязательные события перетаскивания источника и удаления целевых событий, как показано в следующих задачах:

  • Чтобы передать пользовательские данные или несколько элементов данных, создайте DataObject для передачи в метод DoDragDrop.

  • Чтобы выполнить дополнительные действия во время перетаскивания, обработайте события DragEnter, DragOverи DragLeave на целевом объекте перетаскивания.

  • Чтобы изменить внешний вид указателя мыши, обработайте событие GiveFeedback в источнике перетаскивания.

  • Чтобы изменить способ отмены операции перетаскивания, обработайте событие QueryContinueDrag в источнике перетаскивания.

Пример перетаскивания и сброса

В этом разделе описывается, как реализовать перетаскивание для элемента Ellipse. Ellipse — это источник перетаскивания и целевой объект перетаскивания. Передаваемые данные — это строковое представление свойства Fill эллипса. В следующем XAML демонстрируется элемент Ellipse и связанные с перетаскиванием события, которые он обрабатывает. Полные инструкции по реализации функции перетаскивания см. в пошаговом руководстве: включение функции перетаскивания в элементе управления пользователем.

<Ellipse Height="50" Width="50" Fill="Green"
     MouseMove="ellipse_MouseMove"
     GiveFeedback="ellipse_GiveFeedback"
     AllowDrop="True"
     DragEnter="ellipse_DragEnter" DragLeave="ellipse_DragLeave"
     DragOver="ellipse_DragOver" Drop="ellipse_Drop" />

Включение элемента в качестве источника перетаскивания

Объект, являющийся источником перетаскивания, отвечает за:

  • Определение момента начала перетаскивания.

  • Инициирование операции перетаскивания.

  • Определение передаваемых данных.

  • Указание эффектов, которые может иметь операция перетаскивания для передаваемых данных.

Источник перетаскивания также может отправить отзыв пользователю о разрешенных действиях (перемещение, копирование, нет) и отменить операцию перетаскивания на основе дополнительных входных данных пользователя, например нажатия клавиши ESC во время перетаскивания.

Приложение несет ответственность за определение того, когда происходит перетаскивание, а затем инициирует операцию перетаскивания путем вызова метода DoDragDrop. Как правило, это происходит, когда событие MouseMove происходит на элементе, который будет перетаскиваться во время нажатия кнопки мыши. В следующем примере показано, как инициировать операцию перетаскивания из обработчика событий MouseMove элемента Ellipse, чтобы сделать его источником перетаскивания. Передаваемые данные — это строковое представление свойства Fill эллипса.

private void ellipse_MouseMove(object sender, MouseEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null && e.LeftButton == MouseButtonState.Pressed)
    {
        DragDrop.DoDragDrop( ellipse,
                             ellipse.Fill.ToString(),
                             DragDropEffects.Copy);
    }
}
Private Sub Ellipse_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing AndAlso e.LeftButton = MouseButtonState.Pressed Then
        DragDrop.DoDragDrop(ellipse, ellipse.Fill.ToString(), DragDropEffects.Copy)
    End If
End Sub

В обработчике событий MouseMove вызовите метод DoDragDrop, чтобы инициировать операцию перетаскивания. Метод DoDragDrop принимает три параметра:

  • dragSource — ссылка на объект зависимостей, который является источником передаваемых данных; Обычно это источник события MouseMove.

  • data — объект, содержащий передаваемые данные, завернутый в DataObject.

  • allowedEffects — одно из значений перечисления DragDropEffects, указывающее разрешенные эффекты операции перетаскивания.

Любой сериализуемый объект можно передать в параметре data. Если данные еще не упакованы в DataObject, он автоматически будет упакован в новый DataObject. Чтобы передать несколько элементов данных, необходимо создать DataObject самостоятельно и передать его в метод DoDragDrop. Дополнительные сведения см. в Данные и объекты данных.

Параметр allowedEffects используется для указания того, что источник перетаскивания позволит целевому объекту перетаскивания выполнять передачу данных. Распространенные значения источника перетаскивания : Copy, Moveи All.

Заметка

Целевой объект для сброса также может указать, какие эффекты подразумеваются в ответ на сброшенные данные. Например, если целевой объект удаления не распознает тип данных, который нужно удалить, он может отказаться от данных, установив допустимые эффекты для None. Обычно это делается в обработчике событий DragOver.

Источник перетаскивания может опционально обрабатывать события GiveFeedback и QueryContinueDrag. Эти события имеют обработчики по умолчанию, которые используются, если вы не помечаете события как обработанные. Обычно эти события игнорируются, если у вас нет конкретной необходимости изменить их поведение по умолчанию.

Событие GiveFeedback непрерывно вызывается, пока перетаскивается исходный элемент. Обработчик по умолчанию для этого события проверяет, находится ли источник перетаскивания над допустимой целью перетаскивания. Если это так, он проверяет допустимые эффекты целевого объекта удаления. Затем он отправляет отзыв конечному пользователю относительно разрешенных эффектов удаления. Обычно это делается путем изменения курсора мыши на курсор "нет перемещения", "копирование" или "перемещение". Это событие следует обрабатывать только в том случае, если необходимо использовать пользовательские курсоры для предоставления отзывов пользователю. Если вы обрабатываете это событие, не забудьте пометить его как обработанное, чтобы обработчик по умолчанию не переопределил ваш обработчик.

Событие QueryContinueDrag непрерывно вызывается, пока перетаскивается исходный элемент. Это событие можно обрабатывать, чтобы определить, какое действие завершает операцию перетаскивания на основе состояния клавиш ESC, SHIFT, CTRL и ALT, а также состояния кнопок мыши. Обработчик по умолчанию для этого события отменяет операцию перетаскивания при нажатии клавиши ESC и удаляет данные, если кнопка мыши освобождена.

Осторожность

Эти события постоянно вызываются во время операции перетаскивания. Поэтому следует избегать ресурсоемких задач в обработчиках событий. Например, используйте кэшированный курсор вместо создания нового курсора при каждом вызове события GiveFeedback.

Включение элемента в качестве целевого объекта для перетаскивания

Объект, который является целевым объектом удаления, отвечает за:

  • Указание допустимого целевого объекта удаления.

  • Отвечая на источник перетаскивания при перетаскивании по целевому объекту.

  • Убедитесь, что передаваемые данные отображаются в формате, который он может получать.

  • Обработка отброшенных данных.

Чтобы указать, что элемент является целевым объектом удаления, задайте для его свойства AllowDrop значение true. Затем целевые события раскрывающегося списка будут вызваны на элементе, чтобы их можно было обрабатывать.

При оценке целевого объекта выполняется тест попадания, чтобы определить, находится ли курсор над визуальным представлением элемента. Некоторые элементы управления, такие как Canvas, не имеют визуального элемента и не могут использоваться в качестве целевого объекта для перетаскивания, если визуальный элемент не добавлен. Задайте свойству Canvas.Background любой цвет, чтобы создать визуальный элемент, заполняющий Canvas цветом. Чтобы сохранить Canvas прозрачным, но разрешить его использование в качестве цели для перетаскивания, задайте для свойства Background значение Transparent.

Во время операции перетаскивания на целевом объекте сброса происходит следующая последовательность событий:

  1. DragEnter

  2. DragOver

  3. DragLeave или Drop

Событие DragEnter происходит, когда данные перетаскиваются в границы целевого объекта. Обычно это событие обрабатывается, чтобы предоставить предварительный просмотр эффектов операции перетаскивания, если это уместно для вашего приложения. Не устанавливайте свойство DragEventArgs.Effects в событии DragEnter, так как оно будет перезаписано в событии DragOver.

В следующем примере показан обработчик событий DragEnter для элемента Ellipse. Этот код предварительно просматривает эффекты операции перетаскивания, сохраняя текущую кисть Fill. Затем он использует метод GetDataPresent, чтобы проверить, содержит ли DataObject, перетаскиваемый по эллипсу, строковые данные, которые можно преобразовать в Brush. В этом случае данные извлекаются с помощью метода GetData. Затем преобразуется в Brush и применяется к эллипсу. Изменение отменяется в обработчике событий DragLeave. Если данные не могут быть преобразованы в Brush, действие не выполняется.

private Brush _previousFill = null;
private void ellipse_DragEnter(object sender, DragEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null)
    {
        // Save the current Fill brush so that you can revert back to this value in DragLeave.
        _previousFill = ellipse.Fill;

        // If the DataObject contains string data, extract it.
        if (e.Data.GetDataPresent(DataFormats.StringFormat))
        {
            string dataString = (string)e.Data.GetData(DataFormats.StringFormat);

            // If the string can be converted into a Brush, convert it.
            BrushConverter converter = new BrushConverter();
            if (converter.IsValid(dataString))
            {
                Brush newFill = (Brush)converter.ConvertFromString(dataString);
                ellipse.Fill = newFill;
            }
        }
    }
}
Private _previousFill As Brush = Nothing
Private Sub Ellipse_DragEnter(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing Then
        ' Save the current Fill brush so that you can revert back to this value in DragLeave.
        _previousFill = ellipse.Fill

        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString = e.Data.GetData(DataFormats.StringFormat)

            ' If the string can be converted into a Brush, convert it.
            Dim converter As New BrushConverter()
            If converter.IsValid(dataString) Then
                Dim newFill As Brush = CType(converter.ConvertFromString(dataString), Brush)
                ellipse.Fill = newFill
            End If
        End If
    End If
End Sub

Событие DragOver происходит непрерывно, пока данные перетаскиваются по целевому объекту удаления. Это событие связано с событием GiveFeedback в источнике перетаскивания. В обработчике событий DragOver обычно используются методы GetDataPresent и GetData для проверки того, находится ли передаваемые данные в формате, который может обрабатывать целевой объект удаления. Можно также проверить, нажимаются ли какие-либо модификаторные клавиши, которые обычно указывают, намерен ли пользователь переместить или скопировать действие. После выполнения этих проверок необходимо задать свойство DragEventArgs.Effects, чтобы уведомить источник перетаскивания, какой эффект приведет к удалению данных. Источник перетаскивания получает эту информацию в аргументах события GiveFeedback и может установить соответствующий курсор для предоставления обратной связи пользователю.

В следующем примере показан обработчик событий DragOver для элемента Ellipse. Этот код проверяет, содержат ли данные DataObject, который перетаскивается по эллипсу, строковые данные, которые можно преобразовать в Brush. В этом случае свойству DragEventArgs.Effects устанавливается значение Copy. Это указывает источнику перетаскивания, что данные можно скопировать в эллипс. Если данные не могут быть преобразованы в Brush, для свойства DragEventArgs.Effects задано значение None. Это указывает источнику перетаскивания, что эллипс не является допустимым целевым объектом для сброса данных.

private void ellipse_DragOver(object sender, DragEventArgs e)
{
    e.Effects = DragDropEffects.None;

    // If the DataObject contains string data, extract it.
    if (e.Data.GetDataPresent(DataFormats.StringFormat))
    {
        string dataString = (string)e.Data.GetData(DataFormats.StringFormat);

        // If the string can be converted into a Brush, allow copying.
        BrushConverter converter = new BrushConverter();
        if (converter.IsValid(dataString))
        {
            e.Effects = DragDropEffects.Copy | DragDropEffects.Move;
        }
    }
}
Private Sub Ellipse_DragOver(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    e.Effects = DragDropEffects.None

    ' If the DataObject contains string data, extract it.
    If e.Data.GetDataPresent(DataFormats.StringFormat) Then
        Dim dataString = e.Data.GetData(DataFormats.StringFormat)

        ' If the string can be converted into a Brush, convert it.
        Dim converter As New BrushConverter()
        If converter.IsValid(dataString) Then
            e.Effects = DragDropEffects.Copy Or DragDropEffects.Move
        End If
    End If
End Sub

Событие DragLeave возникает, когда данные перетаскиваются из границы целевого объекта без сбрасывания. Это событие обрабатывается для отмены всех действий, которые вы сделали в обработчике событий DragEnter.

В следующем примере показан обработчик событий DragLeave для элемента Ellipse. Этот код отменяет предварительный просмотр, выполняемый в обработчике событий DragEnter, применив сохраненный Brush к эллипсу.

private void ellipse_DragLeave(object sender, DragEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null)
    {
        ellipse.Fill = _previousFill;
    }
}
Private Sub Ellipse_DragLeave(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing Then
        ellipse.Fill = _previousFill
    End If
End Sub

Событие Drop возникает, когда данные сбрасываются на целевой объект. По умолчанию это происходит, когда кнопка мыши отпускается. В обработчике событий Drop используется метод GetData для извлечения передаваемых данных из DataObject и выполнения обработки данных, необходимых приложению. Событие Drop завершает операцию перетаскивания.

В следующем примере показан обработчик событий Drop для элемента Ellipse. Этот код применяет эффекты операции перетаскивания и похож на код в обработчике событий DragEnter. Он проверяет, содержит ли DataObject, перетаскиваемый над эллипсом, строковые данные, которые можно преобразовать в Brush. В этом случае Brush применяется к эллипсу. Если данные не могут быть преобразованы в Brush, действие не выполняется.

private void ellipse_Drop(object sender, DragEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null)
    {
        // If the DataObject contains string data, extract it.
        if (e.Data.GetDataPresent(DataFormats.StringFormat))
        {
            string dataString = (string)e.Data.GetData(DataFormats.StringFormat);

            // If the string can be converted into a Brush,
            // convert it and apply it to the ellipse.
            BrushConverter converter = new BrushConverter();
            if (converter.IsValid(dataString))
            {
                Brush newFill = (Brush)converter.ConvertFromString(dataString);
                ellipse.Fill = newFill;
            }
        }
    }
}
Private Sub Ellipse_Drop(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing Then

        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString = e.Data.GetData(DataFormats.StringFormat)

            ' If the string can be converted into a Brush, convert it.
            Dim converter As New BrushConverter()
            If converter.IsValid(dataString) Then
                Dim newFill As Brush = CType(converter.ConvertFromString(dataString), Brush)
                ellipse.Fill = newFill
            End If
        End If
    End If
End Sub

См. также