Compartir a través de


Tutorial: Habilitar la técnica de arrastrar y colocar en un control de usuario

En este tutorial se muestra cómo crear un control de usuario personalizado que pueda participar en la transferencia de datos de arrastrar y colocar en Windows Presentation Foundation (WPF).

En este tutorial, creará un objeto UserControl de WPF personalizado que representa una forma circular. Implementará la funcionalidad del control para habilitar la transferencia de datos mediante la técnica de arrastrar y colocar. Por ejemplo, si arrastra de un control de círculo a otro, se copian los datos del color de relleno del círculo de origen al de destino. Si arrastra un control Circle a un objeto TextBox, la representación de cadena del color de relleno se aplica a TextBox. También creará una pequeña aplicación con dos controles de panel y un objeto TextBox para probar la funcionalidad de arrastrar y colocar. Escribirá código para que los paneles puedan procesar los datos del círculo colocado, lo que le permitirá mover o copiar círculos de la colección secundaria de un panel a otro.

En este tutorial se muestran las tareas siguientes:

  • Crear un control de usuario personalizado.

  • Permitir que el control de usuario sea un origen de arrastre.

  • Permitir que el control de usuario sea un destino de colocación.

  • Permitir que un panel reciba los datos que se colocan desde el control de usuario.

Prerrequisitos

Necesita Visual Studio para completar este tutorial.

Creación del proyecto de aplicación

En esta sección, creará la infraestructura de la aplicación, que incluye una página principal con dos paneles y un objeto TextBox.

  1. Cree un proyecto de aplicación de WPF en Visual Basic o Visual C# denominado DragDropExample. Para obtener más información, vea Tutorial: Mi primera aplicación de escritorio WPF.

  2. Abra MainWindow.xaml.

  3. Agregue el marcado siguiente en las etiquetas Grid de apertura y cierre.

    Este marcado crea la interfaz de usuario de la aplicación de prueba.

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

Adición de un nuevo control de usuario al proyecto

En esta sección, agregará un nuevo control de usuario al proyecto.

  1. En el menú Proyecto, seleccione Agregar control de usuario.

  2. En el cuadro de diálogo Agregar nuevo elemento, cambie el nombre a Circle.xaml y haga clic en Agregar.

    Circle.xaml y su código subyacente se agregan al proyecto.

  3. Abra Circle.xaml.

    Este archivo contendrá los elementos de la interfaz de usuario del control de usuario.

  4. Agregue el marcado siguiente al elemento Grid raíz para crear un control de usuario sencillo que tenga un círculo azul como interfaz de usuario.

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

  6. En C#, agregue el código siguiente después del constructor sin parámetros para crear un constructor de copias. En Visual Basic, agregue el código siguiente para crear un constructor sin parámetros y un constructor de copias.

    Para permitir que se copie el control de usuario, puede agregar un método de constructor de copias en el archivo de código subyacente. En el control de usuario de círculo simplificado, solo copiará el relleno y el tamaño del control de usuario.

    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
    

Adición del control de usuario a la ventana principal

  1. Abra MainWindow.xaml.

  2. Agregue el código XAML siguiente a la etiqueta Window de apertura para crear una referencia de espacio de nombres XML a la aplicación actual.

    xmlns:local="clr-namespace:DragDropExample"
    
  3. En el primer objeto StackPanel, agregue el código XAML siguiente para crear dos instancias del control de usuario Circle en el primer panel.

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

    El código XAML completo del panel tendrá el aspecto siguiente.

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

Implementación de eventos de origen de arrastre en el control de usuario

En esta sección, invalidará el método OnMouseMove e iniciará la operación de arrastrar y colocar.

Si se inicia un arrastre (se presiona un botón del mouse y se mueve), empaquetará los datos para que se transfieran a un objeto DataObject. En este caso, el control de círculo empaquetará tres elementos de datos: una representación de cadena de su color de relleno, una representación doble de su altura y una copia de sí mismo.

