2009-11-03 18 views
6

Tengo un formulario que incluye una hoja de datos. Me gustaría hacer posible que un usuario seleccione múltiples filas, haga clic en un botón y ejecute algunas consultas SQL y realice algún trabajo en esas filas.¿Cómo accedo a las filas seleccionadas en Access?

Revisando mi código VBA, veo cómo puedo acceder al último registro seleccionado usando la propiedad CurrentRecord. Sin embargo, no veo cómo puedo saber qué filas se seleccionaron en una selección múltiple. (Espero estar seguro ...)

¿Cuál es la forma estándar de hacer esto? Acceda a la documentación de VBA es algo oscuro en la red ...

¡Gracias!

Respuesta

7

Aquí está el código para hacerlo, pero hay una trampa.

Private Sub Command1_Click() 
    Dim i As Long 
    Dim RS As Recordset 
    Dim F As Form 

    Set F = Me.sf.Form 
    Set RS = F.RecordsetClone 

    If F.SelHeight = 0 Then Exit Sub 

    ' Move to the first selected record. 
    RS.Move F.SelTop - 1 

    For i = 1 To F.SelHeight 
     MsgBox RS![myfield] 
     RS.MoveNext 
    Next i 

End Sub 

Aquí está la trampa: Si se añade el código a un botón, tan pronto como el usuario hace clic en ese botón, la selección se pierde en la red (SelHeight será cero). Por lo tanto, debe capturar esa información y guardarla en una variable de nivel de módulo con un temporizador u otros eventos en el formulario.

Aquí hay un artículo que describe cómo solucionar el problema en detalle.
http://www.mvps.org/access/forms/frm0033.htm

Catch 2: Esto sólo funciona con selecciones contiguas. No pueden seleccionar múltiples filas no secuenciales en la grilla.

Actualización:
Puede haber un mejor evento para atrapar a esto, pero aquí es una aplicación de trabajo mediante la propiedad form.timerinterval que he probado (al menos en 2k3 acceso, pero 2k7 debería funcionar bien)

Este código se encuentra en la SUBFORMACIÓN, utilice la propiedad para obtener el valor de autocobertura en el formulario maestro.

Public m_save_selheight As Integer 

Public Property Get save_selheight() As Integer 
    save_selheight = m_save_selheight 
End Property 

Private Sub Form_Open(Cancel As Integer) 
    Me.TimerInterval = 500 
End Sub 

Private Sub Form_Timer() 
    m_save_selheight = Me.selheight 
End Sub 
+0

increíble ... es por eso que seguí recibiendo selheight a las 0 ... Muchas gracias lo intentaré de inmediato y acepto tu respuesta si funcionó! – karlipoppins

+0

hmm no puedo hacer que funcione ... Con Access 2007 , selheight se queda en 0 ... – karlipoppins

+0

El truco está en agarrar el valor antes de que pierda el foco. ¿En qué evento está agarrando el valor del cierre? ¿Lo intentó en un temporizador en el subformulario? Pruebe el valor en el inmediato ventana (ctrl-g) y vea si el problema es la restauración de la configuración después de perder el foco. – JohnFx

2

He intentado hacer algo así antes, pero nunca tuve éxito con el uso de un método que requería que el usuario seleccionara varias filas en el mismo estilo que un cuadro de diálogo de archivo de Windows (presionando Ctrl, Shift, etc.)

Un método que he usado es usar dos cuadros de lista. El usuario puede hacer doble clic en un elemento en el cuadro de lista de la izquierda o hacer clic en un botón cuando se selecciona un elemento, y se moverá al cuadro de lista de la derecha.

Otra opción es utilizar una tabla local que se rellene con los datos de origen más valores booleanos representados como casillas de verificación en un subformulario. Una vez que el usuario selecciona los datos que desea al hacer clic en las casillas de verificación, el usuario presiona un botón (u otro evento), momento en el que va directamente a la tabla de datos subyacente y consulta solo las filas que se marcaron. Creo que esta opción es la mejor, aunque requiere un poco de código para funcionar correctamente.

Incluso en Access, a veces encuentro que es más fácil trabajar directamente con las tablas y las consultas en lugar de tratar de usar las herramientas incorporadas en los formularios de Access. En ocasiones, las herramientas integradas no hacen exactamente lo que usted desea.

