Instruções 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 Windows Presentation Foundation (WPF).

Neste passo a passo, você criará um WPF UserControl personalizado que representa uma forma de círculo. Você implementará a funcionalidade no controle para permitir 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 de cadeia de caracteres da cor Fill será copiada para o TextBox. Você também criará um pequeno aplicativo 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

É necessário o 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 TextBoxarquivo .

  1. Crie um novo projeto de aplicativo do WPF no Visual Basic ou no Visual C#, chamado DragDropExample. Para obter mais informações, confira Passo a passo: Meu primeiro aplicativo de área de trabalho do WPF.

  2. Abra MainWindow.xaml.

  3. Adicione a seguinte marcação entre as tags 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.

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

  4. Adicione a seguinte marcação à raiz Grid para criar um controle de usuário simples que tenha um círculo azul como sua interface do usuário.

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

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

    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 seguinte XAML à tag de abertura para criar uma referência de Window namespace XML para o aplicativo atual.

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

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

    O XAML completo para o painel tem a aparência a seguir.

    <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 arrastar no controle de usuário

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

Se um arrastar for iniciado (um botão do mouse é pressionado e o mouse é movido), você empacotará os dados a serem transferidos para um DataObjectarquivo . 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 a seguinte OnMouseMove substituição para fornecer manipulação de classe para o MouseMove evento.

    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 OnMouseMove substituição executa as seguintes tarefas:

    • Verifica se o botão esquerdo do mouse é pressionado enquanto o mouse se move.

    • Empacota os dados do Circle em um DataObjectarquivo . 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 estático DragDrop.DoDragDrop para iniciar a operação de arrastar e soltar. Você passa os três parâmetros a seguir para o DoDragDrop método:

      • dragSource – Uma referência para 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 o , o TextBoxcursor muda para indicar um movimento.

  5. Ao arrastar um Círculo sobre o TextBox, pressione a tecla Ctrl . Observe como o cursor muda para indicar uma cópia.

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

    String representation of Circle's fill color

Por padrão, o cursor será alterado durante uma operação do tipo “arrastar e soltar” para indicar qual será o efeito de soltar os dados. Você pode personalizar o feedback dado ao usuário manipulando o GiveFeedback evento e definindo um cursor diferente.

Dar feedback ao usuário

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

  2. Adicione a seguinte OnGiveFeedback substituição para fornecer manipulação de classe para o GiveFeedback evento.

    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 OnGiveFeedback substituição executa as seguintes tarefas:

    • Verifica os Effects valores definidos no manipulador de eventos do destino de DragOver descarte.

    • Define um cursor personalizado com base no Effects valor. O cursor deve fornecer comentários visuais ao usuário a respeito de qual será o efeito de soltar os dados.

  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 TextBoxarquivo . Observe que os cursores agora são os cursores personalizados que você especificou na OnGiveFeedback substituição.

    Drag and drop with custom cursors

  5. Selecione o texto green no 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 do tipo “arrastar e soltar”. O cursor de comentários sempre é definido pela fonte de arrastar.

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

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

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

  1. Abra o Circle.xaml.

  2. Na tag de abertura UserControl , adicione a propriedade e defina-a AllowDrop 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 OnDrop método é chamado quando a AllowDrop propriedade é definida como true e os dados da fonte de arraste são soltos no controle de usuário Circle. Nesse método, você processará os dados que foram soltos e aplicará os dados ao círculo.

Para processar os dados soltos

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

  2. Adicione a seguinte OnDrop substituição para fornecer manipulação de classe para o Drop evento.

    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 OnDrop substituição executa as seguintes tarefas:

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

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

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

    • Se a conversão for bem-sucedida, aplicará o Fill pincel ao do que fornece a Ellipse interface do usuário do controle Circle.

    • Marca o Drop evento 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 do círculo e solte-o. O círculo muda de azul para verde.

    Convert a string to a brush

  6. Digite o texto green no TextBoxarquivo .

  7. Selecione o texto gre no TextBox.

  8. Arraste-o para um controle do círculo e solte-o. Observe que o cursor muda para indicar que é permitido soltar; porém, a cor do círculo não muda, 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 se o ou o TextBox Círculo é a origem do arraste.

