Rapport lijst met velden

Status
Niet open voor verdere reacties.

danny88

Gebruiker
Lid geworden
14 apr 2011
Berichten
70
Gebruik de volgende code om een rapport op te bouwen:

Code:
Option Compare Database
Option Explicit

Const Kolom = 9
Const KolomkopVoorvoegsel = "K_"


Private Sub Report_Open(Cancel As Integer)

Dim rstRpt As DAO.Recordset, fldRpt As DAO.Field
Dim intVeldnr As Integer, strVeldnaam As String

    Set rstRpt = CurrentDb.OpenRecordset(Me.RecordSource, dbOpenSnapshot)
    intVeldnr = 0
    For Each fldRpt In rstRpt.Fields
        strVeldnaam = fldRpt.Name
        If Left(strVeldnaam, Len(KolomkopVoorvoegsel)) = KolomkopVoorvoegsel Then
            intVeldnr = intVeldnr + 1
            Me("L" & intVeldnr).Caption = strVeldnaam
            Me("V" & intVeldnr).ControlSource = strVeldnaam
        End If
    Next

Exit_Report_Open:
    rstRpt.Close
    Set fldRpt = Nothing
    Set rstRpt = Nothing
    
End Sub

Maar is het mogelijk om te bekijken of een veld niet voorkomt in de lijst met velden?
If "veldnaam" = "niet in de lijst met velden" then
 
Laatst bewerkt:
Dat doe je toch al? Je controleert al of Left(strVeldnaam, 2) = KolomkopVoorvoegsel; als die check goed is, dan bestaat de veldnaam toch? Anders zet je daarboven nog een aparte IF check om te controleren dat de variabele niet leeg is.
 
Ik ben niet duidelijk genoeg geweest. Dacht laat ik maar zo kort mogelijk zijn.
Maar dat is niet de oplossing.

Code:
Option Compare Database
Option Explicit

Const Kolom = 9
Const KolomkopVoorvoegselA = "A"
Const KolomkopVoorvoegselB = "B"

Private Sub Report_Open(Cancel As Integer)

Dim rstRpt As DAO.Recordset, fldRpt As DAO.Field
Dim intVeldnr As Integer, strVeldnaam As String

    Set rstRpt = CurrentDb.OpenRecordset(Me.RecordSource, dbOpenSnapshot)
    intVeldnr = 0
    For Each fldRpt In rstRpt.Fields
        strVeldnaam = fldRpt.Name
        If Left(strVeldnaam, Len(KolomkopVoorvoegselA)) = KolomkopVoorvoegselA Then
            intVeldnr = intVeldnr + 1
            Me("LA" & intVeldnr).Caption = strVeldnaam
            Me("VA" & intVeldnr).ControlSource = strVeldnaam
                If ("B" & Right(strVeldnaam, 2)) = ?????? then
                     Me("LB" & intVeldnr).Caption = "B" & Right(strVeldnaam, 2)
                     Me("VB" & intVeldnr).ControlSource = "B" & Right(strVeldnaam, 2)
                End If
        End If
    Next

Exit_Report_Open:
    rstRpt.Close
    Set fldRpt = Nothing
    Set rstRpt = Nothing
    
End Sub

Het rapport is gebasseerd op een kruistabelquery.
De kolomkoppen bestaan uit A+nr en B+nr de nummers wisselen door wat ik met het filter mee geef.
De bedoeling is dat A en B met het zelfde nr naast elkaar komen te staan op het rapport.

Door de code laat ik eerst de A+nr invullen en krijgt een internveldnr mee.
Mijn idee was dan om te kijken of B+nr (vanA) voorkomt in de lijst, zo ja dan het label en de controlsource instellen.

Hoop dat het iets duidelijker is.
 
