Using Passed Variable to Select Items like "Me.myvar.Item" - ms-access

I am trying to create a new function that I can pass variables to and those variables will be used to select and modify items.
Function
Function TabDisplay(Switch As String, TargetTab As String)
If (Me. & Switch & .Value) = -1 Then
Me.Tabs.Pages.Item("TargetTab").Visible = True
ElseIf Me.Switch.Value = 0 Then
Me.Tabs.Pages.Item(TargetTab).Visible = False
Else
Me.Switch.Value = "0"
End If
End Function
How I call the function
TabDisplay "SoftwareInstallable", "tabSoftware"
Working Code that I used before attempting this function
If Me.SoftwareInstallable.Value = -1 Then
Me.Tabs.Pages.Item("tabSoftware").Visible = True
ElseIf Me.SoftwareInstallable.Value = 0 Then
Me.Tabs.Pages.Item("tabSoftware").Visible = False
Else
Me.SoftwareInstallable.Value = "0"
End If
====Final Code====
Function
Function TabDisplay(Switch As String, TargetTab As String)
If Me.Controls(Switch).Value = -1 Then
Me.Tabs.Pages.Item(TargetTab).Visible = True
ElseIf Me.Controls(Switch).Value = 0 Then
Me.Tabs.Pages.Item(TargetTab).Visible = False
Else
Me.Controls(Switch).Value = "0"
End If
End Function
Function Call
TabDisplay "SoftwareInstallable", "tabSoftware"

I think I understand what you want. But let me describe it with a different example.
I have a form with a text box named txtFoo. So I can Debug.Print its value to the Immediate window like this ...
Debug.Print Me!txtFoo.Value
Later I decide I want to store the control name in a variable so that I can Debug.Print the value of any other control simply by changing the variable.
Dim strControlName As String
strControlName = "txtBar"
Then I can use that variable to reference the matching item in the form's Controls collection, and obtain its value.
Debug.Print Me.Controls(strControlName).Value
I think you can use that approach to accomplish what you want, but I won't attempt to rewrite your code sample because I suspect I would get it wrong.

Related

Hide Rows / Autofilter Listener - OpenOffice Basic

I need to dev Listener to detect changes of isVisible setting for a rows in calc.
Even better it would be for me to have autofilter changes listener - this is also beyond my skills. I would be appreciative for any or both solutions help.
XEventListener nor XModifyListener dont detects this changes.
Maybe try to use XChangesListener XChangesNotifier? <- anyway, i had problem to implement it for tests too
Sub add_eventsListener
Dim ePrefix As String, eService As String
ePrefix = "event_"
eService = "com.sun.star.document.XEventListener"
If IsNull(mEventHandler) Then
mEventHandler = CreateUnoListener(ePrefix, eService)
ThisComponent.addEventListener(mEventHandler)
EndIf
End Sub
Sub event_notifyEvent(oEvent)
msgbox "event: " & oEvent.EventName
End Sub
Sub add_modifyListener(ByRef Sheet)
Dim ePrefix As String : Dim eService As String
Dim cell as Object
ePrefix = "event_"
eService = "com.sun.star.util.XModifyListener"
cell = Sheet.getCellrangeByName("A2:A9")
If IsNull(mModifyHandler) Then
mModifyHandler = CreateUnoListener(ePrefix, eService)
cell.AddModifyListener(mModifyHandler)
EndIf
End Sub
Sub event_modified(oEvent)
'If oEvent.Source.CellAddress.Column <> 0 Then Exit Sub
Msgbox "changes made"
End Sub
where mEventHandler and mModifyHandler are global
Sub add_autofilter(ByRef Sheet)
On Error GoTo Err
Dim Range As New com.sun.star.table.CellRangeAddress
Dim FilterOn As Boolean, dRange As Object, cell As Object, row%
FilterOn = False
cell = Sheet.getCellRangeByName("A1")
row = getLastRow(Sheet)
On Error Resume Next
dRange = ThisComponent.DatabaseRanges.getByName("Symbols")
FilterOn = dRange.AutoFilter
On Error GoTo 0 : On Error GoTo Err
If FilterOn Then Exit Sub
With Range
.Sheet = 0
.StartColumn = 0
.StartRow = 0
.EndColumn = 0
.EndRow = row
End With
'Range = Sheet.getCellRangeByPosition(0, 0, 0, row)
ThisComponent.DatabaseRanges.addNewByName("Symbols", Range)
ThisComponent.DatabaseRanges.getByName("Symbols").AutoFilter = True
Exit Sub
Err:
End Sub
Function getLastRow(ByRef Sheet) As Integer
Dim cursor
cursor = Sheet.createCursor()
cursor.gotoEndOfUsedArea(false)
getLastRow = cursor.getRangeAddress().EndRow
End Function
While I am waiting for rational solution, I found a workaround - If no autofilter listener will be possible, Ill have to stay with that:
add formula in some cell: (old solution, check EDIT below)
"=IF(NOW()>0;ROWS_FILTERED();0)"
Function ROWS_FILTERED() As Integer
If Freezed Then Exit Function
Dim i%, rows%, Sheet As Object : Sheet = ThisComponent.Sheets(0)
rows = getLastRow(Sheet)
For i = 1 to getLastRow(Sheet) 'row 0 is for labels
If Sheet.Rows(i).IsVisible = True Then
rows = rows - 1
End If
Next i
ROWS_FILTERED = rows
End Function
And if you make changes where computations are not expected, just assign True to Global Freezed for that time
EDIT:
eureka! I have found this shiny formula that works and updating without workaround:
Eureka! Mimo że nie osiągnąłem rezultatu z czystego BASIC'a, znalazłem genialną formułę, która się odnosi bezpośrednio do autofiltra!! i updateuje bez obejścia:
"=SUBTOTAL(3;A2:A" & getLastRow(Sheet) + 1 & ")"
dont forget to include getLastRow(ByRef Sheet) function to your code