Definir a AllowDrop propriedade e true 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 DragEntereventos , DragLeavee DragOver . Nesses eventos, é possível executar verificações e fornecer comentários adicionais ao usuário antes de soltar os dados.

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 DragOver evento e definirá a Effects propriedade.

Para verificar se é permitido soltar os dados

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

  2. Adicione a seguinte OnDragOver substituição para fornecer manipulação de classe para o DragOver evento.

    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 OnDragOver substituição executa as seguintes tarefas:

    • Define a propriedade Effects como None.

    • Executa as mesmas verificações que são executadas no OnDrop método para determinar se o controle de usuário Circle pode processar os dados arrastados.

    • Se o controle de usuário puder processar os dados, defina a Effects propriedade 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 Circle, você substituirá os OnDragEnter métodos e OnDragLeave . Quando os dados são arrastados sobre o controle, o plano de fundo Fill atual é salvo em uma variável de espaço reservado. A cadeia de caracteres é convertida em um pincel e aplicada ao Ellipse que fornece a interface do usuário do Circle. Se os dados forem arrastados para fora do Círculo sem serem soltos, o valor original Fill será reaplicado ao Círculo.

Para visualizar os efeitos da operação do tipo “arrastar e soltar”

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

  2. Na classe Circle, declare uma variável privada Brush nomeada _previousFill e inicialize-a como null.

    public partial class Circle : UserControl
    {
        private Brush _previousFill = null;
    
    Public Class Circle
        Private _previousFill As Brush = Nothing
    
  3. Adicione a seguinte OnDragEnter substituição para fornecer manipulação de classe para o DragEnter evento.

    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 OnDragEnter substituição executa as seguintes tarefas:

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

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

    • Se os dados forem convertidos em um Brushválido, aplica-o Fill ao do Ellipse.

  4. Adicione a seguinte OnDragLeave substituição para fornecer manipulação de classe para o DragLeave evento.

    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 OnDragLeave substituição executa as seguintes tarefas:

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

  6. Selecione o texto green no TextBox.

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

    Preview the effects of a drag-and-drop operation

  8. Arraste o texto para fora do controle do círculo. O círculo volta de verde para azul.

Habilitar um painel para receber dados descartados

Nesta seção, você habilita os painéis que hospedam os controles de usuário do Circle para atuarem como destinos de soltar para dados arrastados do Circle. 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 StackPanel controles, adicione manipuladores para os DragOver eventos e Drop . Nomeie o manipulador de eventos e nomeie o DragOver manipulador de Drop eventos, panel_Drop. panel_DragOver

    Por padrão, os painéis não são destinos de descarte. Para habilitá-los, adicione a propriedade a AllowDrop 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 seguinte código para o manipulador de DragOver eventos.

    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 DragOver manipulador de eventos executa as seguintes tarefas:

    • Verifica se os dados arrastados contêm os dados "Objeto" que foram empacotados DataObject no pelo controle de usuário Circle 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, defina a Effects propriedade como Copy. Caso contrário, defina a Effects propriedade como Move.

  5. Adicione o seguinte código para o manipulador de Drop eventos.

    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 Drop manipulador de eventos executa as seguintes tarefas:

    • Verifica se o Drop evento já foi manipulado. Por exemplo, se um Círculo for descartado em outro Círculo que manipula o evento, você não deseja que o painel que contém o Drop Círculo também o manipule.

    • Se o Drop evento não for manipulado, verifica se a tecla Ctrl está pressionada.

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

    • Se a tecla Ctrl não for pressionada, mova o Círculo da Children coleção de seu painel pai para a Children coleção do painel no qual ele foi solto.

    • Define a Effects propriedade para notificar o DoDragDrop método 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 no TextBox.

  8. Arraste o texto sobre um controle do círculo e solte-o.

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

  10. Arraste um controle Circle do painel em que ele está para o outro painel e solte-o enquanto pressiona a tecla Ctrl . O Círculo é copiado e a cópia é adicionada Children à coleção do painel de recebimento.

    Dragging a Circle while pressing the CTRL key

Confira também