Para iniciar una operación de arrastrar y colocar

  1. Abra Circle.xaml.cs o Circle.xaml.vb.

  2. Agregue la invalidación de OnMouseMove siguiente para proporcionar control de clases al 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
    

    Esta invalidación de OnMouseMove realiza las tareas siguientes:

    • Comprueba si el botón primario del mouse está presionado mientras este se mueve.

    • Empaqueta los datos de Circle en un objeto DataObject. En este caso, el control de círculo empaqueta tres elementos de datos: una representación de cadena de su color de relleno, una representación doble de su altura y una copia de sí mismo.

    • Llama al método estático DragDrop.DoDragDrop para iniciar la operación de arrastrar y colocar. Debe pasar los tres parámetros siguientes al método DoDragDrop:

      • dragSource: una referencia a este control.

      • data: el objeto DataObject creado en el código anterior.

      • allowedEffects: las operaciones de arrastrar y colocar permitidas, que son Copy o Move.

  3. Presione F5 para compilar y ejecutar la aplicación.

  4. Haga clic en uno de los controles Circle y arrástrelo sobre los paneles, el otro control Circle y el elemento TextBox. Al arrastrar sobre TextBox, el cursor cambia para indicar un movimiento.

  5. Al arrastrar un control Circle sobre TextBox, presione la tecla Ctrl. Observe que el cursor cambia para indicar una copia.

  6. Arrastre y coloque un control Circle sobre TextBox. La representación de cadena del color de relleno de Circle se anexa al elemento TextBox.

    Representación de cadena del color de relleno de Circle

De forma predeterminada, el cursor cambiará durante una operación de arrastrar y colocar para indicar el efecto que tendrá la colocación de los datos. Puede personalizar la respuesta que se proporciona al usuario si controla el evento GiveFeedback y establece otro cursor.

Respuesta al usuario

  1. Abra Circle.xaml.cs o Circle.xaml.vb.

  2. Agregue la invalidación de OnGiveFeedback siguiente para proporcionar control de clases al 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
    

    Esta invalidación de OnGiveFeedback realiza las tareas siguientes:

    • Comprueba los valores Effects establecidos en el controlador de eventosDragOver del destino de la operación de colocar.

    • Establece un curso personalizado en función del valor Effects. El cursor está diseñado para proporcionar información visual al usuario sobre el efecto que tendrá la colocación de los datos.

  3. Presione F5 para compilar y ejecutar la aplicación.

  4. Arrastre uno de los controles Circle sobre los paneles, el otro control Circle y el elemento TextBox. Observe que ahora los cursores son los cursores personalizados que ha especificado en la invalidación de OnGiveFeedback.

    Arrastrar y colocar con cursores personalizados

  5. Seleccione el texto green de TextBox.

  6. Arrastre el texto green a un control de círculo. Observe que los cursores predeterminados se muestran para indicar los efectos de la operación de arrastrar y colocar. El origen de arrastre siempre establece el cursor de retroacción.

Implementación de eventos de destino de colocación en el control de usuario

En esta sección, especificará que el control de usuario es un destino de colocación, invalidará los métodos que permiten que el control de usuario sea un destino de colocación y procesará los datos que se colocan en él.

Para permitir que el control de usuario sea un destino de colocación

  1. Abra Circle.xaml.

  2. En la etiqueta UserControl de apertura, agregue la propiedad AllowDrop y establézcala en 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">
    

Se llama al método OnDrop cuando la propiedad AllowDrop se establece en true y los datos del origen de arrastre se colocan en el control de usuario Circle. En este método, procesará los datos que se han colocado y aplicará los datos al círculo.

