Algorithm for finding end of a list (SAP GUI) - vba

I'm writing a script that adds elements to a list in a SAP GUI screen. Now, it seems that when using SAP GUI, nothing "exists" unless it is actually on screen, so the first step involves finding the end of the list.
I accomplished this by scrolling though each element, and checking if it was blank.
Do While Not blank
If session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010/ctxtMAPL-MATNR[2,0]").Text = "" Then blank = True
session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010").verticalScrollbar.Position = i
i = i + 1
Loop
However, for very large existing lists, this takes a long time. I'm trying to figure out a way to find the end more quickly. Some truths/limitations I know:
I'm assuming I have no knowledge of the list length.
I cannot command the verticalScrollbar.position too far beyond the end of
the list. For ex. if the list contains 62 elements, .verticalScrollbar.Position = 100 will not work.
In the case of the above example, SAP does NOT throw an error. Nothing happens at all, and then next line of code executes.
All references to elements are with respect to their position on the screen. Ex, if I scroll down 5 positions, the 6th element of the overall list would actually indexed as 1.
On the other hand, verticalScrollbar.Position is absolute
I'm thinking of doing the following (in very psuedocode):
i = 0
do while scrolled = true
scrolled = false
a = GUIlist[0]
verticalScrollbar.Position = i + 1000
b = GUIlist[0]
'check to see the first element shown has changed
if a <> b then
scrolled = true
i = i + 1000
end if
loop
do while scrolled = true
scrolled = false
a = GUIlist[0]
verticalScrollbar.Position = i + 500
b = GUIlist[0]
if a <> b then
scrolled = true
i = i + 500
end if
loop
...and so on until I'm iterating i by one.
Is there a generally accepted better way of doing this kind of 'search'?
Any input is appreciated.
Thanks

My suggestion:
session.findById("wnd[0]").sendVKey 83
myPosition = session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010").verticalScrollbar.Position
do
if session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010/ctxtMAPL-MATNR[2,0]").Text = "" then exit do
myPosition = myPosition + 1
session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010").verticalScrollbar.Position = myPosition
loop
msgbox myPosition
Regards,
ScriptMan

Just to go to the end
max_scrollbar = session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010").verticalScrollbar.Maximum ' Get the maximum scrollbar value
session.findById("wnd[1]/usr/tblSAPLCZDITCTRL_4010").verticalScrollbar.Position = max_scrollbar ' Go to the end

Related

How can I improve this iterative function?

I am trying to simplify this function which is similar from
Function Snake_makestep()
maincar.Location = locate(xpos, ypos)
car0.Location = locate(posx(0), posy(0))
car1.Location = locate(posx(1), posy(1))
If car2.Visible = True Then
car2.Location = locate(posx(2), posy(2))
End If
If car3.Visible = True Then
car3.Location = locate(posx(3), posy(3))
End If
If car4.Visible = True Then
car4.Location = locate(posx(4), posy(4))
End If
To
If car30.Visible = True Then
car30.Location = locate(posx(30), posy(30))
End If
End Function
I'm not sure If I can/how to use Controls.Find solution within this function. Any help?
To answer the question as asked:
For i = 2 To 30
Dim car = Controls("car" & i)
If car.Visible Then
car.Location = locate(posx(i), posy(i))
End If
Next
Visible and Location are both members of the Control class so it doesn't matter what type of control those car variables are.
Note that this assumes that all controls are on the form itself. If they are in a different parent container, you'd need to use the Controls collection of that container.
Also, I have started i at 2 there, so you'd still need the explicit code for car0 and car1. If they are always visible then you could start the loop at 0 and it would still work, saving you those two lines of code as well.

How do I collapse white space in a list of string that was randomized