How can a reference fields on an Access form using a variable?

I have 20 text boxes on an Access 2010 form called [P101] to [P110] which refers to fields [P101] to [P110] in the source table. The may contain a value or not, but if not I do not want to see them. I also have a field [UsedFields] in the table which has counted how many of the fields are in use. In Form_Current I can set the following code but is there a way I could set a FOR NEXT loop to use a variable for the Field Name?
The current code (which works but is very clumsy) is:
If UsedFields > 0 then
P101.Visible = True
Else
P101.Visible = False
End If
If UsedFields > 1 then
P102.Visible = True
Else
P102.Visible = False
End If
.
.
.
.
If UsedFields > 9 then
P110.Visible = True
Else
P110.Visible = False
End If
As the number of fields is set to increase from 10 to 100 I would like to use a variable to hold the TextBox name, something like:
Private Sub Form_Current()
Dim Ctrl As Control
Dim CtrlName As String
Dim Counter As Long
For Counter = 1 To 10
CtrlName = "P" & Counter
Set Ctrl = CtrlName
If Counter > Me.UsedFields Then
Ctrl.Visible = False
Else
Ctrl.Visible = True
End If
End Sub
Is such a reference possible?
You can use your string variable to refer to an item in the form's Controls collection.
Dim Ctrl As Control
Dim CtrlName As String
Dim Counter As Long
For Counter = 1 To 10
CtrlName = "P" & Counter
Set Ctrl = Me.Controls(CtrlName)
If Counter > Me.UsedFields Then
Ctrl.Visible = False
Else
Ctrl.Visible = True
End If
Next
BTW, you can use a single line in place of the If block if that makes sense.
Ctrl.Visible = Not (Counter > Me.UsedFields)

How to avoid name error in ms access formula?

I want to set DefaultValue of a form field to
=IIf(IsNull([Forms]![MyForm]![MySubForm].[Form]![MyField]);"";[Forms]![MyForm]![MySubForm].[Form]![MyField])
When MyForm is closed I want to set the field value to "". Unfortunately I can't find how to test if MyForm is closed or not : I have tried IsNull, IsObject, very Is it always return #Name?
Is this possible ?
I use a function like this to check to see if a form is open:
Function IsLoaded(ByVal strFormName As String) As Boolean
' Returns True if the specified form is open in Form view or Datasheet view.
Const conObjStateClosed = 0
Const conDesignView = 0
If SysCmd(acSysCmdGetObjectState, acForm, strFormName) <> conObjStateClosed Then
If Forms(strFormName).CurrentView <> conDesignView Then
IsLoaded = True
End If
End If
End Function