+0

+1 para su segunda opción, aunque yo preferiría no haría eso si es posible ... Con suerte Microsoft le ocurrió una manera oscura para acceder a esas filas seleccionadas ...? – karlipoppins

+0

¿Qué versión de Access estás usando? Uso Access 2002 en el trabajo. Curiosamente, acabo de hojear el "Manual del desarrollador de Access 2002" (Considerado como la Biblia del desarrollador de Access) y tienen una clase que crearon llamada MultiPik. Esta clase * no * permite seleccionar varios elementos a la vez, pero dicen que es lenta cuando tienes toneladas de artículos (dicen que 300 elementos son lentos). Si está interesado, puedo analizar la legalidad de publicar el código para esta clase en Stack Overflow. Házmelo saber. –

+0

bueno, estoy usando 2007 y mi tabla tiene más de 10k entradas, así que probablemente no funcionaría bien ... – karlipoppins

6

he utilizado la técnica similar a JohnFx

controlar la altura de selección antes de que desaparezca He utilizado el evento de salida del control de subformulario en el formulario principal.

lo tanto, en la forma principal:

Private Sub MySubForm_Exit(Cancel As Integer) 

    With MySubForm.Form 
    m_SelNumRecs = .SelHeight 
    m_SelTopRec = .SelTop 
    m_CurrentRec = .CurrentRecord 
    End With 

End Sub 
+1

¡Esto es mucho más elegante y eficiente que el evento Timer de formulario! Si hay código necesario para ejecutar en el SubForm, se puede llamar a un método público del SubForm desde el formulario principal en el mismo controlador de eventos Exit: MySubForm.Form.PublicMethod. Los valores SelHeight y SelTop seguirán siendo válidos durante la ejecución de ese método. El beneficio de este patrón es que debido a que el código de trabajo está en el módulo SubForm, será reutilizable si el Subformulario está en múltiples formularios principales ... por lo que el código duplicado en los formularios principales es mínimo. –

2

Una solución a la pérdida de la selección cuando el sub formulario pierde el foco es para guardar la selección en caso de salida (como ya se ha mencionado por otros).

Una buena adición es restaurarlo inmediatamente, usando el temporizador, para que el usuario aún pueda ver la selección que hizo.

Nota: Si desea utilizar la selección en un controlador de botón, la selección puede no restaurarse cuando se ejecuta. Asegúrese de utilizar los valores guardados de las variables o agregue un DoEvents al comienzo del manejador de botones para permitir que el controlador del temporizador se ejecute primero.

Dim m_iOperSelLeft As Integer 
Dim m_iSelTop As Integer 
Dim m_iSelWidth As Integer 
Dim m_iSelHeight As Integer 

Private Sub MySubForm_Exit(Cancel As Integer) 

    m_iSelLeft = MySubForm.Form.SelLeft 
    m_iSelTop = MySubForm.Form.SelTop 
    m_iSelWidth = MySubForm.Form.SelWidth 
    m_iSelHeight = MySubForm.Form.SelHeight 

    TimerInterval = 1 

End Sub 

Private Sub Form_Timer() 

    TimerInterval = 0 

    MySubForm.Form.SelLeft = m_iSelLeft - 1 
    MySubForm.Form.SelTop = m_iSelTop 
    MySubForm.Form.SelWidth = m_iSelWidth 
    MySubForm.Form.SelHeight = m_iSelHeight 

End Sub 
1

Utilice una variable global en el formulario, luego refiérase a eso en el código del botón.

Dim g_numSelectedRecords as long 

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) 
    g_numSelectedRecords = Me.SelHeight 
End Sub 


Dim formRecords As DAO.Recordset 
Dim i As Long 

Set formRecords = Me.RecordsetClone 

' Move to the first record in the recordset. 
formRecords.MoveFirst 

' Move to the first selected record. 
formRecords.Move Me.SelTop - 1 

For i = 1 To numSelectedRecords 
    formRecords.Edit 
    formRecords.Fields("Archived") = True 
    formRecords.Update 
    formRecords.MoveNext 
Next i 
2

