Пошаговое руководство. Включение перетаскивания для пользовательского элемента управления

В этом пошаговом руководстве демонстрируется создание настраиваемого пользовательского элемента управления, который может участвовать в переносе данных путем перетаскивания в Windows Presentation Foundation.

В этом пошаговом руководстве вы создадите настраиваемый элемент управления WPF UserControl, представляющий круг. Вы реализуете в этом элементе управления функциональность, позволяющую переносить данные посредством перетаскивания. Например, при перетаскивании из одного элемента управления Circle в другой данные цвета заливки копируются из исходного круга в целевой. При перетаскивании элемента управления в TextBox, в TextBox копируется строковое представление цвета заливки. Также вы создадите небольшое приложение с двумя панельными элементами управления и классом TextBox, чтобы проверить функцию перетаскивания. Вы напишете код, позволяющий панелям обрабатывать перемещенные перетаскиванием данные Circle, в результате чего элементы управления Circle можно будет перемещать или копировать из дочерней коллекции на одной панели в другую.

В этом пошаговом руководстве рассматриваются следующие задачи:

  • Создание настраиваемого пользовательского элемента управления.

  • Включение пользовательского элемента управления в качестве источника перетаскивания.

  • Включение пользовательского элемента управления в качестве целевого объекта перетаскивания.

  • Включение панели для получения данных, перемещенных перетаскиванием из пользовательского элемента управления.

Необходимые компоненты

Для выполнения шагов, описанных в этом руководстве, вам понадобится Visual Studio.

Создание проекта приложения

В этом разделе создается инфраструктура приложения, которая включает главную страницу с двумя панелями и поле TextBox.

  1. Создайте проект приложения WPF на Visual Basic или Visual C# с именем DragDropExample. Дополнительные сведения см. в разделе Пошаговое руководство. Создание первого классического приложения WPF.

  2. Откройте файл MainWindow.xaml.

  3. Добавьте следующую разметку между открывающим и закрывающим тегом Grid.

    Эта разметка создает пользовательский интерфейс для тестового приложения.

    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0"
                Background="Beige">
        <TextBox Width="Auto" Margin="2"
                 Text="green"/>
    </StackPanel>
    <StackPanel Grid.Column="1"
                Background="Bisque">
    </StackPanel>
    

Добавление в проект нового пользовательского элемента управления

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

  1. В меню "Проект" выберите пункт Добавить пользовательский элемент управления.

  2. В диалоговом окне Добавление нового элемента измените имя на Circle.xaml и нажмите Добавить.

    Circle.xaml и его код программной части добавляются в проект.

  3. Откройте файл Circle.xaml.

    Этот файл будет содержать элементы пользовательского интерфейса пользовательского элемента управления.

  4. Добавьте следующую разметку в корневой каталог Grid, чтобы создать простой пользовательский элемент управления, пользовательский интерфейс которого представлен синим кругом.

    <Ellipse x:Name="circleUI" 
             Height="100" Width="100"
             Fill="Blue" />
    
  5. Откройте файл Circle.xaml.cs или Circle.xaml.vb.

  6. В C# добавьте следующий код после конструктора без параметров, чтобы создать его перегрузку. В Visual Basic добавьте следующий код, чтобы создать конструктор без параметров и его перегрузку.

    Чтобы разрешить копирование пользовательского элемента управления, нужно добавить метод конструктора копии в файле кода программной части. В упрощенном пользовательском элементе управления Circle копируются только свойства Fill и размер пользовательского элемента управления.

    public Circle(Circle c)
    {
        InitializeComponent();
        this.circleUI.Height = c.circleUI.Height;
        this.circleUI.Width = c.circleUI.Height;
        this.circleUI.Fill = c.circleUI.Fill;
    }
    
    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()
    End Sub
    
    Public Sub New(ByVal c As Circle)
        InitializeComponent()
        Me.circleUI.Height = c.circleUI.Height
        Me.circleUI.Width = c.circleUI.Height
        Me.circleUI.Fill = c.circleUI.Fill
    End Sub
    

