Saber cuando cambia el contenido del Portapapeles
Un botón "Pegar" que se habilita o deshabilita según el contenido del portapapeles

Fecha: 22/Jul/2004 (21/07/2004)
Autor: Pablo Daniel Tilli (tillipablo@hotmail.com)
 


Introducción

 

Este tutorial, nace como motivo de una función que necesitaba implementar en una aplicación sobre la que estuve trabajando hace ya algún tiempo. Concretamente lo que debía lograr es que haya un botón “Pegar” en una barra de herramientas, y que este sea sensible al contenido del portapapeles. El botón se debería habilitar cuando había un texto copiado en el portapapeles, pero sino debía estar deshabilitado. Un ejemplo de lo que digo, lo podemos ver en cualquier aplicación del Office como Word o Excel. Hecha esta introducción, ahora vamos a ver un poco como es el manejo del portapapeles de Windows.

 

 

La teoría

 

Lo que necesitamos hacer, es interceptar ciertos mensajes relacionados con el portapapeles de Windows que nos indiquen cuando hubo algún cambio en el mismo.

Es importante saber que Windows mantiene una lista de todas las aplicaciones que están “viendo el portapapeles”. Lo que quiero decir con esto, es que Windows considera un tipo especial de aplicaciones que denomina “Clipboard Viewer” (Visor del portapapeles), que es justamente el tipo de aplicación que necesitamos para nuestro proyecto. Windows nos proporciona a través de su API, algunas funciones para trabajar con este tipo de aplicaciones que nos harán las cosas bastante simples, pero igualmente debemos tomar algunos recaudos para que no haya problemas.

Como dije antes, Windows posee una lista de todas las aplicaciones del tipo “Clipboard Viewer”, pero lo llamativo, es que nuestra aplicación será la encargada de saber cual es la siguiente aplicación en la lista y entonces seremos nosotros los que debemos enviarle los mensajes del portapapeles. O sea, si nos llega un mensaje que nos avisa que se modifico el portapapeles, nosotros debemos mandarle a la próxima aplicación (Si existe) el mensaje que recibimos. También deberemos controlar el hecho de que la lista se modifique, por ejemplo, si la aplicación que nosotros tenemos como siguiente se cierra, deberemos apuntar a nuestra nueva “siguiente aplicación” (Si es que existe).

Calma, ya se que no se entiende mucho, pero cuando los veas codificado, toda será mas claro, y vas a ver que no es tan grave.

Bueno, basta de teoría y a escribir código.

 

 

El código

 

Vamos a escribir una clase que tendrá un método que nos permitirá comenzar a interceptar los mensajes del portapapeles, al cual llamaremos IniciarControlDelPortapapales y otro método que hará justo lo contrario al que llamaremos DetenerControlDelPortapapales. También tendremos un evento que será el encargado de avisarnos de que el contenido del portapapeles ha cambiado, a este le llamaremos CambioElPortapapeles. Listo… a trabajar:

 

 

 

Código de la clase

 

 

Public Class cPortapapeles

 

    'Heredamos la clase NativeWindow, la cual nos permitira a traves de su metodo AssignHandle

    'hacer que los mensajes que lleguen a un formulario, sean recibidos

    'en esta clase (Mas especificamente en el procedimiento WndProc de esta clase)

    Inherits NativeWindow

 

#Region "API de Windows"

 

    '*** FUNCIONES ***

 

    '"Sendmessage" nos pemite enviar un mensaje a una ventana

    Private Declare Auto Function SendMessage Lib "user32" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

 

    '"SetClipboardChain" agrega una ventana a la lista de aplicaciones de tipo "Clipboard Viewer"

    Private Declare Function SetClipboardViewer Lib "user32" (ByVal hWnd As IntPtr) As IntPtr

 

    '"ChangeClipboardChain" quita una ventana de la lista de aplicaciones de tipo "Clipboard Viewer"

    Private Declare Function ChangeClipboardChain Lib "user32" (ByVal hWnd As IntPtr, ByVal hWndNext As IntPtr) As Integer

 

    '*** CONSTANTES ***

 

    'El mensaje "WM_CHANGECBCHAIN" indica que ha cambiado

    'la lista de aplicaciones de tipo "Clipboard Viewer"

    Private Const WM_CHANGECBCHAIN As Integer = &H30D

 

    'El mensaje "WM_DRAWCLIPBOARD" indica que ha habido un cambio

    'en el contenido del portapapeles de Windows

    Private WM_DRAWCLIPBOARD As Integer = &H308

 

#End Region

 

#Region "VARIABLES"

    'Aqui guardaremos el manejador de la ventana que esta en lista, como "siguiente" de nuestra aplicacion

    Private ProximaVentana As IntPtr

#End Region

 

#Region "EVENTOS"

    'Este evento notificara que el contenido del portapapeles ha cambiado

    Public Event CambioElPortapapeles()

#End Region

 