Para procesar los datos colocados

  1. Abra Circle.xaml.cs o Circle.xaml.vb.

  2. Agregue la invalidación de OnDrop siguiente para proporcionar control de clases al 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
    

    Esta invalidación de OnDrop realiza las tareas siguientes:

    • Usa el método GetDataPresent para comprobar si los datos arrastrados contienen un objeto de cadena.

    • Usa el método GetData para extraer los datos de cadena, si existen.

    • Usa un objeto BrushConverter para intentar convertir la cadena en un objeto Brush.

    • Si la conversión es correcta, aplica el pincel al objeto Fill del elemento Ellipse que proporciona la interfaz de usuario del control Circle.

    • Marca el evento Drop como controlado. Debe marcar el evento de colocación como controlado para que los elementos que reciban este evento sepan que el control de usuario de círculo lo ha controlado.

  3. Presione F5 para compilar y ejecutar la aplicación.

  4. Seleccione el texto green en TextBox.

  5. Arrastre el texto a un control de círculo y colóquelo. El círculo cambia de azul a verde.

    Convertir una cadena en pincel

  6. Escriba el texto green en TextBox.

  7. Seleccione el texto gre en TextBox.

  8. Arrástrelo a un control de círculo y colóquelo. Observe que el cursor cambia para indicar que se permite la colocación, pero el color del círculo no cambia porque gre no es un color válido.

  9. Arrastre desde el control de círculo verde y coloque en el control de círculo azul. El círculo cambia de azul a verde. Observe que el cursor que se muestra depende de si el origen de arrastre es TextBox o el control Circle.

Lo único necesario para permitir que un elemento sea un destino de colocación es establecer la propiedad AllowDrop en true y procesar los datos colocados. Pero para proporcionar una experiencia de usuario mejor, también debe controlar los eventos DragEnter, DragLeave y DragOver. En estos eventos, puede realizar comprobaciones y proporcionar repuesta adicional al usuario antes de que se coloquen los datos.

Cuando los datos se arrastran sobre el control de usuario de círculo, el control debe notificarle al origen de arrastre si puede procesar los datos que se están arrastrando. Si el control no sabe cómo procesar los datos, debe rechazar la operación de colocar. Para ello, controlará el evento DragOver y establecerá la propiedad Effects.

Para comprobar que se permite la colocación de datos

  1. Abra Circle.xaml.cs o Circle.xaml.vb.

  2. Agregue la invalidación de OnDragOver siguiente para proporcionar control de clases al 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
    

    Esta invalidación de OnDragOver realiza las tareas siguientes:

    • Establece la propiedad Effects como None.

    • Realiza las mismas comprobaciones que en el método OnDrop para determinar si el control de usuario Circle puede procesar los datos arrastrados.

    • Si el control de usuario puede procesar los datos, establece la propiedad Effects en Copy o Move.

  3. Presione F5 para compilar y ejecutar la aplicación.

  4. Seleccione el texto gre en TextBox.

  5. Arrastre el texto a un control de círculo. Observe que ahora el cursor cambia para indicar que no se permite la colocación porque gre no es un color válido.

Puede mejorar la experiencia del usuario si aplica una vista previa de la operación de colocar. Para el control de usuario Circle, tendrá que invalidar los métodos OnDragEnter y OnDragLeave. Cuando los datos se arrastran sobre el control, el valor Fill del fondo actual se guarda en una variable de marcador de posición. Después, la cadena se convierte en un pincel y se aplica al elemento Ellipse que proporciona la interfaz de usuario de Circle. Si los datos se arrastran fuera del control Circle sin colocarse, se le vuelve a aplicar el valor Fill original.