Добавление пользовательского элемента управления в главное окно

  1. Откройте файл MainWindow.xaml.

  2. Добавьте следующий код XAML в открывающий тег Window, чтобы создать ссылку на текущее приложение в пространстве имен XML.

    xmlns:local="clr-namespace:DragDropExample"
    
  3. В первом объекте StackPanel добавьте следующий XAML-код, чтобы создать два экземпляра пользовательского элемента управления Circle на первой панели.

    <local:Circle Margin="2" />
    <local:Circle Margin="2" />
    

    Полный код XAML для панели выглядит следующим образом.

    <StackPanel Grid.Column="0"
                Background="Beige">
        <TextBox Width="Auto" Margin="2"
                 Text="green"/>
        <local:Circle Margin="2" />
        <local:Circle Margin="2" />
    </StackPanel>
    <StackPanel Grid.Column="1"
                Background="Bisque">
    </StackPanel>
    

Реализация событий источника перетаскивания в пользовательском элементе управления

В этом разделе будет переопределен метод OnMouseMove и инициирована операция перетаскивания.

Если перетаскивание начато (нажата кнопка мыши, мышь перемещается), вы упакуете переносимые данные в объект DataObject. В данном случае элемент управления Circle упаковывает три элемента данных; строковое представление цвета заливки, двойное представление высоты и копию самого себя.

Запуск операции перетаскивания

  1. Откройте файл Circle.xaml.cs или Circle.xaml.vb.

  2. Добавьте следующее переопределение OnMouseMove, чтобы обеспечить обработку классом события MouseMove.

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            // Package the data.
            DataObject data = new DataObject();
            data.SetData(DataFormats.StringFormat, circleUI.Fill.ToString());
            data.SetData("Double", circleUI.Height);
            data.SetData("Object", this);
    
            // Initiate the drag-and-drop operation.
            DragDrop.DoDragDrop(this, data, DragDropEffects.Copy | DragDropEffects.Move);
        }
    }
    
    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Input.MouseEventArgs)
        MyBase.OnMouseMove(e)
        If e.LeftButton = MouseButtonState.Pressed Then
            ' Package the data.
            Dim data As New DataObject
            data.SetData(DataFormats.StringFormat, circleUI.Fill.ToString())
            data.SetData("Double", circleUI.Height)
            data.SetData("Object", Me)
    
            ' Inititate the drag-and-drop operation.
            DragDrop.DoDragDrop(Me, data, DragDropEffects.Copy Or DragDropEffects.Move)
        End If
    End Sub
    

    Переопределение OnMouseMove выполняет следующие задачи.

    • Проверяет, нажата ли левая кнопка мыши при перемещении мыши.

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

    • Вызывает статический метод DragDrop.DoDragDrop для инициализации операции перетаскивания. В метод DoDragDrop передаются следующие параметры.

      • dragSource — ссылка на этот элемент управления.

      • data — DataObject, созданный в коде ранее.

      • allowedEffects — разрешенные операции перетаскивания (Copy и Move).

  3. Нажмите клавишу F5 для сборки и запуска приложения.

  4. Выберите один из элементов управления Circle и перетащите его над панелями, другим элементом Circle и полем TextBox. При перетаскивании мышью над элементом TextBox курсор изменяется, чтобы обозначить перемещение.

  5. Перетаскивая объект Circle над TextBox, нажмите клавишу Ctrl. Обратите внимание на то, как изменится курсор, обозначая копирование.

  6. Перетащите и сбросьте объект Circle на TextBox. Строковое представление цвета заливки элемента управления Circle добавляется в поле TextBox.

    String representation of Circle's fill color

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

