Compartilhar via


Passo a passo: habilitando arrastar e soltar em um controle de usuário

Este passo a passo demonstra como criar um controle de usuário personalizado que pode participar da transferência de dados de arrastar e soltar no WPF (Windows Presentation Foundation).

Neste passo a passo, você criará um UserControl WPF personalizado que representa uma forma de círculo. Você implementará a funcionalidade no controle para habilitar a transferência de dados por meio de arrastar e soltar. Por exemplo, se você arrastar de um controle do círculo para outro, os dados de cor de preenchimento serão copiados do círculo de origem para o destino. Se você arrastar de um controle Circle para um TextBox, a representação da string da cor de preenchimento será copiada para o TextBox. Você também criará um aplicativo pequeno que contém dois controles de painel e um TextBox para testar a funcionalidade de arrastar e soltar. Será gravado um código que permite que os painéis processem os dados do círculo que foram soltos, o que permitirá mover ou copiar círculos da coleção de filhos de um painel para o outro.

Este passo a passo ilustra as seguintes tarefas:

  • Crie um controle de usuário personalizado.

  • Habilite o controle de usuário como uma fonte de arrastar.

  • Habilite o controle de usuário como um destino de soltar.

  • Habilite um painel para receber dados que foram soltos pelo controle de usuário.

Pré-requisitos

Você precisa do Visual Studio para concluir este passo a passo.

Criar o projeto de aplicativo

Nesta seção, você criará a infraestrutura do aplicativo, que inclui uma página principal com dois painéis e um TextBox.

  1. Crie um novo projeto de aplicativo WPF no Visual Basic ou no Visual C# chamado DragDropExample. Para obter mais informações, consulte Passo a passo: Meu Primeiro Aplicativo Desktop WPF.

  2. Abra MainWindow.xaml.

  3. Adicione a marcação a seguir entre as marcas de abertura e fechamento Grid.

    Essa marcação cria a interface do usuário para o aplicativo de teste.

    <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>
    

Adicionar um novo controle de usuário ao projeto

Nesta seção, você adicionará um novo controle de usuário ao projeto.

  1. No menu Projeto, selecione Adicionar controle de usuário.

  2. Na caixa de diálogo Adicionar Novo Item, altere o nome para Circle.xamle clique em Adicionar.

    O Circle.xaml e seu code-behind são adicionados ao projeto.

  3. Abra o Circle.xaml.

    Este arquivo conterá os elementos da interface de usuário do controle do usuário.

  4. Adicione as marcações a seguir à raiz Grid para criar um controle de usuário simples que tenha um círculo azul como seu UI.

    <Ellipse x:Name="circleUI" 
             Height="100" Width="100"
             Fill="Blue" />
    
  5. Abra Circle.xaml.cs ou Circle.xaml.vb.

  6. Em C#, adicione o código a seguir após o construtor sem parâmetros para criar um construtor de cópia. No Visual Basic, adicione o código a seguir para criar um construtor sem parâmetros e um construtor de cópia.

    Para permitir que o controle de usuário seja copiado, você pode adicionar um método de construtor de cópia no arquivo code-behind. No controle de usuário simplificado do círculo, será copiado apenas o preenchimento e o tamanho do controle de usuário.

    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
    

Adicionar o controle de usuário à janela principal

  1. Abra MainWindow.xaml.

  2. Adicione o XAML a seguir à marca de abertura Window para criar uma referência de namespace XML ao aplicativo atual.

    xmlns:local="clr-namespace:DragDropExample"
    
  3. No primeiro StackPanel, adicione o XAML a seguir para criar duas instâncias do controle de usuário Circle no primeiro painel.

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

    O XAML completo para o painel é semelhante ao seguinte.

    <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>
    

Implementar eventos de origem de arrasto no controle de usuário

Nesta seção, você substituirá o método OnMouseMove e iniciará a operação de arrastar e soltar.