Hay otra solución.

El siguiente código mostrará el número de filas seleccionadas tan pronto como suelte el botón del mouse. Guardar este valor hará el truco.

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) 

     MsgBox Me.SelHeight 

End Sub 
+0

Oye, es posible que desee corregir el formato de su código, el principio y el final no están sangrados. – cpburnz

+0

Esto funciona bien, pero está limitado solo a las operaciones del mouse. El teclado también se puede usar para seleccionar múltiples columnas y filas, pero esto no las capturará. Lamentablemente, los eventos KeyPress y KeyUp no parecen capturar el control y las teclas de flecha cuando se navega alrededor de una Hoja de Datos. Las otras soluciones publicadas que involucran el evento Parent Exit son más generales y no dependen del método de selección. –

0

Por qué no utilizar una matriz o conjunto de registros y luego cada vez que el usuario hace clic en una fila (ya sean contiguos o no, salvo que la fila o algún identificador en el conjunto de registros. Luego, cuando se haga clic en el botón en el formulario padre , simplemente itere el conjunto de registros que se guardó para hacer lo que desee. Simplemente no olvide borrar la matriz o conjunto de registros después de hacer clic en el botón.?

0

Otra solución para mantener la selección mientras se intenta ejecutar un procedimiento - En su lugar de dejar la hoja de datos para activar un botón, solo use el evento OnKeyDown y defina un código clave específico y combinación de cambio para ejecutar su código.

-1

El código proporcionado por JohnFx funciona bien. He implementado sin un temporizador de esta manera (MS-Access 2003):
1- Ajuste de la Vista previa del formulario clave para Sí
2- poner el código en una función
3- establezca el evento OnKeyUp y onmouseup llamar a la función .

Option Compare Database 
Option Explicit 

Dim rowSelected() As String 

Private Sub Form_Load() 
'initialize array 
ReDim rowSelected(0, 2) 
End Sub 

Private Sub Form_Current() 
' if cursor place on a different record after a selection was made 
' the selection is no longer valid 
If "" <> rowSelected(0, 2) Then 
    If Me.Recordset.AbsolutePosition <> rowSelected(0, 2) Then 
    rowSelected(0, 0) = "" 
    rowSelected(0, 1) = "" 
    rowSelected(0, 2) = "" 
    End If 
End If 
End Sub 

Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) 
rowsSelected 
If KeyCode = vbKeyDelete And Me.SelHeight > 0 Then 
    removeRows 
End If 
End Sub 

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) 
rowsSelected 
End Sub 

Sub rowsSelected() 
Dim i As Long, rs As DAO.Recordset, selH As Long, selT As Long 
selH = Me.SelHeight 
selT = Me.SelTop - 1 
If selH = 0 Then 
    ReDim rowSelected(0, 2) 
    Exit Sub 
Else 
    ReDim rowSelected(selH, 2) 
    rowSelected(0, 0) = selT 
    rowSelected(0, 1) = selH 
    rowSelected(0, 2) = Me.Recordset.AbsolutePosition ' for repositioning 
    Set rs = Me.RecordsetClone 
    rs.MoveFirst ' other key touched caused the pointer to shift 
    rs.Move selT 
    For i = 1 To selH 
     rowSelected(i, 0) = rs!PositionNumber 
     rowSelected(i, 1) = Nz(rs!CurrentMbr) 
     rowSelected(i, 2) = Nz(rs!FutureMbr) 
     rs.MoveNext 
    Next 
    Set rs = Nothing 
    Debug.Print selH & " rows selected starting at " & selT 
End If 
End Sub 

Sub removeRows() 
' remove rows in underlying table using collected criteria in rowSelected() 
    Me.Requery 
' reposition cursor 
End Sub 

Private Sub cmdRemRows_Click() 
If Val(rowSelected(0, 1)) > 0 Then 
    removeRows 
Else 
    MsgBox "To remove row(s) select one or more sequential records using the record selector on the left side." 
End If 
End Sub 
+0

Olvidé mencionar: mi escenario implica seleccionar registros en un formulario donde la consulta fuente no es actualizable y los usuarios necesitan eliminar el registro; no es un subformulario –

Cuestiones relacionadas