Обратная связь с пользователем

  1. Откройте файл Circle.xaml.cs или Circle.xaml.vb.

  2. Добавьте следующее переопределение OnGiveFeedback, чтобы обеспечить обработку классом события GiveFeedback.

    protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
    {
        base.OnGiveFeedback(e);
        // These Effects values are set in the drop target's
        // DragOver event handler.
        if (e.Effects.HasFlag(DragDropEffects.Copy))
        {
            Mouse.SetCursor(Cursors.Cross);
        }
        else if (e.Effects.HasFlag(DragDropEffects.Move))
        {
            Mouse.SetCursor(Cursors.Pen);
        }
        else
        {
            Mouse.SetCursor(Cursors.No);
        }
        e.Handled = true;
    }
    
    Protected Overrides Sub OnGiveFeedback(ByVal e As System.Windows.GiveFeedbackEventArgs)
        MyBase.OnGiveFeedback(e)
        ' These Effects values are set in the drop target's
        ' DragOver event handler.
        If e.Effects.HasFlag(DragDropEffects.Copy) Then
            Mouse.SetCursor(Cursors.Cross)
        ElseIf e.Effects.HasFlag(DragDropEffects.Move) Then
            Mouse.SetCursor(Cursors.Pen)
        Else
            Mouse.SetCursor(Cursors.No)
        End If
        e.Handled = True
    End Sub
    

    Переопределение OnGiveFeedback выполняет следующие задачи.

    • Проверяет значения Effects, установленные в обработчике событий DragOver конечного (целевого) объекта перетаскивания.

    • Задает пользовательский курсор в зависимости от значения Effects. Курсор предназначен для предоставления визуальной обратной связи пользователю о том, какой результат будет иметь перетаскивание данных.

  3. Нажмите клавишу F5 для сборки и запуска приложения.

  4. Перетащите один из элементов управления Circle над панелями, другим элементом Circle и полем TextBox. Обратите внимание, что теперь вместо обычных курсоров используются пользовательские, заданные в переопределении OnGiveFeedback.

    Drag and drop with custom cursors

  5. Выделите текст green из поля TextBox.

  6. Перетащите текст green в элемент управления Circle. Обратите внимание, что отображаются курсоры по умолчанию: они указывают на результаты операции перетаскивания. Курсор обратной связи всегда задается источником перетаскивания.

Реализация событий целевого объекта перетаскивания в пользовательском элементе управления

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

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

  1. Откройте файл Circle.xaml.

  2. В открывающем теге UserControl добавьте свойство AllowDrop и присвойте ему значение true.

    <UserControl x:Class="DragDropWalkthrough.Circle"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300"
                 AllowDrop="True">
    

Метод OnDrop вызывается, когда свойству AllowDrop задано значение true и данные исходного объекта перетаскивания сброшены на элемент управления Circle. В этом методе вы обработаете перемещенные данные и примените их к элементу управления Circle.

Обработка вставляемых данных

  1. Откройте файл Circle.xaml.cs или Circle.xaml.vb.

  2. Добавьте следующее переопределение OnDrop, чтобы обеспечить обработку классом события Drop.

    protected override void OnDrop(DragEventArgs e)
    {
        base.OnDrop(e);
    
        // 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);
                circleUI.Fill = newFill;
    
                // Set Effects to notify the drag source what effect
                // the drag-and-drop operation had.
                // (Copy if CTRL is pressed; otherwise, move.)
                if (e.KeyStates.HasFlag(DragDropKeyStates.ControlKey))
                {
                    e.Effects = DragDropEffects.Copy;
                }
                else
                {
                    e.Effects = DragDropEffects.Move;
                }
            }
        }
        e.Handled = true;
    }
    
    Protected Overrides Sub OnDrop(ByVal e As System.Windows.DragEventArgs)
        MyBase.OnDrop(e)
        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString As String = e.Data.GetData(DataFormats.StringFormat)
    
            ' If the string can be converted into a Brush, 
            ' convert it and apply it to the ellipse.
            Dim converter As New BrushConverter
            If converter.IsValid(dataString) Then
                Dim newFill As Brush = converter.ConvertFromString(dataString)
                circleUI.Fill = newFill
    
                ' Set Effects to notify the drag source what effect
                ' the drag-and-drop operation had.
                ' (Copy if CTRL is pressed; otherwise, move.)
                If e.KeyStates.HasFlag(DragDropKeyStates.ControlKey) Then
                    e.Effects = DragDropEffects.Copy
                Else
                    e.Effects = DragDropEffects.Move
                End If
            End If
        End If
        e.Handled = True
    End Sub
    

    Переопределение OnDrop выполняет следующие задачи.

    • Использует метод GetDataPresent для проверки, не содержится ли в перенесенных данных строковый объект.

    • Использует метод GetData для извлечения строковых данных, если таковые имеются.

    • Использует BrushConverter в попытке преобразования строки в структуру Brush.

    • Если преобразование прошло успешно, применяет кисть методу Fill эллипса Ellipse, который предоставляет пользовательский интерфейс для элемента управления Circle.

    • Помечает событие Drop как обработанное. Событие сброса необходимо пометить как обработанное, чтобы другие элементы, которые получают это событие, знали, что событие было обработано пользовательским элементом управления Circle.

  3. Нажмите клавишу F5 для сборки и запуска приложения.

  4. Выделите текст green в поле TextBox.

  5. Перетащите текст в элемент управления Circle. Элемент управления Circle поменяет цвет с синего на зеленый.

    Convert a string to a brush

  6. Введите текст green в поле TextBox.

  7. Выделите текст gre в поле TextBox.

  8. Перетащите его в элемент управления Circle. Обратите внимание, что курсор изменяется, чтобы указать, что перенос разрешен, но цвет элемента управления Circle не меняется, потому что gre — недопустимый цвет.

  9. Выполните перетаскивание с зеленого элемента управления Circle на синий. Элемент управления Circle поменяет цвет с синего на зеленый. Обратите внимание, что отображаемый курсор зависит от того, что является источником перетаскивания: поле TextBox или элемент управления Circle.

