4.4. Selección Múltiple Interactiva.

Una manera de implementar la selección múltiple consiste en emplear para indicar el final de la selección la pulsación de una tecla, por ejemplo INTRO.

Eventos de Teclado.

Esto implica recurrir a una serie de eventos diferente, los eventos de teclado. El acceso a los eventos de teclado se hace a partir del objeto KeyboardEvents. Será necesario declarar una variable de objeto para ello.

Private WithEvents oEventosTeclado As KeyboardEvents

Este objeto habilita un conjunto de eventos que pueden ser recibidos por la aplicación cliente en el momento en que se pulsa una tecla estando iniciado el objeto InteractionEvents. Contrariamente a lo que parece entenderse de los ejemplos contenidos en la ayuda de programación de Autodesk Inventor, cuando se vigilan los eventos del teclado la propiedad InteractionDisabled deberá establecerse como Verdadero (True) en lugar de como Falso (False). Esta propiedad, según su descripción en la ayuda, establece si se disparará o no cualquiera de los eventos de Selección (SelectEvents), del Ratón (MouseEvents) o del Teclado (KeyboardEvents). La ayuda explica que si su valor es Falso NO se disparará ninguno de estos eventos. Sin embargo, al explicar los eventos de teclado se indica que los eventos del teclado pueden ser recibidos por el cliente cuando una tecla es presionada mientras esté iniciado el objeto InteractionEvents y el indicador InteractionDisabled tenga el valor de False.

Una de las dos afirmaciones (o quizás ambas) tendrá que estar equivocada. Según las comprobaciones realizadas en los talleres de programación se llega a la conclusión de que para recibir los eventos del teclado la propiedad InteractionDisabled debe establecerse como Verdadera (True). El valor de esta propiedad parece no afectar a los eventos de selección.

Los eventos del teclado disponibles son OnKeyDown, OnKeyPress y OnKeyUp.

OnKeyDown

OnKeyDown es el evento que se dispara cuando el usuario presiona una tecla. Sólo las teclas especificadas en el enumerador se gestionan mediante este evento.

OnKeyDown( Key As Long, ShiftKeys As ShiftStateEnum)

El argumento Key devuelve un código de tecla, por ejemplo kKeyF1 (para la tecla F1) o kKeyHome (para la tecla INICIO). Los códigos están definidos en la lista de constantes KeyCodeEnum.

El argumento ShiftKeys devuelve una constante de enumeración que corresponde al estado de las teclas SHIFT, CTRL, y ALT cuando la tecla especificada en el argumento Key fue presionada. Las constantes corresponden a estar presionadas una o más de esas teclas. Cada una de ellas corresponde a un bit: la tecla SHIFT al bit 0, la tecla CTRL al bit 1, y la tecla ALT al bit 2. Estos bits corresponden a los valores decimales 1, 2, y 4, respectivamente. El enumerador ShiftStateEnum especifica combinaciones de estas teclas. Por ejemplo, si solo hubiera sido presionada la tecla ALT, la constante kShiftStateAlt que corresponde al valor numérico entero 4 sería devuelta como valor del argumento ShiftKeys. Si hubieran estado pulsadas CTRL y ALT, se devolvería la constante kShiftStateCtrlAlt de valor 6.

OnKeyPress

OnKeyPress es el evento que se dispara cuando el usuario presiona y suelta una tecla. Se devuelve el valor ANSI de la tecla pulsada.

OnKeyPress( KeyASCII As Long)

El argumento KeyASCII devuelve un código numérico correspondiente a la norma ANSI. El código que corresponde a la tecla INTRO es el 13, Asc(vbCr). Otros códigos que corresponden a teclas que no tienen una representación gráfica son 8, Asc(vbBack) para RETROCESO, 9 para el TABULADOR, Asc(vbTab) y 10, Asc(vbLf) para el salto de línea.

OnKeyUp

OnKeyUp es el evento que se dispara cuando el usuario suelta una tecla que ha pulsado. Sólo se tienen en cuenta las teclas especificadas en el enumerador ShiftStateEnum. El argumento Key representa la tecla pulsada.

OnKeyUp( Key As Long, ShiftKeys As ShiftStateEnum)

Los argumentos corresponden a los ya descritos para el evento OnKeyDown.

Establecer una referencia al objeto Eventos de Teclado.

Para emplear los eventos de teclado será necesario establecer una referencia a este objeto.

Set oEventosTeclado = oEventosInteraccion.KeyboardEvents