Je hebt zo te zien maar een deel van een code gebruikt uit een andere post. Wat jij doet is de recordset gebruiken zonder dat je controleert op de veldnamen. In die andere post worden eerst de veldnamen uitgelezen en in een array variabele gezet, en vervolgens worden de veldnamen gekoppeld aan vaste tekstvakken op het rapport. Daarmee omzeil je dus ook het probleem van niet-bestaande veldnamen... Dat voorbeeld (inclusief db) vind je hier.
 
Ik had een voorbeeld gevonden op deze site en had de post gezien en bekeken die jij aandroeg OctaFish.
HTML:
http://office.webforums.nl/viewtopic.php?p=52931


Alleen als ik de veldnamen wil koppelen aan vaste tekstvakken (dus veldnaam 4 = tekstvak4) moet ik 2 maal 60 = 120 tekstvakken maken op mijn rapport.

Mijn rapport bestaat uit VeldA1, VeldB1, Veld A2, VeldB2, VeldA3, VeldB3 enz. tm 15

Als veldnamen heb ik bijvoorbeeld A03, A04, A06, A07, A08, B04, B07

De bedoeling is dat:
A03 op VeldA1
A04 op VeldA2
A06 op VeldA3
A07 op VeldA4
A08 op VeldA5
B04 op VeldB2 naast A04
B07 op VeldB4 naast A07

Volgens mij kan dat niet met de code die jij bedoelde OctaFish.
Maar wie ben ik om dat te zeggen.
 
Dat kan exact met de code uit mijn eerdere voorbeeld; het enige dat je vast op je rapport hebt (al is zelfs dat nog te omzeilen) is het aantal teksvakken dat je wilt gebruiken; met de code lees je de veldnamen uit, en koppel je die vervolgens aan de beschikbare tekstvakken. daarbij kan dus A04 probleemloos op tekstveld2, en A06 op tekstveld3. Bekijk de code nog maar eens goed, zou ik zeggen!
 
Als ik deze code gebruik moet ik in mij geval een 60 tal tekstvelden aanmaken, omdat de veldnaam A50 gekoppeld wordt aan tekstveld VA50.
Lijkt mij niet handig.

Code:
    With CurrentDb.OpenRecordset("SELECT*FROM_test")
        i = .Fields.Count
        For i = 3 To i - 1
            ReDim Preserve sVelden(j)
            sVelden(j) = .Fields(i).Name
            j = j + 1
        Next i
    End With
    
    With CurrentDb.OpenRecordset("SELECT * FROM qry_test")
        On Error Resume Next
        For j = 1 To 12
            For i = LBound(sVelden) To UBound(sVelden)
                If Me("VA" & j).Name = "V" & sVelden(i) Then
                    Me("V" & j).ControlSource = sVelden(i)
                    Me("l" & j).Caption = sVelden(i)
                    Me("l" & j).Visible = True
                End If
            Next i
        Next j
    End With

Haal ik deze regel code weg.
Dan koppelt die de eerste veldnaam die die tegen komt aan het eerste tekstveld.
Dat is prima, maar kom er niet echt uit als bijvoorbeeld Veldnaam A7 gekoppeld is aan tekstveld VA3. Hoe dan veldnaam B7 gekoppeld wordt aan tekstveld VB7.

Code:
 If Me("VA" & j).Name = "V" & sVelden(i) Then

Kan je een hint in de goede richting geven?
 
Dus eigenlijk wil je dit:
A03 op VeldA1
A04 op VeldA2
B04 op VeldB2 naast A04
A06 op VeldA3
A07 op VeldA4
B07 op VeldB4 naast A07
A08 op VeldA5
Overigens snap je denk ik het principe van de code nog niet, want die leest alle velden in de volgorde uit waarin ze in de query staan, en koppelt de (gebruikte) velden vervolgens in oplopende volgorde aan de tekstvelden op het rapport. A50 komt dus alleen aan VA50 als je alle velden gebruikt. Mij lijkt het een kwestie van de kruistabel op de juiste manier maken. En daarin de velden dus in de goede volgorde te zetten, zodat ze goed uitgelezen worden.
 