Присвоение свойству AllowDrop значения true и обработка перенесенных данных — это все, что необходимо, чтобы сделать элемент целевым объектом перетаскивания. Однако, для обеспечения лучшего взаимодействия с пользователем рекомендуется обработка событий DragEnter, DragLeave и DragOver. В этих событиях можно выполнять проверки и предоставлять дополнительную обратную связь пользователю до сброса данных.

При перетаскивании данных над пользовательским элементом управления Circle элемент управления должен уведомить источник перетаскивания, может ли он обработать переносимые данные. Если элемент управления не знает, как обрабатывать данные, он должен отклонить перетаскивание. Для этого необходимо обработать событие DragOver и задать свойство Effects.

Проверка допустимости сброса данных

  1. Откройте файл Circle.xaml.cs или Circle.xaml.vb.

  2. Добавьте следующее переопределение OnDragOver, чтобы обеспечить обработку классом события DragOver.

    protected override void OnDragOver(DragEventArgs e)
    {
        base.OnDragOver(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 or moving.
            BrushConverter converter = new BrushConverter();
            if (converter.IsValid(dataString))
            {
                // Set Effects to notify the drag source what effect
                // the drag-and-drop operation will have. These values are
                // used by the drag source's GiveFeedback event handler.
                // (Copy if CTRL is pressed; otherwise, move.)
                if (e.KeyStates.HasFlag(DragDropKeyStates.ControlKey))
                {
                    e.Effects = DragDropEffects.Copy;
                }
                else
                {
                    e.Effects = DragDropEffects.Move;
                }
            }
        }
        e.Handled = true;
    }
    
    Protected Overrides Sub OnDragOver(ByVal e As System.Windows.DragEventArgs)
        MyBase.OnDragOver(e)
        e.Effects = DragDropEffects.None
    
        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString As String = e.Data.GetData(DataFormats.StringFormat)
    
            ' If the string can be converted into a Brush, allow copying or moving.
            Dim converter As New BrushConverter
            If converter.IsValid(dataString) Then
                ' Set Effects to notify the drag source what effect
                ' the drag-and-drop operation will have. These values are 
                ' used by the drag source's GiveFeedback event handler.
                ' (Copy if CTRL is pressed; otherwise, move.)
                If e.KeyStates.HasFlag(DragDropKeyStates.ControlKey) Then
                    e.Effects = DragDropEffects.Copy
                Else
                    e.Effects = DragDropEffects.Move
                End If
            End If
        End If
        e.Handled = True
    End Sub
    

    Переопределение OnDragOver выполняет следующие задачи.

    • Задает для свойства Effects значение None.

    • Выполняет те же проверки, что и в методе OnDrop, чтобы определить, может ли пользовательский элемент управления Circle обработать перенесенные данные.

    • Если пользовательский элемент управления может обработать данные, установите для свойства Effects значение Copy или Move.

  3. Нажмите клавишу F5 для сборки и запуска приложения.

  4. Выделите текст gre в поле TextBox.

  5. Перетащите текст в элемент управления Circle. Обратите внимание, что курсор изменяется, чтобы указать, что перетаскивание не разрешено, потому что gre не является допустимым цветом.

Использование предварительного просмотра при перетаскивании повышает удобство работы пользователей. Для элемента управления Circle вы переопределите методы OnDragEnter и OnDragLeave. Когда данные перетаскиваются над элементом управления, текущий фон Fill сохраняется в переменной-заполнителе. Затем строка преобразуется в кисть и применяется к объекту Ellipse, предоставляющему пользовательский интерфейс элемента Circle. Если данные перетаскиваются за пределы элемента управления Circle без сброса, исходное значение Fill повторно применяется к элементу управления Circle.