Para obtener una vista previa de los efectos de la operación de arrastrar y colocar

  1. Abra Circle.xaml.cs o Circle.xaml.vb.

  2. En la clase Circle, declare una variable Brush privada denominada _previousFill e inicialícela en null.

    public partial class Circle : UserControl
    {
        private Brush _previousFill = null;
    
    Public Class Circle
        Private _previousFill As Brush = Nothing
    
  3. Agregue la invalidación de OnDragEnter siguiente para proporcionar control de clases al 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
    

    Esta invalidación de OnDragEnter realiza las tareas siguientes:

    • Guarda la propiedad Fill de Ellipse en la variable _previousFill.

    • Realiza las mismas comprobaciones que en el método OnDrop para determinar si los datos se pueden convertir en un objeto Brush.

    • Si los datos se convierten en un objeto Brush válido, se aplica a Fill de Ellipse.

  4. Agregue la invalidación de OnDragLeave siguiente para proporcionar control de clases al 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
    

    Esta invalidación de OnDragLeave realiza las tareas siguientes:

    • Aplica el valor Brush guardado en la variable _previousFill al valor Fill del objeto Ellipse que proporciona la interfaz de usuario para el control de usuario Circle.
  5. Presione F5 para compilar y ejecutar la aplicación.

  6. Seleccione el texto green en TextBox.

  7. Arrastre el texto sobre un control de círculo sin colocarlo. El círculo cambia de azul a verde.

    Vista previa de los efectos de una operación de arrastrar y colocar

  8. Arrastre el texto fuera del control de círculo. El círculo cambia de verde a azul.

Permiso para que un panel reciba datos colocados

En esta sección, habilitará los paneles que hospedan los controles de usuario Circle para que actúen como destinos de colocación de los datos de Circle arrastrados. Implementará código que le permita mover un control Circle de un panel a otro, o bien crear una copia de un control Circle manteniendo presionada la tecla Ctrl mientras arrastra y coloca un control Circle.

  1. Abra MainWindow.xaml.

  2. Como se muestra en el código XAML siguiente, en cada control StackPanel, agregue controladores para los eventos DragOver y Drop. Asigne el nombre panel_DragOveral controlador de eventos DragOver, y el nombre panel_Drop al controlador de eventos Drop.

    De manera predeterminada, los paneles no son destinos de colocación. Para habilitarlos, agregue la propiedad AllowDrop a ambos paneles y establezca el valor en 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 o MainWindow.xaml.vb.

  4. Agregue el código siguiente para el controlador 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
    

    Este controlador de eventos DragOver realiza las tareas siguientes:

    • Comprueba que los datos arrastrados contienen los datos "Object" que el control de usuario Circle ha empaquetado en DataObject y se han pasado en la llamada a DoDragDrop.

    • Si los datos "Object" están presentes, comprueba si se ha presionado la tecla Ctrl.

    • Si se ha presionado la tecla Ctrl, establece la propiedad Effects en Copy. De lo contrario, la propiedad Effects se establece en Move.

  5. Agregue el código siguiente para el controlador 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
    

    Este controlador de eventos Drop realiza las tareas siguientes:

    • Comprueba si el evento Drop ya se ha controlado. Por ejemplo, si se coloca un control Circle sobre otro que controla el Drop, no le interesa que el panel que contiene el control Circle también lo controle.

    • Si no se controla el evento Drop, comprueba si se ha presionado la tecla Ctrl.

    • Si se ha presionado la tecla Ctrl cuando se produce Drop, crea una copia del control Circle y la agrega a la colección Children de StackPanel.

    • Si no se ha presionado la tecla Ctrl, mueve el control Circle de la colección Children de su panel principal a la colección Children del panel en el que se ha colocado.

    • Establece la propiedad Effects para notificar al método DoDragDrop si se ha realizado una operación de movimiento o de copia.

  6. Presione F5 para compilar y ejecutar la aplicación.

  7. Seleccione el texto green de TextBox.

  8. Arrastre el texto sobre un control de círculo y colóquelo.

  9. Arrastre un control de círculo del panel izquierdo al panel derecho y colóquelo. El control Circle se quita de la colección Children del panel izquierdo y se agrega a la colección Children del panel derecho.

  10. Arrastre un control Circle del panel en el que se encuentra al otro panel y colóquelo mientras presiona la tecla Ctrl. El control Circle se copia y la copia se agrega a la colección Children del panel receptor.

    Arrastrar un control Circle mientras se presiona la tecla CTRL

Vea también