Se uma operação de arrasto for iniciada (um botão do mouse será pressionado e o mouse será movido), você empacotará os dados para serem transferidos para um DataObject. Nesse caso, o controle do círculo empacotará três itens de dados; uma representação de cadeia de caracteres da cor de preenchimento, uma representação dupla da altura e uma cópia de si mesmo.

Para iniciar uma operação do tipo “arrastar e soltar”

  1. Abra Circle.xaml.cs ou Circle.xaml.vb.

  2. Adicione o seguinte override OnMouseMove para fornecer tratamento de classe para o evento 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
    

    Essa substituição OnMouseMove executa as seguintes tarefas:

    • Verifica se o botão esquerdo do mouse é pressionado enquanto o mouse está se movendo.

    • Empacota os dados do Círculo em um DataObject. Nesse caso, o controle do círculo empacota três itens de dados; uma representação de cadeia de caracteres da cor de preenchimento, uma representação dupla da altura e uma cópia de si mesmo.

    • Chama o método DragDrop.DoDragDrop estático para iniciar a operação de arrastar e soltar. Você passa os três parâmetros a seguir para o método DoDragDrop:

      • dragSource – uma referência a esse controle.

      • data – o DataObject criado no código anterior.

      • allowedEffects – as operações de arrastar e soltar permitidas, que são Copy ou Move.

  3. Pressione F5 para compilar e executar o aplicativo.

  4. Clique em um dos controles Círculo e arraste-o sobre os painéis, o outro Círculo e o TextBox. Ao arrastar sobre a TextBox, o cursor é alterado para indicar uma movimentação.

  5. Enquanto arrasta um Círculo sobre a TextBox, pressione a tecla Ctrl. Observe como o cursor é alterado para indicar uma cópia.

  6. Arraste e solte um Círculo no TextBox. A representação de cadeia de caracteres da cor de preenchimento do círculo é acrescentada ao TextBox.

    Representação de cadeia de caracteres da cor de preenchimento do Círculo

Por padrão, o cursor será alterado durante uma operação de arrastar e soltar para indicar qual efeito a remoção dos dados terá. Você pode personalizar os comentários dados ao usuário manipulando o evento GiveFeedback e definindo um cursor diferente.

Faça comentários ao usuário

  1. Abra Circle.xaml.cs ou Circle.xaml.vb.

  2. Adicione o seguinte override OnGiveFeedback para fornecer tratamento de classe para o evento 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
    

    Essa substituição OnGiveFeedback executa as seguintes tarefas:

    • Verifica os valores de Effects definidos no manipulador de eventos DragOver do ponto de soltura.

    • Define um cursor personalizado com base no valor Effects. O cursor destina-se a fornecer comentários visuais ao usuário sobre o efeito que a remoção dos dados terá.

  3. Pressione F5 para compilar e executar o aplicativo.

  4. Arraste um dos controles Círculo sobre os painéis, o outro Círculo e o TextBox. Note que os cursores agora são personalizados conforme especificado no parâmetro de substituição OnGiveFeedback.

    Arrastar e soltar com cursores personalizados

  5. Selecione o texto green do TextBox.

  6. Arraste o texto green para um controle do círculo. Observe que os cursores padrão são mostrados para indicar os efeitos da operação de arrastar e soltar. O cursor de comentários sempre é definido pela fonte de arrastar.

Implementar eventos de destino de soltar no controle de usuário

Nesta seção, você especificará que o controle do usuário é um alvo de soltar, substituirá os métodos que permitem que o controle do usuário seja um alvo de soltar e processará os dados que são soltos nele.

Para permitir que o controle de usuário seja um destino de soltar

  1. Abra o Circle.xaml.

  2. Na etiqueta de abertura UserControl, adicione a propriedade AllowDrop e defina-a como 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">
    

O método OnDrop é chamado quando a propriedade AllowDrop é definida como true e os dados da fonte de arrastar são descartados no controle de usuário do Círculo. Nesse método, você processará os dados que foram descartados e aplicará os dados ao Círculo.