Предварительный просмотр результатов операции перетаскивания

  1. Откройте файл Circle.xaml.cs или Circle.xaml.vb.

  2. В классе Circle объявите закрытую переменную Brush с именем _previousFill и инициализируйте ее в null.

    public partial class Circle : UserControl
    {
        private Brush _previousFill = null;
    
    Public Class Circle
        Private _previousFill As Brush = Nothing
    
  3. Добавьте следующее переопределение OnDragEnter, чтобы обеспечить обработку классом события DragEnter.

    protected override void OnDragEnter(DragEventArgs e)
    {
        base.OnDragEnter(e);
        // Save the current Fill brush so that you can revert back to this value in DragLeave.
        _previousFill = circleUI.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.ToString());
                circleUI.Fill = newFill;
            }
        }
    }
    
    Protected Overrides Sub OnDragEnter(ByVal e As System.Windows.DragEventArgs)
        MyBase.OnDragEnter(e)
        _previousFill = circleUI.Fill
    
        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString As String = 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 = converter.ConvertFromString(dataString)
                circleUI.Fill = newFill
            End If
        End If
        e.Handled = True
    End Sub
    

    Переопределение OnDragEnter выполняет следующие задачи.

    • Сохраняет свойство Fill класса Ellipse в переменной _previousFill.

    • Выполняет те же проверки, что и в методе OnDrop, чтобы определить, можно ли преобразовать данные в объект Brush.

    • Если данные преобразуются в допустимый объект Brush, они применяются к свойству Fill класса Ellipse.

  4. Добавьте следующее переопределение OnDragLeave, чтобы обеспечить обработку классом события DragLeave.

    protected override void OnDragLeave(DragEventArgs e)
    {
        base.OnDragLeave(e);
        // Undo the preview that was applied in OnDragEnter.
        circleUI.Fill = _previousFill;
    }
    
    Protected Overrides Sub OnDragLeave(ByVal e As System.Windows.DragEventArgs)
        MyBase.OnDragLeave(e)
        ' Undo the preview that was applied in OnDragEnter.
        circleUI.Fill = _previousFill
    End Sub
    

    Переопределение OnDragLeave выполняет следующие задачи.

    • Применяет значение Brush, сохраненное в переменной _previousFill, для свойства Fill объекта Ellipse, который обеспечивает пользовательский интерфейс элемента управления Circle.
  5. Нажмите клавишу F5 для сборки и запуска приложения.

  6. Выделите текст green в поле TextBox.

  7. Перетащите текст над элементом управления Circle, не сбрасывая его. Элемент управления Circle поменяет цвет с синего на зеленый.

    Preview the effects of a drag-and-drop operation

  8. Перетащите текст от элемента управления Circle. Элемент управления Circle изменится с зеленого на синий.