Wat er in de tabel staat bedoel ik.
(leeg) = komt niet voor in de lijst met velden.


Rapport tekstvakken
A1-B1 A2-B2 A3-B3 A4-B4 A5-B5
Veldnamen
A5-B5 A6-(leeg) A7-B7 A8-(leeg) A9-(leeg)

Kort gezegd:
Als A6 voorkomt in de lijst met velden en deze geplaatst is op tekstveld A2 in het rapport
en B6 komt niet voor in de lijst met velden dat dan tekstveld B2 overgeslagen wordt.

Denk zelf dat het wat te ingewikkeld wordt.
Misschien toch handiger om meer tekstvakken te maken op het rapport en dan
veld A3 te koppelen aan tekstvak A3 en B7 te koppelen aan tekstvak B7.
en de ingevulde tekstvakken zichtbaar maken.
 
Laatst bewerkt:
In je nieuwe voorbeeldje heb je het alleen over de B-velden die leeg zijn, en zijn de gebruikte A-velden niet leeg. Als dit de de facto standaard is van de kruistabel, dan is dat weer heel simpel op te lossen, door de rapportvelden met een lus te vullen die je dan zo opbouwt: For j = 1 To 12 Step 2. Daarbij pak je steeds de oneven velden voor je gevulde A-velden, en hang je de gevulde B-velden op de even velden ernaast. Je kunt dan simpel checken op veldnaam, om de lege velden te elimineren.
 
Als ik in de lijst met velden bijvoorbeeld A3,A4 en A6 heb kan (hoef niet) B3,B4 en B6 voorkomen, maar geen B2 of B8.

Heb zelf niet veel kaas gegeten van om in vba code te typen, alleen wat knip en plak werk.
Met deze code zet je de velden in een matrix variabele:

Code:
With CurrentDb.OpenRecordset("SELECT*FROM_test")
        i = .Fields.Count
        For i = 3 To i - 1
            ReDim Preserve sVelden(j)
            sVelden(j) = .Fields(i).Name
            j = j + 1
        Next i
    End With

Mijn eerste voorzet, maar ben bang dat ik toch helemaal op de verkeerde weg ben.
Heb eigenlijk geen idee hoe ik de check op veldnaam erin krijg.

De eerste lus voor de oneven velden.
De tweede lus voor de even velden.

Code:
    With CurrentDb.OpenRecordset("SELECT * FROM qry_test")
        On Error Resume Next
        For j = 1 To 11 step 2
            For i = LBound(sVelden) To UBound(sVelden)
                If "A" = Left (sVelden(i),1) Then
                    Me("VA" & j).ControlSource = sVelden(i)
                    Me("lA" & j).Caption = sVelden(i)
                End If
            Next i
        Next j

	For j = 2 To 12 step 2
            For i = LBound(sVelden) To UBound(sVelden)
                If "B" = Left (sVelden(i),1) Then
                    Me("VB" & j).ControlSource = sVelden(i)
                    Me("lB" & j).Caption = sVelden(i)
                End If
            Next i
        Next j
    End With
 
Het zou een boel schelen als je een voorbeeldje maakt; jouw vraag is dermate specifiek dat ik daar geen kant en klaar voorbeeld voor heb liggen, en ik heb geen tijd/zin om er zelf een te maken...
 
OctaFish: Ik heb snel een voorbeeldje gemaakt.
Heb op het rapport een teksveld C bij geplaatst, misschien dat er in de toekomst een band bij komt.

Op mijn rapport heb ik de tekstvelden vA1,vB1,vC1. en dit herhaalt zich maar dan vA2 enz. enz.

De kolomkop bestaat altijd uit band letter A,B,C en daarachter het artikelnr.
Bijvoorbeeld A03, A04, A08, B04, B08