I have a list of strings that hold 10 answers.
Each question has a different amount of answers 2-10.
After randomizing the list, I end up with white space or empty spaces in my list depending on the number of answers.
After randomizing the list of let's say 2 answers, i would like to shift them back to position 0 and 1 in my list keeping the size of the list at 10 and of course keeping the order randomized.
I'm not sure how to programmatically solve this problem...
I've tried to sort/reverse the list after randomize, but of course, this removes the randomization.
I've tried to remove the white space with something like
answerlist.RemoveAll(Function(str) String.IsNullOrWhiteSpace(str))
but I then get an out of bounds when trying to write them back to my radiobuttons.text as there are 10 of them.
This is where writing my list of...
RadioAnswer1.Text = answerlist(0)
RadioAnswer2.Text = answerlist(1)
RadioAnswer3.Text = answerlist(2)
RadioAnswer4.Text = answerlist(3)
RadioAnswer5.Text = answerlist(4)
RadioAnswer6.Text = answerlist(5)
RadioAnswer7.Text = answerlist(6)
RadioAnswer8.Text = answerlist(7)
RadioAnswer9.Text = answerlist(8)
RadioAnswer10.Text = answerlist(9)
Ideally, I want the list randomized then however many answers there are written back into the list starting at 0 going down to 10.
I hope my question is clear.
Additional info edit
So here is how I'm loading the answers into the List Of..
Dim answerlist As New List(Of String)
WEFESQLConn.ConnectionString = connectstring
WEFESQLConn.Open()
WERESQLStatment.CommandText = "SELECT * FROM [WEFE Questions] WHERE QuestionID = " & SQLQuestionNum.ToString
WERESQLStatment.Connection = WEFESQLConn
WEFESQLRead = WERESQLStatment.ExecuteReader
If WEFESQLRead.HasRows Then
WEFESQLRead.Read()
lblQuestion.Text = WEFESQLRead.Item("Question").ToString
answerlist.Add(WEFESQLRead.Item("CorrectAnswer").ToString)
answerlist.Add(WEFESQLRead.Item("Answer2").ToString)
answerlist.Add(WEFESQLRead.Item("Answer3").ToString)
answerlist.Add(WEFESQLRead.Item("Answer4").ToString)
answerlist.Add(WEFESQLRead.Item("Answer5").ToString)
answerlist.Add(WEFESQLRead.Item("Answer6").ToString)
answerlist.Add(WEFESQLRead.Item("Answer7").ToString)
answerlist.Add(WEFESQLRead.Item("Answer8").ToString)
answerlist.Add(WEFESQLRead.Item("Answer9").ToString)
answerlist.Add(WEFESQLRead.Item("Answer10").ToString)
answerlist.RemoveAll(Function(str) String.IsNullOrWhiteSpace(str))
WEFESQLRead.Close()
WEFESQLConn.Close()
RadioAnswer1.Text = answerlist(0)
RadioAnswer2.Text = answerlist(1)
RadioAnswer3.Text = answerlist(2)
RadioAnswer4.Text = answerlist(3)
RadioAnswer5.Text = answerlist(4)
RadioAnswer6.Text = answerlist(5)
RadioAnswer7.Text = answerlist(6)
RadioAnswer8.Text = answerlist(7)
RadioAnswer9.Text = answerlist(8)
RadioAnswer10.Text = answerlist(9)
With this code I get the out of bounds as there are not enough answers to populate the answerlist.
Without
answerlist.RemoveAll(Function(str) String.IsNullOrWhiteSpace(str))
I get the spaces in my pre-drawn radio buttons.
I am all ready hiding the unused buttons - the issue there are 10 positions for the buttons and with the randomization of the list.
4 spots of 10 used image
Why don't you toggle the visibility of the controls dynamically after removing the blank entries from the list? Take a look at this example:
'Store all controls in a collection
Dim answers(9) As RadioButton = {RadioAnswer1, RadioAnswer2, RadioAnswer3, RadioAnswer4, RadioAnswer5, RadioAnswer6, RadioAnswer7, RadioAnswer8, RadioAnswer9, RadioAnswer10}
'Iterate through all answers
For index As Integer = 0 To answerlist.Count - 1
'Show the control and set the text
With answers(index)
.Text = answerlist.Item(index)
.Visible = True
End With
Next
'Loop through the rest of the answer controls
For index As Integer = answerlist.Count To answers.Length - 1
'Hide the control
answers(index).Visible = False
Next
Initial setup...
Dim radioButtons As New List(Of RadioButton)
radioButtons.Add(RadioAnswer1)
radioButtons.Add(RadioAnswer2)
...
radioButtons.Add(RadioAnswer10)
After removing blank answers from the randomized list...
For i = 0 To answerList.Count - 1
radioButtons(i).Text = answerList(i)
radioButtons(i).Visible = True
Next
For i = answerList.Count to radioButtons.Count - 1
radioButtons(i).Visible = False
Next
To close this out I ended up going with removing all the spaces from my ListOF then using a DO WHILE for each radiobutton.text entries.
example portion
answerlist = RandomizeListOrder(answerlist)
answerlist.RemoveAll(Function(str) String.IsNullOrWhiteSpace(str))
Dim ALCount As Integer = answerlist.Count
Dim ALCounter = 0
Do
If ALCounter < ALCount Then
ALCounter += 1
RadioAnswer1.Text = answerlist(0)
ElseIf ALCounter = ALCount Then
Exit Do
End If
Maybe not very clean as there are 10 of these entries, but I'll work on that later. Just trying to get the idea out.
Thanks everyone for the suggestions they got me on the right path.

VBA - Rotate Word.Shapes In A Word-Document

