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

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

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

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

Можно перетаскивать совершенно произвольные типы и количества объектов. Например, в качестве наиболее распространенных объектов перетаскивания можно назвать файлы, папки, выбранное содержимое.

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

В средствах перетаскивания, предоставляемых 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. Дополнительные сведения о работе с объектами данных см. в разделе Данные и объекты данных.

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

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

Операции перетаскивания могут по-разному воздействовать на передаваемые данные. Например, данные могут копироваться или перемещаться. 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. Затем в этом элементе будут возникать события цели перетаскивания, чтобы их можно было обработать. Во время операции перетаскивания в цели перетаскивания возникает следующая последовательность событий:

  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

См. также