Настройка панели для получения перемещенных данных

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

  1. Откройте файл MainWindow.xaml.

  2. Как показано в приведенном ниже фрагменте XAML, в каждом из элементов управления StackPanel добавляются обработчики событий DragOver и Drop. Присвойте обработчику события DragOver имя panel_DragOver, а обработчику события Drop — имя panel_Drop.

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

    <StackPanel Grid.Column="0"
                Background="Beige"
                AllowDrop="True"
                DragOver="panel_DragOver"
                Drop="panel_Drop">
        <TextBox Width="Auto" Margin="2"
                 Text="green"/>
        <local:Circle Margin="2" />
        <local:Circle Margin="2" />
    </StackPanel>
    <StackPanel Grid.Column="1"
                Background="Bisque"
                AllowDrop="True"
                DragOver="panel_DragOver"
                Drop="panel_Drop">
    </StackPanel>
    
  3. Откройте файл MainWindows.xaml.cs или MainWindow.xaml.vb.

  4. Добавьте следующий код в обработчик событий DragOver.

    private void panel_DragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent("Object"))
        {
            // These Effects values are used in the drag source's
            // GiveFeedback event handler to determine which cursor to display.
            if (e.KeyStates == DragDropKeyStates.ControlKey)
            {
                e.Effects = DragDropEffects.Copy;
            }
            else
            {
                e.Effects = DragDropEffects.Move;
            }
        }
    }
    
    Private Sub panel_DragOver(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
        If e.Data.GetDataPresent("Object") Then
            ' These Effects values are used in the drag source's
            ' GiveFeedback event handler to determine which cursor to display.
            If e.KeyStates = DragDropKeyStates.ControlKey Then
                e.Effects = DragDropEffects.Copy
            Else
                e.Effects = DragDropEffects.Move
            End If
        End If
    End Sub
    

    Обработчик событий DragOver выполняет следующие задачи.

    • Проверяет наличие в перемещенных сведениях данных типа "Объект", которые были упакованы в DataObject пользовательским элементом Circle и переданы в вызове метода DoDragDrop.

    • Если данные типа "Объект" присутствуют, обработчик проверяет нажата ли клавиша Ctrl.

    • Если клавиша Ctrl нажата, присваивает свойству Effects значение Copy. Иначе присваивает свойству Effects значение Move.

  5. Добавьте следующий код в обработчик событий Drop.

    private void panel_Drop(object sender, DragEventArgs e)
    {
        // If an element in the panel has already handled the drop,
        // the panel should not also handle it.
        if (e.Handled == false)
        {
            Panel _panel = (Panel)sender;
            UIElement _element = (UIElement)e.Data.GetData("Object");
    
            if (_panel != null && _element != null)
            {
                // Get the panel that the element currently belongs to,
                // then remove it from that panel and add it the Children of
                // the panel that its been dropped on.
                Panel _parent = (Panel)VisualTreeHelper.GetParent(_element);
    
                if (_parent != null)
                {
                    if (e.KeyStates == DragDropKeyStates.ControlKey &&
                        e.AllowedEffects.HasFlag(DragDropEffects.Copy))
                    {
                        Circle _circle = new Circle((Circle)_element);
                        _panel.Children.Add(_circle);
                        // set the value to return to the DoDragDrop call
                        e.Effects = DragDropEffects.Copy;
                    }
                    else if (e.AllowedEffects.HasFlag(DragDropEffects.Move))
                    {
                        _parent.Children.Remove(_element);
                        _panel.Children.Add(_element);
                        // set the value to return to the DoDragDrop call
                        e.Effects = DragDropEffects.Move;
                    }
                }
            }
        }
    }
    
    Private Sub panel_Drop(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
        ' If an element in the panel has already handled the drop,
        ' the panel should not also handle it.
        If e.Handled = False Then
            Dim _panel As Panel = sender
            Dim _element As UIElement = e.Data.GetData("Object")
    
            If _panel IsNot Nothing And _element IsNot Nothing Then
                ' Get the panel that the element currently belongs to,
                ' then remove it from that panel and add it the Children of
                ' the panel that its been dropped on.
    
                Dim _parent As Panel = VisualTreeHelper.GetParent(_element)
                If _parent IsNot Nothing Then
                    If e.KeyStates = DragDropKeyStates.ControlKey And _
                        e.AllowedEffects.HasFlag(DragDropEffects.Copy) Then
                        Dim _circle As New Circle(_element)
                        _panel.Children.Add(_circle)
                        ' set the value to return to the DoDragDrop call
                        e.Effects = DragDropEffects.Copy
                    ElseIf e.AllowedEffects.HasFlag(DragDropEffects.Move) Then
                        _parent.Children.Remove(_element)
                        _panel.Children.Add(_element)
                        ' set the value to return to the DoDragDrop call
                        e.Effects = DragDropEffects.Move
                    End If
                End If
            End If
        End If
    End Sub
    

    Обработчик событий Drop выполняет следующие задачи.

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

    • Если событие Drop не обработано, обработчик проверяет, нажата ли клавиша Ctrl.

    • Если клавиша Ctrl нажата, когда происходит событие Drop, создает копию элемента управления Circle и добавляет ее в коллекцию Children панели StackPanel.

    • Если клавиша Ctrl не нажата, перемещает Circle из коллекции Children родительской панели в коллекцию Children панели, на которую было осуществлено перемещение.

    • Задает свойство Effects, чтобы уведомить метод DoDragDrop о том, что операция перемещения или копирования завершилась.

  6. Нажмите клавишу F5 для сборки и запуска приложения.

  7. Выделите текст green из поля TextBox.

  8. Перетащите текст над элементом управления Circle и опустите его на элемент.

  9. Перетащите элемент управления Circle из левой панели в правую панель и опустите его. Элемент управления Circle удаляется из коллекции Children левой панели и добавляется в коллекцию дочерних элементов на правой панели.

  10. Перетащите элемент управления Circle с панели, на которой он находится, на другую панель и опустите, не отпуская клавишу Ctrl. Элемент управления Circle копируется, после чего копия добавляется в коллекцию Children принимающей панели.

    Dragging a Circle while pressing the CTRL key

См. также