Sección de tutoriales y manuales vb

Tutorial para crear un ocx que coloca el ejecutable en el systray



 

 

En esta guia se ve como crear un control de usuario para colocar el ejecutable en la barra tray de windows.

Nota: si querés descargar el ocx ya compilado para usarlo, lo podés hacer desde este enlace : Ocx para colocar el formulario en el systray

 

Este control tiene los siguientes métodos, eventos y propiedades

  1. PonerSystray : Notifica el ejecutable en la barra de windows
  2. RemoverSystray: Elimina el ícono
  3. Propiedad IconPicture: es el archivo de ícono a utilizar
  4. Propiedad ToolTipText: Texto descriptivo.
  5. Eventos: MouseMove, MouseUp, MouseDown y DblClick.

 

 

 

Descripción básica del Api Shell_NotifyIcon

Para colocar el ejecutable, o mejor dicho, notificarlo en la barra tray de windows, se utiliza el Api Shell_NotifyIcon. Esta función tiene una estructura (UDT) que hay que llenar con los datos necesarios para que se pueda realizar lo dicho, como por ejemplo, indicarle el Hwnd de la ventana a notificar (el formulario) o en este caso el Hwnd del Usercontrol, el archivo de ícono a mostrar, el texto o ToolTipText y algunas otras.

La estructura se llama NOTIFYICONDATA. Luego de que se asignaron esos valores a esta estructura, se le indica al Api que es lo que queremos hacer, es decir colocar la ventana en el tray, eliminarlo, modificar algo (como el icono ,el tooltip) y hay otras mas que la verdad no tengo bien en claro para que están, pero que no son necesarias para lo que se explica acá. En la página de Micro$oft podés leer información sobre esta función y otras.

Como se decía antes, una ves llenada la estructura NOTIFYICONDATA con la información para enviarsela a la función Api, mediante unas constantes que actúan como mensajes, se le indica que es lo que se vamos hacer, ponerlo en el systray, eliminarlo, modificar algun dato etc..

 

Por ejemplo para llenar la estructura podriamos hacer algo asi:

'Tamaño de la estructura systray
sysTray.cbSize = Len(sysTray)
'Establecemos el Hwnd, en este caso del formulario
sysTray.hwnd = UserControl.hwnd
sysTray.uId = vbNull
'Flags
sysTray.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
'Establecemos el mensaje callback
sysTray.ucallbackMessage = WM_MOUSEMOVE
'establecemos el icono, en este caso el que tiene el form, puede ser otro
sysTray.hIcon = Image1.Picture
'Establecemos el tooltiptext
sysTray.szTip = m_ToolTiptext & vbNullChar

 

Las variables que importan en este caso, son hwnd (Hwnd del UserControl), hIcon (archivo de ícono) y szTip (el texto del ToolTip)

Una ves llenada NOTIFYICONDATA, se puede ejecutar el Api de esta forma:

'Ponemos el icono en el systray
Shell_NotifyIcon NIM_ADD, sysTray

 

En el caso anterior , la constante o mensaje NIM_ADD, indica al api que se agregará y notificará en el Tray. Si quisieramos eliminarlo, haríamos:

Shell_NotifyIcon NIM_DELETE, sysTray

 

Donde el mensaje NIM_DELETE inidica que se eliminará del tray.

Si queremos modificar algún dato, como por ejemplo, cambiarle el ícono o el texto del toolTip, podriamos hacerlo asi:

'Le indicamos otro ícono
sysTray.hIcon = Image1.Picture
'Ejecutamos el Api pasandole los datos
Shell_NotifyIcon NIM_MODIFY, sysTray

 

En el caso anterior, primero cambiamos los datos de la estructura, luego ejecutamos Shell_NotifyIcon pasandole el mensaje NIM_MODIFY, para que surta efecto.


 

Pasos para crear el ocx

Comienza un proyecto Exe estandar. Luego desde el menú Archivo elige la opción Agregar proyecto y selecciona Agregar control Activex. En la ventana de proyectos debes tener 2 proyectos como muestra esta figura:

Vista previa de la venta de explorador de proyectos de visual basic

 

Ahora cambia el nombre del proyecto del UserControl llamado Proyecto1, por SysTray, como está en la imagen:

Vista previa del proyecto de tipo Activex

 

ahora le cambiaremos el nombre al control llamado UserControl1 por el nombre Tray:

Vista previa del cambio de nombre de la ventana del ocx

Al hacer esto, cuando coloquemos una instancia del ocx, es decir que lo agreguemos en un formulario en un proyecto Exe, el nombre del control ocx será Tray1, si agregamos otra instancia será Tray2, y asi.

Por último guardamos el grupo de proyectos desde el menú archivo en la opción " Guardar Grupo de proyectos " , y de esta forma se crearán en la carpeta donde los guardes los siguientes archivos:

 

Control Image para el ocx

La imagen que será el ícono que se notificará en la barra tray, se cargará en un control Image. Entonces para esto agrega un control llamado Image1 en el ocx.

 

Agregar las declaraciones Api en el UserControl

Ahora abre la ventana de código del usercontrol y pega el siguiente código que tiene la declaración de la función Api Shell_NotifyIcon, con la estructura mencionada antes llamada NOTIFYICONDATA, también las constantes para los mensajes, otras constantes que determinarán los eventos de mouse (click, dobleclick, etc..) y otra variable llamada m_ToolTipText que es una variable local para la propiedad ToolTipText que se creará luego:

'Función Api Shell_NotifyIcon
Private Declare Function Shell_NotifyIcon Lib "shell32" Alias "Shell_NotifyIconA" (ByVal _
                                              dwMessage As Long, pnid As NOTIFYICONDATA) As Boolean


'*******************************************
'Estuctura
'*******************************************

'Estructura NOTIFYICONDATA
Private Type NOTIFYICONDATA
		cbSize As Long
		hwnd As Long
		uId As Long
		uFlags As Long
		ucallbackMessage As Long
		hIcon As Long
		szTip As String * 64
End Type

'*******************************************
'Constantes
'*******************************************
'Para las acciones de Shell_NotifyIcon
Private Const NIM_ADD = &H0
Private Const NIM_MODIFY = &H1
Private Const NIM_DELETE = &H2
Private Const NIF_MESSAGE = &H1
Private Const NIF_ICON = &H2
Private Const NIF_TIP = &H4

'Para los botones y el mouse (mensajes)
Private Const WM_MOUSEMOVE = &H200
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const WM_LBUTTONDBLCLK = &H203
Private Const WM_RBUTTONDOWN = &H204
Private Const WM_RBUTTONUP = &H205
Private Const WM_RBUTTONDBLCLK = &H206

'*******************************************
'Variables locales
'*******************************************

'Variables para las propiedades
Private m_ToolTiptext As String

'*******************************************
'otras Variables
'*******************************************

'variable para la estructura NOTIFYICONDATA
Dim sysTray As NOTIFYICONDATA 

 


Crear los métodos que colocan y eliminan el ícono del systray:

 

Método para colocar y notificar:

Para que estos métodos se puedan acceder una ves que colocamos una instancia del ocx en un formulario, es decir colocando el nombre del ocx y presionar el punto "." , los métodos, que son funciones o rutinas, deben ser públicos, es decir declarados como Public

El que notificará el ícono lo llamaremos PonerSystray. Este es el código y pegalo en la ventana de código del UserControl:

'Coloca el systray
'************************
Public Sub PonerSystray()

  'Tamaño de la estructura systray
  sysTray.cbSize = Len(sysTray)
  'Establecemos el Hwnd, en este caso del formulario
  sysTray.hwnd = UserControl.hwnd
  sysTray.uId = vbNull
  'Flags
  sysTray.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
  'Establecemos el mensaje callback
  sysTray.ucallbackMessage = WM_MOUSEMOVE
  'establecemos el icono, en este caso el que tiene el control Image1
  sysTray.hIcon = Image1.Picture
  'Establecemos el tooltiptext
  sysTray.szTip = m_ToolTiptext & vbNullChar
  'Ponemos el icono en el systray
  Shell_NotifyIcon NIM_ADD, sysTray