Para controlar el final de la interacción mediante la pulsación de alguna tecla será necesario definir el código correspondiente al evento que deseemos emplear. La opción más sencilla será emplear el evento OnKeyPress, aunque una opción más pudiera estar en los otros que además comprueban si se han pulsado teclas modificadoras como CTRL, ALT o SHIFT. El código que se muestra a continuación detendrá la selección en el momento que se pulse INTRO.

Private Sub oEventosTeclado_OnKeyPress(ByVal KeyASCII As Long)
    If KeyASCII = 13 Then
        bolEnSeleccion = False
    End If
End Sub
El parámetro KeyASCII es una constante de enumeración correspondiente al valor de KeyCodeEnum.

Devolver los datos sobre las caras seleccionadas.

Al ser múltiple la selección, no bastará con devolver la primera cara del conjunto de selección. Para ello habrían diversas opciones. Empleando los recursos nativos del lenguaje de programación sería posible devolver un Array que contenga las diferentes caras seleccionadas.Pero el API de Inventor ofrece un recurso para solucionar este tipo de situaciones: la colección de objetos (ObjectCollection). Demostraremos a continuación las dos posibilidades.

Array.

Para ello deberá declararse un array de tipo Object:

Dim arrCaras() As Object

Debe observarse que este array se crea en tiempo de compilación al cargar el proyecto, pero aún no se sabe el tamaño que este array tendrá. El tamaño del Array sólo puede conocerse una vez que haya concluido la selección, una vez que se conozca el número de elementos que contendrá. El número de elementos estará dado por la propiedad Count de la selección:

numCaras = oEntsSelecc.Count

Para dimensionar en tiempo de ejecución el array se usa la instrucción de Visual Basic ReDim.

ReDim arrCaras(1 To numCaras)

Ahora será necesario incluir en el array los objetos seleccionados. Esto se deberá hacer en un bucle que recorra los ítems del conjunto de selección:

Dim i As Integer
i=1
Do

    Set arrCaras(i) = oEntsSelecc.Item(i -1)
    i=i+1
Loop Until i > oEntsSelecc.Count

Por último será necesario asignar al método que realiza la selección (que llamaremos Designa) este array. Designa deberá haber sido declarado como del tipo Variant.

Designa = arrCaras

ObjectCollection

El objeto ObjectCollection es uno de los "objetos transitorios" (TransientObjects) que suministra el API de Inventor, utilizados como contenedores temporales durante la ejecución del programa. En nuestro caso servirá como contenedor de las caras seleccionadas. Para emplear el ObjectCollection añadiremos al código de la función Designa la definición de una variable de ese tipo:
Dim oCaras As ObjectCollection

Una vez concluida la selección, comprobaremos si se ha seleccionada una o más caras. Si así fuera crearemos un nuevo objeto objeto ObjectCollection empleando el método CreateObjectCollection del objeto TransientObjects, que asignaremos a la variable oCaras.
Set oCaras = ThisApplication.TransientObjects.CreateObjectCollection

Para poblar la colección con las caras seleccionadas, que habremos obtenido como ObjectsEnumerator a partir de la propiedad SelectedEntities de los eventos de selección (SelectEvents), implementaremos un ciclo iterativo.
        Do
            oCaras.Add oEntsSelecc.Item(i)
            i = i + 1
        Loop Until i > numCaras

Donde numCaras representa la cantidad de caras seleccionadas obtenidas a partir de la propiedad Count de las entidades seleccionadas:
    Dim numCaras As Integer
    numCaras = oEntsSelecc.Count

Detener la selección si se cancela el comando.

Cuando el usuario selecciona un comando diferente o pulsa la tecla Escape el bucle de selección deberá cancelarse, cosa que no sucederá hasta que la variable que le sirve de control no adopte el valor de Falso. Para ello se puede aprovechar el evento OnTerminate del objeto InteractionEvents que en esos casos se produce  incluyendo el código necesario en el procedimiento que se activa por este evento:

Private Sub oInteraccion_OnTerminate()
     ' Indicador de que hemos concluido.
     bolEnSeleccion = False
End Sub

Ejercicio.

Implementar el ejercicio anterior utilizando la selección interactiva múltiple.Para ello será necesario:

  1. Modificar el código de clsSelecc para incorporar la funcionalidad de los eventos del teclado y reunir en un array las caras seleccionadas. Se modificará el tipo de dato devuelto por el método Designa para poder devolver el conjunto de caras seleccionadas en lugar de un único objeto Cara.
  2. En el código del formulario modificar el tipo de dato que recibe la selección e implementar el recorrido por el conjunto de caras devuelto.

Modificaciones a clsSelecc.

