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