End Sub  

 

Como verás, la variable systray que es de tipo NOTIFYICONDATA (que está declarada arriba de todo en el código anterior), almacena los valores que necesitaremos. Estos valores, no importa para que significan todos, solo mencionaremos los siguientes:

 

Por último, se llama a la función Shell_NotifyIcon, pasandole el mensage NIM_ADD que le ordena que se coloque en el tray, y en el segundo parámetro la estructura.

 

Método para eliminar el ícono de la barra Tray:

Para eliminarlo no es necesario llenar la estructura como en el caso anterior, pero SI hay que pasarla igualmente en el parámetro al Api.

Para removerlo ,solo basta con ejecutar Shell_NotifyIcon con el mensaje NIM_DELETE.

Al igual que en el método anterior, la función o subrutina debe ser de tipo Public para poder accederla luego cuando usemos el ocx en un formulario

Pega el código para el método RemoverTray:

'Remueve el systray
'************************
Public Sub RemoverSystray()
Shell_NotifyIcon NIM_DELETE, sysTray
End Sub 

 

Crear las propiedades ToolTiptext y la propiedad IconPicture:

 

El código para estas 2 propiedades es el siguiente:

'Propiedad ToolTiptext
'*********************
Public Property Get ToolTipText() As String
ToolTipText = m_ToolTiptext
End Property

Public Property Let ToolTipText(ByVal vNewValue As String)
  m_ToolTiptext = vNewValue
  
sysTray.szTip = m_ToolTiptext & vbNullChar Shell_NotifyIcon NIM_MODIFY, sysTray
PropertyChanged ("ToolTipText") End Property 'Propiedad IconPicture '********************* Public Property Get IconPicture() As Picture Set IconPicture = Image1.Picture End Property Public Property Set IconPicture(ByVal New_Picture As Picture) Set Image1.Picture = New_Picture sysTray.hIcon = Image1.Picture
Shell_NotifyIcon NIM_MODIFY, sysTray PropertyChanged "IconPicture" UserControl_Resize End Property Property Let IconPicture(ByVal New_Picture As Picture)
Set Image1.Picture = New_Picture
PropertyChanged "IconPicture" UserControl_Resize
End Property 

En la propiedad TootlTiptext, en Property Let (escribe la propiedad), lo que hacemos es primero asignarle el valor a la copia local de la variable m_toolTipText, luego ejecutamos el Api Shell_NotifyIcon con el mensaje NIM_MODIFY, esto le informa que ha cambiado algo, en este caso el texto.

Lo mismo ocurre con la propiedad IconPicture, en la cual también se ejecuta el api con la constante NIM_MODIFY, pero en ves de especificar el ToolTip en la variable sysTray.szTip, lo hacemos modificando la variable sysTray.hIcon con el valor de Image1.Picture, es decir la imagen que este cargada en el Image1 del ocx.

 

Sobre Property Let, Property Get y Poperty Set:

El primero: ocurrirá o ejecutará cuando nosotros cambiamos o modificamos alguna de las propiedades del control ya sea en tiempo de diseño como de ejecución.

Property Get hace lo mismo, pero en ves de escribir la propiedad, lo que hace es leerla.

Property Set se utiliza para escribir la propiedad, pero para propiedades que son objetos.

Nota: para comprobar esto, podrías poner puntos de interrupción en los Property y comprobar, cuando invocas alguna de ellas, el flujo de la ejecución.

El PropertyChanged se debe ejecutar cuando una propiedad cambia su estado o valor, es decir en el Property Let, luego de que se ha modificado la propiedad. Por ejemplo en el caso del ToolTipText, primero se cambia el valor dentro del Let:

m_ToolTiptext = vNewValue

y luego se llama a PropertyChanged, pasándole como parámetro el nombre de la propiedad, en este caso:

PropertyChanged ("ToolTipText")

 

Agregar Código en el evento WriteProperty y ReadProperty del UserControl

 

Para que podamos escribir y recuperar las propiedades ToolTiptext e IconPicture, para que una ves que el ocx está instanciado en un formulario, y estas no se pierdan, hay que escribir el código en dichos eventos.

El primero WriteProperty escribe la propiedad cuando modificamos desde la ventana de propiedades mientras que la otra ReadProperty las lee desde el almacén.

Pegar lo siguiente en el UserControl

'Cargar valores de propiedad desde el almacén
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
    On Local Error Resume Next
    m_ToolTiptext = PropBag.ReadProperty("ToolTipText", "")
    Set Image1.Picture = PropBag.ReadProperty("IconPicture", Nothing)
End Sub




'Escribir valores de propiedad en el almacén
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
    On Local Error Resume Next
    Call PropBag.WriteProperty("ToolTipText", m_ToolTiptext, "")
    Call PropBag.WriteProperty("IconPicture", Image1.Picture, Nothing)
End Sub 

 

Propiedad InvisibleAtRuntime del los UserControl:

 

Esta propiedad que es de tipo True / False , lo que hace es determinar si el ocx una ves puesto en un formulario y que se ejecute el mismo, el ocx esté visible o invisible en tiempo de ejecución, por ejemplo como lo hace el control CommonDialog.

En este caso lo pondremos en True, ya que no queremos que esté visible cuando se corra el programa. Para hacerlo, solo selecciona la ventana de propiedades del Usercontrol , y cambiale el valor False por True.

 

Evento resize del Usercontrol:

 

Ahora colocaremos código en el evento Resize del UserControl, para que la ventana del control ocx se ajuste a las dimensiones del control Image1.

Entonces abrir la ventana de código del USercontrol, buscar en la lista de eventos " Resize " y colocar esto:

    Static flag As Boolean
    
    If flag Then Exit Sub
    
    flag = True
    
    With Image1
         Height = .Height
         Width = .Width
    
    End With
    flag = False 

 

Evento Inicialize del USercontrol

 

Este evento se dispara una única ves cuando colocamos una instancia del ocx en un formulario.

El código que colocaremos será para situar el Top y el Left del control Image1, con respecto al UserControl. El valor será 0 para el Left y también 0 para el Top

Private Sub UserControl_Initialize()
Image1.Top = 0
Image1.Left = 0

End Sub 

 

Probando el ocx en el formulario

 

Si bien todavía faltan agregar algunas partes para terminar, probaremos el ocx.

Antes de poder colocoar una instancia del mismo en el formulario form1 del otro proyecto (el proyecto EXE), selecciona el control Image1 y cargale un ícono. Este ícono será la imágen por default que tendrá el ocx cuando lo instanciemos en el Form.

Ahora, para poder probar parcialmente lo visto hasta ahora, es decir agregar el ocx en el form, es necesario ir al otro proyecto (el exe) y veremos que aparece nuestro ocx en el cuadro de controles o cuadro de herramientas. Pero hay un detalle importante y es que, para que el mismo esté disponible y podamos agregar y probar una instancia en el form, la ventana del ocx (llamada ventana de diseñador) debe estar cerrada, de lo contrario el ocx, desde el proyecto exe, en el cuadro de controles, se visualizará deshabilitado, como muestra la imagen:

 

 

Por lo tanto, debes cerrar la ventana de diseño del ocx. Para cerrarla, debes estar situado en la ventana del UserControl, y desde el botón ubicado en la esquina superior derecha presionar dicho botón. Una ves cerrada la ventana de diseñador, ya se puede acceder al formulario Form1 y verás que el está habilitado para agregarlo al formulario.

Para cerciorarte que va todo bien, agrega una instancia del ocx en el Form1. Por defecto al añadirlo se agreegará con el nombre tray1.