Here's the question I'm having. I need to rotate Word.Shapes in a single Word-Document, but my script will only rotate the first one, and i can't figure out why.
Here's how the Word-Document comes to be (opens a PDF with one Shape per page):
Set wrdDoc = wrdAppMain.Documents.Open(FileName:=sToSaveAs, Visible:=False)
Here's how the loop is designed:
For Each wrdShape In wrdDoc.Shapes
If CheckFormat(wrdShape) = False Then
FitToPage = False
GoTo ExitScript
End If
Next wrdShape
And now the part that's acting up:
Private Function CheckFormat(oShapeToCheck As Word.Shape) As Boolean
On Error GoTo Failed
Dim siAspectRatio As Single
Dim iRotation As Integer
'---- Seitenverhältnis und Rotation berechnen ----
If oShapeToCheck.Height > 0 And oShapeToCheck.Width > 0 Then
siAspectRatio = oShapeToCheck.Height / oShapeToCheck.Width
iRotation = oShapeToCheck.Rotation
Else
ErrorCode = " (PDF)"
GoTo Failed
End If
'---- Kontrolle ob Bild im Querformat vorliegt ----
If siAspectRatio < 1 Then
'---- Kontrolle ob rotiert oder natives Querformat ----
Select Case iRotation
Case 0
oShapeToCheck.IncrementRotation 90
Case 180
oShapeToCheck.IncrementRotation 270
Case 90
oShapeToCheck.IncrementRotation 0
Case 270
oShapeToCheck.IncrementRotation 180
End Select
So and here's where the problem is. Although I the first Word.Shape meeting the criteria will be rotated, any others will not. Additionally if I set the visibility for the Word-Document to TRUE, debug through, and fullscreen the Word-Document before the script performs the rotation, it will rotate any Word.Shape every time.
I tried messing around with .Activate and the like but nothing seems to work. Hope you can help me there!
Thanks!
Markus
So I found a way to make this work. Instead of rotating every Word.Shape individually, I gather them all in a ShapeRange via their Indexes (or whatever the plural is on that one) and rotate them all at once.
Select Case iRotation
Case 0
If bIsDimensioned = False Then
ReDim Preserve RotationArray(0 To 0) As Variant
RotationArray(0) = iShapeIndex
bIsDimensioned = True
Else
ReDim Preserve RotationArray(0 To UBound(RotationArray) + 1) As Variant
RotationArray(UBound(RotationArray)) = iShapeIndex
End If
End Select
And after the ShapeRange is fully populated:
If bIsDimensioned = True Then
Set RotationShapeRange = wrdDoc.Shapes.Range(RotationArray)
RotationShapeRange.IncrementRotation 90
RotationShapeRange.WrapFormat.Type = wdWrapTight
RotationShapeRange.RelativeHorizontalPosition = wdRelativeHorizontalPositionPage
RotationShapeRange.RelativeVerticalPosition = wdRelativeVerticalPositionPage
RotationShapeRange.Left = wdShapeCenter
RotationShapeRange.Top = wdShapeCenter
End If
That should be it!
Frustrating, that new code is repasted in broken sections - can't get to work.

Selecting layer to search on all pages

This is part of the code im working with;
Dim s As Shape
Dim p As Page, numberPage As Integer
Dim i&
Dim WhatSamp As String
WhatSamp = "Sample1"
For i = 1 To ActiveDocument.Pages.Count
ActiveDocument.Pages(i).Activate
For Each s In ActiveDocument.ActivePage.Shapes
If s.Type = cdrTextShape Then
If InStr(1, s.Text.Story, WhatSamp) > 0 Then
ActivePage.Layers("Sample").Visible = True
ActivePage.Layers("Sample").Printable = True
End If
End If
Next
Next i
The code im working with is much longer but i believe this is the relevant part.
It searches for my text on the page (Sample1) then displays and makes printable the layer called "Sample".
I think because I have a ridiculous amount of needed layers it takes forever to run
So, Im trying to get it to search for my text only on a specific layer that exists on each page called "Style" but i cant seem to figure it out.
Thank you in advance.
Let me know if more information is needed
To test on what layer is object "S" use if
if S.Layer.Name="Style" then
then
If InStr(1, s.Text.Story, WhatSamp) > 0 Then
ActivePage.Layers("Sample").Visible = True
ActivePage.Layers("Sample").Printable = True

Deleting empty Series out of Graph (with VBA)

I am trying to remove all empty series out of an Excel graph.
Dim isEmptySeries As Boolean
For Series = 1 To .SeriesCollection.count
.SeriesCollection(Series).ApplyDataLabels Type:=xlDataLabelsShowValue, AutoText:=True, LegendKey:=False
isEmptySeries = True
For i = 1 To .SeriesCollection(Series).points.count
If .SeriesCollection(Series).points(i).DataLabel.Text = 0 Then
.SeriesCollection(Series).points(i).HasDataLabel = False
Else
isEmptySeries = False
.SeriesCollection(Series).points(i).DataLabel.Font.Size = 17
End If
Next i
If isEmptySeries Then
.SeriesCollection(Series).Delete
End If
Next Datenreihe
The script fails at the ApplyDatalabels line ("Method SeriesCollection of Object Chart failed").
I believe that Excel shifts the Series indexes when one of the Series is deleted? Is that the case? It's the only explanation that I have for the error.
How else would I loop through the series and remove the ones that are empty?
In these sorts of situations try looping in reverse order
For i = .SeriesCollection(Series).points.count To 1 Step -1
That way the .Delete doesn't effect the item yet to be looped through