Para processar os dados descartados

  1. Abra Circle.xaml.cs ou Circle.xaml.vb.

  2. Adicione o seguinte override OnDrop para fornecer tratamento de classe para o evento 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
    

    Essa substituição OnDrop executa as seguintes tarefas:

    • Usa o método GetDataPresent para verificar se os dados arrastados contêm um objeto de cadeia de caracteres.

    • Usa o método GetData para extrair os dados da cadeia de caracteres se eles estiverem presentes.

    • Usa um BrushConverter para tentar converter a cadeia de caracteres em um Brush.

    • Se a conversão for bem-sucedida, aplica o pincel à Fill do Ellipse que fornece a interface do controle Circle.

    • Marca o evento Drop como manipulado. O evento de soltar deve ser marcado como manipulado para que os outros elementos que o recebem saibam que foi manipulado pelo controle de usuário do círculo.

  3. Pressione F5 para compilar e executar o aplicativo.

  4. Selecione o texto green no TextBox.

  5. Arraste o texto para um controle Círculo e solte-o. O Círculo muda de azul para verde.

    Converter uma cadeia de caracteres em um pincel

  6. Digite o texto green no TextBox.

  7. Selecione o texto gre no TextBox.

  8. Arraste-o para um controle de Círculo e solte-o. Observe que o cursor é alterado para indicar que a queda é permitida, mas a cor do Círculo não é alterada porque gre não é uma cor válida.

  9. Arraste do controle do círculo verde e solte no controle do círculo azul. O Círculo muda de azul para verde. Observe que o cursor mostrado depende de se o TextBox ou o Círculo é a origem do arraste.

Definir a propriedade AllowDrop para true e processar os dados descartados é tudo o que é necessário para permitir que um elemento seja um destino de descarte. No entanto, para fornecer uma melhor experiência do usuário, você também deve lidar com os eventos DragEnter, DragLeavee DragOver. Nesses eventos, você pode executar verificações e fornecer comentários adicionais ao usuário antes que os dados sejam removidos.

Quando dados são arrastados sobre o controle de usuário do círculo, o controle deve notificar a fonte de arrasto se os dados arrastados podem ser processados. Se o controle não souber como processar os dados, deverá recusá-los quando forem soltos. Para fazer isso, você manipulará o evento DragOver e definirá a propriedade Effects.

Para verificar se é permitido soltar os dados

  1. Abra Circle.xaml.cs ou Circle.xaml.vb.

  2. Adicione o seguinte override OnDragOver para fornecer tratamento de classe para o evento 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
    

    Essa substituição OnDragOver executa as seguintes tarefas:

    • Define a propriedade Effects como None.

    • Executa as mesmas verificações executadas no método OnDrop para determinar se o controle de usuário do Círculo pode processar os dados arrastados.

    • Se o controle do usuário puder processar os dados, definirá a propriedade Effects como Copy ou Move.

  3. Pressione F5 para compilar e executar o aplicativo.

  4. Selecione o texto gre no TextBox.

  5. Arraste o texto para um controle do círculo. Observe que, agora, o cursor muda para indicar que não é permitido soltar, porque gre não é uma cor válida.

É possível aprimorar ainda mais a experiência do usuário por meio da aplicação de uma visualização da operação do tipo “soltar”. Para o controle de usuário do Círculo, você substituirá os métodos OnDragEnter e OnDragLeave. Quando os dados são arrastados sobre o controle, a tela de fundo atual Fill é salva em uma variável de espaço reservado. A seguir, a cadeia de caracteres é convertida em um pincel e aplicada a Ellipse que fornece a interface do usuário do círculo. Se o dado for arrastado para fora do Círculo sem ser descartado, o valor original de Fill é reaplicado ao Círculo.