ahora selecciona el ocx y desde la ventana de propiedades debes poder visualizar las creadas anteriormente (TooltipText e IconPicture). Para probar IconPicture, cambia la propiedad seleccionado un archivo de icono que tengas en algun sitio de tu disco, y si todo está bien el ocx debe visualizar dicho gráfico.

Ahora en el Form1 agrega 2 CommandButton, el Command1 en el caption escribe "Colocar en Tray" y en el otro command2 "Quitar del tray".

El código debe ser este:

'Colocar
Private Sub Command1_Click() Tray1.PonerSystray End Sub
'Eliminar Private Sub Command2_Click() Tray1.RemoverSystray End Sub 

Ahora F5 para correr el formulario y probar que funcione.

Nota: asegurate que el ocx no esté visible en el momento de ejecutar el formulario. Por si te olvidaste, debes especificar la propiedad InvisibleAtRuntime del UserControl en True.

 

Cambiar en tiempo de ejecución la propiedad ToolTip y la propiedad IconPicture

 

Coloca otros 2 commandButton. El command3 en el Caption escribe " Cambiar ToolTip " y al Command4 " Cambiar ícono ". También agrega un textbox llamado Text1 para escribir el texto del ToolTip y también un control Microsoft CommonDialog1 .

El código en el Form1

'Cambia el ícono del systray
Private Sub Command4_Click()

With CommonDialog1 .Filter = "Archivos de iconos|*.ico" .ShowOpen If .FileName = "" Then Exit Sub 'Le cambiamos el ícono
Tray1.IconPicture = LoadPicture(.FileName) End With End Sub 'Cambia el toolTipText Private Sub Command5_Click()
Tray1.ToolTipText = Text1 End Sub 

 

Ahora puedes probar cambiando en tiempo de ejecución el texto del ToolTip y el ícono.

 

Crear los eventos para el ocx:

 

Ahora el ocx funciona bien, pero le falta crearle eventos para que una ves que esté notificado en el Tray, por ejemplo al hacer click derecho, click Izquierdo etc..., responda el mismo, por ejemplo para desplegar un menú, maximizar el form, o hacer cualquier otra acción que se requiera.

Los eventos que se crearán serán:

 

Para definir estos eventos en el módulo del USerControl, abre la ventana de código y escribe lo siguiente:

Public Event MouseDown(Button As Integer)
Public Event MouseUP(Button As Integer)
Public Event MouseMove()
Public Event DblClick(Button As Integer)

Estos eventos al ser públicos, se podrán acceder desde el ocx como cualquier evento de un control, es decir, al definirlos, ya aparecerán en la lista de eventos del formulario del proyecto Exe.

Pero lógicamente no basta con definirlos, ya que por mas que aparezcan, no se dispararán o ejecutarán, solo se podrán ver. Para que se disparen dichos eventos se utiliza el método RaiseEvent.

Ahora haremos lo siguiente: Abre la ventana de código del Usercontrol. En la lista izquierda serciorate que diga UserControl, y en la lista derecha selecciona el evento MouseMove del mismo, como está en la imagen:

Vista de diseñio de la ventana del usercontrol

 

Dentro de este evento colocaremos el código que disparará los eventos creados antes.

 

El código que debes colocar en UserControl_MouseMove .

 On Local Error Resume Next

If (ScaleMode = vbPixels) Then
    mensaje = X
Else
    mensaje = X / Screen.TwipsPerPixelX
End If

'Lanza el evento MouseMove RaiseEvent MouseMove Select Case mensaje 'Lanza el evento Dobleclick para el botón izquierdo Case WM_LBUTTONDBLCLK RaiseEvent DblClick(vbLeftButton) ''Lanza el evento Dobleclick - boton derecho Case WM_RBUTTONDBLCLK RaiseEvent DblClick(vbRightButton) 'Botón Arriba Derecho Case WM_RBUTTONUP RaiseEvent MouseUP(vbRightButton) 'Botón Arriba Izquierdo Case WM_LBUTTONUP RaiseEvent MouseUP(vbLeftButton) 'Botón Derecho abajo Case WM_RBUTTONDOWN RaiseEvent MouseDown(vbRightButton) 'Botón izquierdo abajo Case WM_LBUTTONDOWN RaiseEvent MouseDown(vbLeftButton) End Select

 