#Region "METODOS"

 

    Public Sub IniciarControlDelPortapapales(ByVal FormHandle As IntPtr)

        'Asignamos como identificador de esta clase, el manejador del formulario recibido como parametro,

        'para que lleguen al procemiento WndProc los mensajes enviados al formulario

        Me.AssignHandle(FormHandle)

 

        'Agregamos nuestra aplicacion a lista de aplicaciones del tipo "Clipboard Viewer", para que nos lleguen

        'los mensajes relacionados con el portapapeles, y tambien obtenemos la siguiente ventana de la lista

        ProximaVentana = SetClipboardViewer(Me.Handle)

    End Sub

 

    Public Sub DetenerControlDelPortapapales()

        'Elimino nuestra aplicacion de la lista de aplicaciones del tipo "Clipboard viewer"

        ChangeClipboardChain(Me.Handle, ProximaVentana)

    End Sub

 

#End Region

 

#Region "FUNCIONES PRIVADAS"

    'Aqui llegaran los mensajes de Windows, y por lo tanto

    'aqui es donde nos daremos cuenta de que pasa en el portapapeles

    Protected Overrides Sub WndProc(ByRef e As System.Windows.Forms.Message)

 

        Select Case (e.Msg)

 

            Case WM_DRAWCLIPBOARD 'Hubo un cambio en el contenido del portapapeles

 

                'Si existe una proxima ventana, le paso el mensaje

                If Not (ProximaVentana.Equals(IntPtr.Zero)) Then

                    SendMessage(ProximaVentana, e.Msg, e.WParam, e.LParam)

                End If

 

                'Notifico el cambio, a traves del evento CambioElPortapapeles

                RaiseEvent CambioElPortapapeles()

 

            Case WM_CHANGECBCHAIN 'Hubo un cambio en la lista de aplicaciones tipo "Clipboard viewer"

 

                'En el campo WParam, se recibe el manejador de la ventana que se quito de la lista

                'En el campo LParam, se recibe el manejador de nuestra nueva siguiente ventana

 

                'Verfico si es nuestra siguiente ventana, la que se elimino de la lista

                If (e.WParam.Equals(ProximaVentana)) Then

                    ProximaVentana = e.LParam 'Actualizo nuestra nueva "siguiente ventana"

                Else

                    'Si existe una proxima ventana, le paso el mensaje

                    If Not (ProximaVentana.Equals(IntPtr.Zero)) Then

                        SendMessage(ProximaVentana, e.Msg, e.WParam, e.LParam)

                    End If

                End If

 

            Case Else

                'No es un mensaje que nos interese, asi que lo enviamos a la ventana, sin hacer nada

                Call MyBase.WndProc(e)

 

        End Select

 

    End Sub

#End Region

 

End Class

 

 

 

Como utilizar la clase

 

 

 

 

Ahora crearemos una pequeña aplicación para probar la clase. Como se puede ver en la imagen que se muestra arriba, nuestra aplicación tendrá dos botones y una caja de texto. La idea es que cuando haya un texto en el portapapeles, el botón “Pegar texto” se habilite y al pulsarlo se muestre el texto en el textbox, y si en el portapapeles no hay un texto el botón “Pegar texto” se deshabilite. El otro botón (“Limpiar”) tiene como función, simplemente borrar el contenido del textbox.

 

 

Código de la aplicación de prueba

 

 

Public Class Form1

    Inherits System.Windows.Forms.Form

 

+ "Código generado por el Diseñador de Windows Forms"

 

    Private WithEvents ClipboardViewer As New cPortapapeles()

 

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Inicio el control de portapapeles

        ClipboardViewer.IniciarControlDelPortapapales(Me.Handle)

    End Sub

 

    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing

        'Finalizo el control de portapapeles

        ClipboardViewer.DetenerControlDelPortapapales()

    End Sub

 

    Private Sub ClipboardViewer_CambioElPortapapeles() Handles ClipboardViewer.CambioElPortapapeles

 

        'Al cambiar el portapapeles, verfico si hay un texto en el mismo

        If Clipboard.GetDataObject.GetData("Text") <> "" Then

            'Si hay texto, habilito el boton "Pegar texto"

            cmdPegarTexto.Enabled = True

        Else

            'Si no hay texto, deshabilito el boton "Pegar texto"

            cmdPegarTexto.Enabled = False

        End If

 

    End Sub

 

    Private Sub cmdPegarTexto_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdPegarTexto.Click

        'Muestro el texto que hay en el portapapeles de Windows dentro del textbox

        TextBox1.Text = Clipboard.GetDataObject.GetData("Text")

    End Sub

 

    Private Sub cmdLimpiar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdLimpiar.Click

       'Borro el contenido de la caja de texto

        TextBox1.Clear()

    End Sub

End Class

 

 

 

Conclusión

 

Aquí hemos aprendido un poco como se maneja el portapapeles de Windows y como hacer para interceptar mensajes de un formulario desde una clase. Espero que esto les sea de utilidad, y desde ya quien tenga alguna duda, consejo, encuentre un error o simplemente quiera decir algo, mi mail esta disponible para todo esto (tillipablo@hotmail.com).

 

ir al ndice