VBA custom procedure/function in Worksheet called outside its module

I've been playing with this problem for some time, and havent figured out how to do it.
I have the same function in every worksheet (and those sheets are named like this Name="One", CodeName="SheetOne" ...):
const someVar as Boolean = True
Public Function myFunction() as Boolean
myFunction = someVar
End Function
Now I want it to be called from outside like this - In ThisWorkbook there is procedure "doThis()" and function "TestThis():
Sub doThis()
Dim i as Integer
For i = 1 to ThisWorkbook.Sheets.Count
If testThis(ThisWorkbook.Worksheets(i)) = True then
Debug.print "Success!"
End If
Next i
Function testThis(x As Worksheet)
If x.myFunction = True Then
testThis = True
Else
testThis = False
End If
Now I know that at this line "If x.myFunction = True" it throws error "Method or data member not found", because i cant call that function with this reference, so I've tried it with VBComponent:
Sub doThis()
Dim i as Integer
For i = 1 to ThisWorkbook.Sheets.Count
If testThis(ThisWorkbook.VBProject.VBComponents(Worksheets(i).CodeName)) _
= True then
Debug.print "Success!"
End If
Next i
Function testThis(x As VBComponent)
If x.myFunction = True Then
testThis = True
Else
testThis = False
End If
But again, it throws an "Object does not support this property or method" error. Any ideas, How can I call that function from a variable, that has stored reference of any kind to that Worksheet? Any help would be appreciated.
When compiling the code, Excel checks to see if "WorkSheet" object type has a myFunction method - it is looking at the generic built-in Worksheet object, and this doesn't include your "add on" function (and neither does the "VBComponent" type), so it throws an error.
If you change your parameter type to Object (a more generic type) then it will compile...
Function testThis(x As Object)
testThis = x.myFunction()
End Function
I know this is an old thread but I thought I may as well share my discoveries.
Also, I suspect there is a better way to evaluate the exact same function in every sheet but, for anyone searching, here is a solution to do exactly what you asked for in the title of the post.
In your ThisWorkbook include
Const myFunction = "myFunctuion"
Public ws as Worksheet
For each ws in Me.Sheets
If testThis(ws) then Debug.print "Success!"
next ws
Function testThis(x as Worksheet)
testThis = callByName x, x.CodeName & "." & myFunction
End Function

Return multiple values from a function, sub or type?