Al ejecutar RaiseEvent seguido del nombre del evento , le enviamos como parámetro el valor del botón, usando las constantes de visual basic, vbLeftButton y vbRightButton.

 

Probar los eventos creados desde el formulario:

 

Ahora para probar lo anterior, cierra la ventana de diseñador Ocx, y vuelve al proyecto Exe y situate en el Form1.

Selecciona el ocx, que colocamos anteriormente en el Form1 (Tray1), y dale DobleClick. Se abrirá la ventana de código del Form y se creará un evento vació como muestra la imagen:

 

vista de la ventana de código del Form1

 

Despliega la lista derecha de eventos del Tray1 para asegurarte que están los que definiste en el módulo del UserControl.

Para hacer una prueba, dentro de este evento (MouseDown) coloca este código:

MsgBox "Se ejecutó el evento Mousedown"

Presiona F5 para ejecutar el Form. También dale al botón "Poner en tray" para notificarlo.

Luego hacé un click sobre el ícono con el botón izquierdo y también con el botón derecho para probar que responde dicho evento mostrando el mensaje: "se ejecutó el evento ...".

Como el evento dentro del UserControl, lo definimos con la variable Button, podemos determinar que botón se presionó, ya que recibirá dicho valor enviado con RaiseEvent en el parámetro del mismo

Por ejemplo:

Private Sub Tray1_MouseDown(Button As Integer)

'Derecho If Button = vbRightButton Then MsgBox "Botón derecho" End If 'Iquierdo If Button = vbLeftButton Then MsgBox "Botón izquierdo" End If End Sub 

Otro ejemplo.

Sabiendo esto, ahora se puede utilizar para mostrar un menú popup, o hacer otra acción.

Coloca esto en el Command1 para que se notifique y se oculte el form

Código

Private Sub Command1_Click()
'Notificamos
Tray1.PonerSystray
'Ocultamos el Form
Me.Hide
End Sub

Ahora coloca el siguiente código para que al dar Doble click con el botón izquierdo, se restaure el form1 y se elimine el ícono del Tray

Código en el evento DblClick del Tray1:

Private Sub sysTray1_DblClick(Button As Integer)
'Si hacemos dobleClick con el botón izquierdo restauramos el form y _
quitamos el ícono del Tray

If Button = vbLeftButton Then
Me.Show
Tray1.RemoverSystray
End If
End Sub

 

Propiedad ToolBoxBitMap

 

Para que el control ocx, desde el cuadro de herramientas de vb, en ves de visualizar la imagen por defecto que muestra para los controles de usuario, se pueda especificar un mapa de bits creado por nosotros, se debe utilizar la propiedad ToolBoxBitMap.

En la ayuda de vb dice que el mapa de bits debe ser de 15 x 16 pixeles, no lo he probado con otras medidas, pero es posible que no lo tome correctamente.

Para agregarle un gráfico nuestro, utiliza un editor de imágenes, crea un archivo de tipo Bmp, salvala a disco, y seleccionala desde la propiedad ToolBoxBitMap.

Prueba cerrando la ventana de diseñador, vuelve al proyecto Exe, y verifica que la imagen se muestra correctamente.

 

Generar y compilar el ocx:

 

El último paso será Generar y compilar el control Ocx.

Para hacer esto, selecciona desde la ventana de explorador de proyectos, el Proyecto Systray.

Ahora desde el menú archivo elige la opción Generar Systray.ocx. Listo, ahora puedes cerrar todo y crear un nuevo proyecto de tipo Exe desde 0 para probar el control.

Código fuente completo:

Descargar


Nota: cualquier error de concepto o error en el código de esta guía, sería bueno que me lo comunicaras mediante un mail para que lo corrija. Espero que te sirva !!!

 

 


Buscar en Recursos vb