Para visualizar os efeitos da operação de arrastar e soltar

  1. Abra Circle.xaml.cs ou Circle.xaml.vb.

  2. Na classe Circle, declare uma variável de Brush privada chamada _previousFill e inicialize-a para null.

    public partial class Circle : UserControl
    {
        private Brush _previousFill = null;
    
    Public Class Circle
        Private _previousFill As Brush = Nothing
    
  3. Adicione o seguinte override OnDragEnter para fornecer tratamento de classe para o evento 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
    

    Essa substituição OnDragEnter executa as seguintes tarefas:

    • Salva a propriedade Fill do Ellipse na variável _previousFill.

    • Executa as mesmas verificações executadas no método OnDrop para determinar se os dados podem ser convertidos em um Brush.

    • Se os dados forem convertidos em um Brush válido, aplique-o ao Fill da Ellipse.

  4. Adicione o seguinte override OnDragLeave para fornecer tratamento de classe para o evento 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
    

    Essa substituição OnDragLeave executa as seguintes tarefas:

    • Aplica o Brush salvo na variável _previousFill ao Fill da Ellipse que fornece a interface de usuário do controle de usuário Círculo.
  5. Pressione F5 para compilar e executar o aplicativo.

  6. Selecione o texto green no TextBox.

  7. Arraste o texto sobre um controle de círculo sem soltá-lo. O Círculo muda de azul para verde.

    Visualizar os efeitos de uma operação de arrastar e soltar

  8. Arraste o texto para longe do controle Circle. O Círculo muda de verde de volta para azul.

Habilitar um painel para receber dados via soltar

Nesta seção, você permite que os painéis que hospedam os controles de usuário do Círculo atuem como destinos de soltura para os dados de Círculo arrastados. Você implementará um código que permite mover um Círculo de um painel para outro ou fazer uma cópia de um controle Circle mantendo pressionada a tecla Ctrl enquanto arrasta e solta um Círculo.

  1. Abra MainWindow.xaml.

  2. Conforme mostrado no XAML a seguir, em cada um dos controles de StackPanel, adicione manipuladores para os eventos DragOver e Drop. Nomeie o manipulador de eventos DragOver, panel_DragOvere nomeie o manipulador de eventos Drop, panel_Drop.

    Por padrão, os painéis não são destinos de soltura. Para habilitá-los, adicione a propriedade AllowDrop a ambos os painéis e defina o valor como 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. Abra MainWindows.xaml.cs ou MainWindow.xaml.vb.

  4. Adicione o código a seguir para o manipulador de eventos 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
    

    Esse manipulador de eventos DragOver executa as seguintes tarefas:

    • Verifica se os dados arrastados contêm os dados de “objeto” que foram empacotados no DataObject pelo controle de usuário do círculo e passados na chamada para DoDragDrop.

    • Se os dados de "Objeto" estiverem presentes, verifique se a tecla Ctrl está pressionada.

    • Se a tecla Ctrl for pressionada, definirá a propriedade Effects como Copy. Caso contrário, defina a propriedade Effects como Move.

  5. Adicione o código a seguir para o manipulador de eventos 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
    

    Esse manipulador de eventos Drop executa as seguintes tarefas:

    • Verifica se o evento Drop já foi tratado. Por exemplo, se um círculo foi solto em outro círculo que manipula o evento Drop, não deixe que o painel que contém o círculo também o manipule.

    • Se o evento Drop não for tratado, verifique se a tecla Ctrl é pressionada.

    • Se a tecla Ctrl for pressionada quando o Drop acontecer, faça uma cópia do controle Círculo e adicione-a à coleção Children do StackPanel.

    • Se a tecla Ctrl não for pressionada, move o Círculo da coleção Children do painel pai dele para a coleção Children do painel em que ele foi solto.

    • Define a propriedade Effects para notificar o método DoDragDrop se uma operação de movimentação ou cópia foi executada.

  6. Pressione F5 para compilar e executar o aplicativo.

  7. Selecione o texto green do TextBox.

  8. Arraste o texto sobre um controle Circular e solte-o.

  9. Arraste um controle Circle do painel esquerdo para o painel direito e solte-o. O Círculo é removido da coleção Children do painel esquerdo e adicionado à coleção Children do painel direito.

  10. Arraste um controle Circle do painel no qual ele está para o outro painel e solte-o mantendo pressionada a tecla Ctrl. O círculo é copiado e a cópia é adicionada à coleção Children do painel que o recebe.

    arrastar um círculo enquanto pressiona a tecla CTRL

Consulte também