Het mooiste zal zijn:
-dat de code op zoek gaat naar het laagste artikelnr in de lijst met velden. (bijvoorbeeld 6)
-Vervolgens checkt of A06, B06 of C06 voorkomt in de lijst met velden.
-En als B06 voorkomt deze als controlsource van tekstvak vB1 ingesteld wordt.
en als C06 voorkomt deze als controlsource van tekstvak vC1 ingesteld wordt.

-En dan het eerst volgende artikelnr uit de lijst met velden pakken en vervolgens het hele verhaal herhalen tot het hoogste artikel nr bereikt is.
 

Bijlagen

Het schiet aardig op, maar is nog niet helemaal af. Toch een tussenfase gepost, want hier kun je misschien zelf ook al wel verder mee stoeien. Ik heb het bestandje omgezet naar 2003, zodat ik er zelf op het werk ook nog even naar kan kijken.
De stand van zaken: ik heb een formulier gemaakt waarmee je een datum kan selecteren. Vervolgens kun je met een knop de kruistabel aanpassen. Wat die knop doet is het volgende:
1. Eerst wordt de kruistabel opgebouwd op basis van de geselecteerde gegevens. Eigenlijk zoals de oude query ook werkte. Deze maakte alleen kolommen aan voor velden die ook echt een waarde hadden, en niet voor de ontbrekende velden.
2. Vervolgens wordt deze query in een functie gedouwd, die de veldnamen uitleest. Omdat het alleen om series gaat die met een A beginnen, heb ik die letter als basis genomen. De functie zoekt dus alle veldnamen uit de kruistabel op die met A beginnen, en voegt daar vevolgens een B en een C variant aan toe. Dit gebeurt op basis van een Ascii code, dus je kunt heel eenvoudig extra kolommen toevoegen mocht dat nodig zijn. Het eindresultaat wordt teruggelezen in een variabele.
3. De variabele wordt vervolgens gebruikt om kolommen te definiëren in de kruistabel. Daarbij worden dus setjes van 3 kolommen gemaakt: voor elke A-waarde, en de bijbehorende B- en C-waarde.

Eigenlijk een redelijk simpele functie dus!

Wat nog moet gebeuren is het volgende: op het rapport moeten de veldnamen van de query worden uitgelezen, en worden gekoppeld aan de rapportvelden. Deze moeten dan ook zichtbaar worden gemaakt.
Eigenlijk de basisfunctie die je al had voor het rapport. En dat is ook gelijk de reden dat ik aan deze oplossing denk: als je er voor zorgt dat je Kruistabel in orde is, dus alle noodzakelijke velden bevat, dan hoef je op het rapport alleen maar de velden uit te lezen, te koppelen en zichtbaar te maken.

Hopelijk kom je er uit!
 

Bijlagen

Ik vermoed dat je nog aan het puzzelen bent.... Daarom hier alvast een werkend voorbeeldje ter inspiratie!
 

Bijlagen

Laatst bewerkt:
Zo helemaal super :thumb: en eigenlijk vrij simpel.
Ben toch altijd verbaasd :shocked::shocked: hoe je die oplossingen verzint.

Maar het puzzelen viel best mee, ben de hele dag onderweg geweest en nu pas tijd om je voorbeeld te bekijken.
Het is allemaal goed te begrijpen op een paar dingetjes na zoals:


Met deze regel wordt de functie VeldenZoeken al aangeroepen?
Ben zelf altijd gewent op call ...... te gebruiken.
En door (strSQL) er achter te zetten vertel je de funtie waaruit de velden gehaald moeten worden?
Code:
sIn = "In (" & VeldenZoeken(strSQL) & ")"


Dit stukje gaat me boven de pet.
Hiermee wordt een recordset geopend volgens mij, alleen snap al die andere termen niet echt.
Code:
Function VeldenZoeken(Kruistabel As String) As String

Set rst = New ADODB.Recordset
rst.CursorLocation = adUseClient
rst.Open Kruistabel, CurrentProject.Connection, adOpenKeyset, adLockOptimistic