So I was wondering, how can I return multiple values from a function, sub or type in VBA?
I've got this main sub which is supposed to collect data from several functions, but a function can only return one value it seems. So how can I return multiple ones to a sub?
You might want want to rethink the structure of you application, if you really, really want one method to return multiple values.
Either break things apart, so distinct methods return distinct values, or figure out a logical grouping and build an object to hold that data that can in turn be returned.
' this is the VB6/VBA equivalent of a struct
' data, no methods
Private Type settings
root As String
path As String
name_first As String
name_last As String
overwrite_prompt As Boolean
End Type
Public Sub Main()
Dim mySettings As settings
mySettings = getSettings()
End Sub
' if you want this to be public, you're better off with a class instead of a User-Defined-Type (UDT)
Private Function getSettings() As settings
Dim sets As settings
With sets ' retrieve values here
.root = "foo"
.path = "bar"
.name_first = "Don"
.name_last = "Knuth"
.overwrite_prompt = False
End With
' return a single struct, vb6/vba-style
getSettings = sets
End Function
You could try returning a VBA Collection.
As long as you dealing with pair values, like "Version=1.31", you could store the identifier as a key ("Version") and the actual value (1.31) as the item itself.
Dim c As New Collection
Dim item as Variant
Dim key as String
key = "Version"
item = 1.31
c.Add item, key
'Then return c
Accessing the values after that it's a breeze:
c.Item("Version") 'Returns 1.31
or
c("Version") '.Item is the default member
Does it make sense?
Ideas :
Use pass by reference (ByRef)
Build a User Defined Type to hold the stuff you want to return, and return that.
Similar to 2 - build a class to represent the information returned, and return objects of that class...
You can also use a variant array as the return result to return a sequence of arbitrary values:
Function f(i As Integer, s As String) As Variant()
f = Array(i + 1, "ate my " + s, Array(1#, 2#, 3#))
End Function
Sub test()
result = f(2, "hat")
i1 = result(0)
s1 = result(1)
a1 = result(2)
End Sub
Ugly and bug prone because your caller needs to know what's being returned to use the result, but occasionally useful nonetheless.
A function returns one value, but it can "output" any number of values. A sample code:
Function Test (ByVal Input1 As Integer, ByVal Input2 As Integer, _
ByRef Output1 As Integer, ByRef Output2 As Integer) As Integer
Output1 = Input1 + Input2
Output2 = Input1 - Input2
Test = Output1 + Output2
End Function
Sub Test2()
Dim Ret As Integer, Input1 As Integer, Input2 As Integer, _
Output1 As integer, Output2 As Integer
Input1 = 1
Input2 = 2
Ret = Test(Input1, Input2, Output1, Output2)
Sheet1.Range("A1") = Ret ' 2
Sheet1.Range("A2") = Output1 ' 3
Sheet1.Range("A3") = Output2 '-1
End Sub
you can return 2 or more values to a function in VBA or any other visual basic stuff but you need to use the pointer method called Byref. See my example below. I will make a function to add and subtract 2 values say 5,6
sub Macro1
' now you call the function this way
dim o1 as integer, o2 as integer
AddSubtract 5, 6, o1, o2
msgbox o2
msgbox o1
end sub
function AddSubtract(a as integer, b as integer, ByRef sum as integer, ByRef dif as integer)
sum = a + b
dif = b - 1
end function
Not elegant, but if you don't use your method overlappingly you can also use global variables, defined by the Public statement at the beginning of your code, before the Subs.
You have to be cautious though, once you change a public value, it will be held throughout your code in all Subs and Functions.
I always approach returning more than one result from a function by always returning an ArrayList. By using an ArrayList I can return only one item, consisting of many multiple values, mixing between Strings and Integers.
Once I have the ArrayList returned in my main sub, I simply use ArrayList.Item(i).ToString where i is the index of the value I want to return from the ArrayList
An example:
Public Function Set_Database_Path()
Dim Result As ArrayList = New ArrayList
Dim fd As OpenFileDialog = New OpenFileDialog()
fd.Title = "Open File Dialog"
fd.InitialDirectory = "C:\"
fd.RestoreDirectory = True
fd.Filter = "All files (*.*)|*.*|All files (*.*)|*.*"
fd.FilterIndex = 2
fd.Multiselect = False
If fd.ShowDialog() = DialogResult.OK Then
Dim Database_Location = Path.GetFullPath(fd.FileName)
Dim Database_Connection_Var = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=""" & Database_Location & """"
Result.Add(Database_Connection_Var)
Result.Add(Database_Location)
Return (Result)
Else
Return (Nothing)
End If
End Function
And then call the Function like this:
Private Sub Main_Load()
Dim PathArray As ArrayList
PathArray = Set_Database_Path()
My.Settings.Database_Connection_String = PathArray.Item(0).ToString
My.Settings.FilePath = PathArray.Item(1).ToString
My.Settings.Save()
End Sub
you could connect all the data you need from the file to a single string, and in the excel sheet seperate it with text to column.
here is an example i did for same issue, enjoy:
Sub CP()
Dim ToolFile As String
Cells(3, 2).Select
For i = 0 To 5
r = ActiveCell.Row
ToolFile = Cells(r, 7).Value
On Error Resume Next
ActiveCell.Value = CP_getdatta(ToolFile)
'seperate data by "-"
Selection.TextToColumns Destination:=Range("C3"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=True, OtherChar _
:="-", FieldInfo:=Array(Array(1, 1), Array(2, 1)), TrailingMinusNumbers:=True
Cells(r + 1, 2).Select
Next
End Sub
Function CP_getdatta(ToolFile As String) As String
Workbooks.Open Filename:=ToolFile, UpdateLinks:=False, ReadOnly:=True
Range("A56000").Select
Selection.End(xlUp).Select
x = CStr(ActiveCell.Value)
ActiveCell.Offset(0, 20).Select
Selection.End(xlToLeft).Select
While IsNumeric(ActiveCell.Value) = False
ActiveCell.Offset(0, -1).Select
Wend
' combine data to 1 string
CP_getdatta = CStr(x & "-" & ActiveCell.Value)
ActiveWindow.Close False
End Function