El primer aspecto a modificar se encuentra en la declaración de variable s globales dentro de la clase, incorporando los eventos de teclado:
Option Explicit
'Declarar los objetos de evento, Interacción y Selección:
Private WithEvents oEventosInteracc As InteractionEvents
Private WithEvents oEventosSelecc As SelectEvents
Private WithEvents oEventosTeclado As KeyboardEvents
Private m_Flt As SelectionFilterEnum
Private m_Msj As String

Habrá que incorporar el procedimiento de respuesta al evento KeyPress como se ha explicado más arriba. La otra modificación estará en la función Designa que reproducimos a continuación. En este código recurrimos al ObjectCollection para agrupar las caras seleccionadas.

Public Function Designa() As ObjectCollection
   
    bolEnSeleccion = True
    Set oEventosInteracc = _
    ThisApplication.CommandManager.CreateInteractionEvents
    Set oEventosTeclado = oEventosInteracc.KeyboardEvents
    oEventosInteracc.InteractionDisabled = False
    oEventosInteracc.StatusBarText = m_Msj
    Set oEventosSelecc = oEventosInteracc.SelectEvents
    oEventosSelecc.AddSelectionFilter m_Flt
    oEventosInteracc.Start
   
    Do While bolEnSeleccion
        DoEvents
    Loop
   
    Dim oEntsSelecc As ObjectsEnumerator
    Set oEntsSelecc = oEventosSelecc.SelectedEntities
    Dim numCaras As Integer
    numCaras = oEntsSelecc.Count
    If numCaras > 0 Then
        Dim oCaras As ObjectCollection
        Set oCaras = ThisApplication.TransientObjects.CreateObjectCollection
        Dim i As Integer
        i = 1
        Do
            oCaras.Add oEntsSelecc.Item(i)
            i = i + 1
        Loop Until i > numCaras
    End If
    Set Designa = oCaras
    oEventosInteracc.Stop
    Set oEventosSelecc = Nothing
    Set oEventosInteracc = Nothing
End Function

Modificaciones al código del formulario:

La primera modificación estaría en la declaración de la variable global oSelCaras para contener el ObjectCollection devuelto por la función Designa.
Option Explicit
Dim oSelCaras As ObjectCollection

A esta variable se asignará, en el procedimiento de respuesta al evento Clic del botón cmdSeleccion, lo devuelto por Designa.
Private Sub cmdSeleccion_Click()
    Dim oSelecc As New clsSelecc
    oSelecc.Filtro = kPartFaceFilter
    oSelecc.Mensaje = "Seleccione una cara"
    Me.Hide
    Set oSelCaras = oSelecc.Designa
    lblResultado.Caption = "Pulse Calcular para ver el resultado"
    Me.Show
End Sub

Y debemos entonces modificar el procedimiento asociado al botón cmdAceptar de manera que procese cada una de las caras. Se implementará un ciclo que prepare un mensaje con las áreas de cada una de las caras y la suma del área total que será presentada en la etiqueta lblInfo.
Private Sub cmdAceptar_Click()
    Dim oDoc As PartDocument
    Set oDoc = ThisApplication.ActiveDocument
    Dim oUOM As UnitsOfMeasure
    Set oUOM = oDoc.UnitsOfMeasure
    Dim eUnidad As UnitsTypeEnum
    eUnidad = oUOM.LengthUnits
    Dim sUnidad As String
    sUnidad = oUOM.GetStringFromType(eUnidad)
    Dim sUnidadArea As String
    sUnidadArea = sUnidad & "^2"
    Dim strArea As String
    strArea = ""
    Dim Datos As String
    Datos = ""
    Set oDoc = ThisApplication.ActiveDocument
    Dim dblArea As Double
    Dim dblTotal As Double
    Dim strTotal As String
    Dim i As Long
    i = 1
    If oSelCaras.Count > 0 Then
        Do
            dblArea = oSelCaras(i).Evaluator.Area
            dblTotal = dblTotal + dblArea
            strArea = oUOM.GetStringFromValue(dblArea, sUnidadArea)
            Datos = Datos & "El área de la cara " & i & " = " & strArea & vbCrLf
            i = i + 1
        Loop Until i > oSelCaras.Count
        strTotal = oUOM.GetStringFromValue(dblTotal, sUnidadArea)
        lblResultado.Caption = Datos & "Area total= " & strTotal
    Else
        lblResultado.Caption = "Ninguna cara seleccionada"
    End If
End Sub

El proyecto correspondiente a este ejercicio puede descargarse desde el enlace situado al final de esta página.

ċ
SeleccionMúltipleInteractiva.zip
(25k)
Reinaldo Togores Fernández,
Mar 14, 2011, 9:52 AM