Heb het een en ander ook al gevonden door wat te googlen.
Ga ermee aan de slag om het in mijn database toe te passen, maar dat moet wel lukken denk.

Bedankt!!
 
Laatst bewerkt:
Als je tijdaanpassing klopt, google je niet alleen snel, maar lees en snap je het ook nog eens in formidabel tempo!
Maar een beetje uitleg kan natuurlijk nooit kwaad... Een functie mag je met Call aanroepen, maar vaak hoeft dat niet eens. Hangt een beetje van het bereik van de functie af. Een functie heeft (minstens) één groot verschil met een procedure: hij kan wat terugleveren. Dat gebeurt door de functie als variabele te declareren, zoals hier dus:
Code:
Function VeldenZoeken(Kruistabel As String) [B]As String[/B]
Hierin is Kruistabel de input voor de functie (een query dus, in dit geval een kruistabel, maar dat maakt verder niet uit) en wordt het resultaat van de functie in een stringvariabele gezet. Die variabele wordt vervolgens zelf ook weer toegewezen aan een variabele:
Code:
sIn = "In (" & VeldenZoeken(strSQL) & ")"
Op het moment dat de procedure de functie VeldenZoeken tegenkomt in deze regel, stopt a.h.w. de uitvoering van het vullen van de variabele. Eerst wordt de functie dus uitgevoerd. Omdat de functie een query nodig heeft, staat de naam van de query tussen de haakjes van de functie. Door het zo te doen, spaar je een hoop regels uit. Vandaar deze werkwijze.

De functie zelf begint met het vastleggen van de ADO recordset. Bij een recordset kun je een aantal zaken instellen, zoals de Cursorlocation (lokaal of op de server bijvoorbeeld). Bij het openen van een recordset is een connectie nodig (CurrentProject.Connection die we uit de actieve database halen. Verder kun je nog instellen hoe de recordset moet worden gelezen. Als je de helpfunctie raadpleegt door op een sleutelwoord te gaan staan en op F1 te drukken, dan krijg je een vrij uitgebreid verhaal waarin e.e.a. wordt uitgelegd. Ik ga dat zelf in de cursus ook nog doen.

Hoop dat je wat aan de oplossing hebt! (Zo te zien wel :D )
 
OctaFish: Als ik het toepas in mijn eigen database krijg ik de volgende foutmelding:

Compileerfout: een variabele is niet gedefinieerd

Deze regels wordt geel:
Code:
Function VeldenZoeken(Kruistabel As String) As String

En van deze regel wordt rst = blauw gemaakt
Code:
Set rst = New ADODB.Recordset

Nu staat er bij mij bovenaan de database:
Option Compare Database
Option Explicit

Haal ik de regel Option Explicit weg dan krijg ik geen foutmelding.

Mij vraag: Wat moet er achter:
Dim rst as ???????????
 
Set rst = New ADODB.Recordset gaat er vanuit dat je de ADO bibliotheek hebt geladen; deze staat standaard niet altijd aan. In het VBA venster ga je naar <Extra>, <Verwijzingen>. Daar zie je een lijst met actieve bibliotheken. Daar zou <Microsoft ActiveX Data Objects 2.8> of hoger ook bij moeten staan. Zo niet: zoek 'm op in de lijst en selecteer 'm. Je vindt vermoedelijk een aantal versies vanaf 2.0. Kies bij voorkeur altijd de hoogste versie.
 
Microsoft ActiveX Data Objects 2.8 was aangevinkt.
Staat bij mij ook nog 6.0 in de lijst, is dit beter om aan te vinken?
Vind het zo rare stap van 2.8 naar 6.0.


Maar heb deze regel toegevoegd.
Dim rst As ADODB.Recordset aan de code en krijg nu geen compileerfout meer.
Neem aan dat de variabele zo goed gedeclareerd is.
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan