Excel creating autofilling form - forms

I'm trying to create a form which contains two entries:
-folder number
-list of toms which are in folders
This is for archiving purpose. Form is divided on 4 section which will be printed on labels for archive boxes.
Folders are numbered from 1 to 1500, some of them contain 1 tom of documents, some of them up to 10. For now I'm doing this manualy by just copying from the table which looks like this:
table
Only thing I need in form is TOM NUMBER from this table
form
I was trying to use VLOOKUP but it only returns first row which has searched folder number.
So bascially I want a function which will take folder number from label form and find all toms which are assigned to and write it below. first 3 digits in folder number aren't important, only last 4 digits are considered most important variable

Unfortunately vlookup will not work, you are going to have to use an array folder. I am making an assumption that you will have a table that is called [Folders]
and I am going to create a form form with some vba on how to do this.
1. Create a Table by selecting the folder dataset and push ctl+T.
Alt + F11 to enter Visual basic editor
At the top choose insert ==> UserForm
Push F4 and in the properties window name your form FileFinder
Your toolbox maynot appear if it doesn't choose view => toolbox to open
drag 2 labels, 2 listboxes, and 2 buttons, you can format it however you like.
7.Create a new Module same as adding userform only choose module
Copy paste this code
Public Function CreateWorksheet(Optional name As String = "") As Worksheet
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets.Add
If name <> "" Then ws.name = name
Set Create = ws
End Function
Public Function LastRow() As Integer 'gets last row from column A
LastRow = ActiveSheet.Cells(Rows.count, 1).End(xlUp).Row
End Function
Public Function DistintFolders() As String()
Dim list() As String
Dim counter As Integer
For Each cell In ActiveSheet.Range("E2:E" & LastRow)
If Not IsInList(list, cell.Value, counter) Then
counter = counter + 1
ReDim Preserve list(1 To counter)
list(counter) = cell.Value
End If
Next cell
DistintFolders = list
End Function
Public Function TomNumberByFolder(folderName As Variant) As String()
Dim list() As String
Dim counter As Integer
Dim rowNumber As Integer
For Each cell In ActiveSheet.Range("B2:B" & LastRow)
rowNumber = rowNumber + 1
If IsCorrectFolder(folderName, rowNumber) Then
counter = counter + 1
ReDim Preserve list(1 To counter)
list(counter) = cell.Value
End If
Next cell
TomNumberByFolder = list
End Function
Public Function IsInList(ByRef list() As String, compare As String, count As Integer) As Boolean
Dim l As Variant
If compare = "" Then
IsInList = True
Exit Function
End If
If count = 0 Then
IsInList = False
Exit Function
End If
For Each l In list
If l = compare Then
IsInList = True
Exit Function
End If
Next l
IsInList = False
End Function
Public Function IsCorrectFolder(folderName As Variant, rowNumber As Integer) As Boolean
IsCorrectFolder = (ActiveSheet.Range("E" & rowNumber).Value = folderName)
End Function
double click your form and paste this code
`
Private Sub btnCancel_Click()
Unload Me
End Sub
Private Sub btnCreate_Click()
Dim ws As Worksheet
If lstTom.ListCount = 0 Then
MessageBox "Please select a folder"
End If
Set ws = ThisWorkbook.Sheets.Add
ws.Cells(1, 1).Value = "Tom Number"
ws.Cells(2, 1).Resize(Me.lstTom.ListCount, 1) = Me.lstTom.list
End Sub
Private Sub lstFolder_Click()
Dim folder As String
If ActiveSheet.name <> "Data" Then ThisWorkbook.Sheets("Data").Activate 'please name this whatever your datasheet is called
For i = 0 To lstFolder.ListCount - 1
If lstFolder.Selected(i) Then
Me.lstTom.Clear
For Each s In TomNumberByFolder(lstFolder.list(i))
With lstTom
.AddItem s
End With
Next s
End If
Next i
End Sub
Private Sub UserForm_Initialize()
For Each s In DistintFolders
With lstFolder
.AddItem s
End With
Next s
End Sub
`
please note that you may have to change sheet names if you would like I will send you this.
Download Here

Related

In LibreOffice Calc, how do I change through LibreOffice Basic the value of a cell with an event listener set to it without crashing the program?

I am trying to create two tables which mirror changes made to any of them to one another automatically.
To that end, I added event listeners which are triggered when the cells of these tables are edited by the user.
Unfortunately, editing one of the tables causes LibreOffice to crash, even though the changes are indeed reflected correctly, as seen upon reopening the file.
I thought the crash might be due to a never-ending circular reference, but it still crashes after it has been made non-circular (by commenting out the relevant parts of the code so that changes are reflected only one way rather than both ways).
I noticed the code worked fine when writing to a cell that didn't have an event listener set to it.
How can I write to one of the cells with event listeners set to them without causing LibreOffice to crash?
You may want to download the following file. Please run Main and then try editing the cell C3 of the Planning sheet. The arbitrary string "C" should be written in the cell C4 of the Services sheet.
Here is a simplified version of the code :
REM ***** BASIC *****
const SERVICESSHEET_NUMBER = 2
const SERVICESSHEET_SERVICES_COLUMN = 2
Type cellStruct
columnNumber As Integer
rowNumber As Integer
End Type
Sub UpdateServicesSheet(editedCell As cellStruct, newValue As String)
Dim oSheets
Dim servicesSheet
oSheets = ThisComponent.getSheets()
servicesSheet = oSheets.getByIndex(SERVICESSHEET_NUMBER)
servicesSheet.getCellByPosition(SERVICESSHEET_SERVICES_COLUMN, 3).setString(newValue)
End Sub
Private oListener, cellRange as Object
Sub AddListener
Dim sheet, cell as Object
sheet = ThisComponent.Sheets.getByIndex(0) 'get leftmost sheet
servicesSheet = ThisComponent.Sheets.getByIndex(2)
cellRange = sheet.getCellrangeByName("C3")
oListener = createUnoListener("Modify_","com.sun.star.util.XModifyListener") 'create a listener
cellRange.addModifyListener(oListener) 'register the listener
cellRange = servicesSheet.getCellrangeByName("C4")
oListener = createUnoListener("Modify_","com.sun.star.util.XModifyListener") 'create a listener
cellRange.addModifyListener(oListener) 'register the listener
End Sub
global CircularReferenceAllowed As boolean
Sub Modify_modified(oEv)
Dim editedCell As cellStruct
Dim newValue As String
editedCell.columnNumber = 2
editedCell.rowNumber = 2
If CircularReferenceAllowed Then
CircularReferenceAllowed = false
UpdateServicesSheet(editedCell, "C")
End If
End Sub
Sub Modify_disposing(oEv)
End Sub
Sub RmvListener
cellRange.removeModifyListener(oListener)
End Sub
Sub Main
CircularReferenceAllowed = true
AddListener
End Sub
Crossposted to :
OpenOffice forums
LibreOffice discourse platform
It seems like the event trigger is within another event's function is causing the crash. In any case, the solution is to remove the listener, then add it back after modifying the other cell.
You do need to global the Listener and the Cell objects to make this work.
This code is simplified to work on C3 and C15 on the first sheet. It would also output some information on C14, which isn't really necessary for your purpose, but I use it to see what's happening. You need to adopt the according to what you need.
global goListener as Object
global goListener2 as Object
global goCellR as Object
global goCellR2 as Object
global goSheet as Object
global giRun as integer
global giUpd as Integer
Sub Modify_modified(oEv)
Dim sCurStr$
Dim sNewStr As String
'xRay oEv
giRun = giRun + 1
sCurStr = oEv.source.string
oCell = goSheet.getCellByPosition(2, 14)
If (oCell.getString() <> sCurStr) Then
' only update if it's different.
giUpd = giUpd + 1
goCellR2.removeModifyListener(goListener2)
oCell.setString(sCurStr)
goCellR2.addModifyListener(goListener2)
End If
sNewStr =sCurStr & " M1 Run=" & giRun & " Upd=" & giUpd
goSheet.getCellByPosition(2, 13).setString(sNewStr)
End Sub
Sub Modify2_modified(oEv)
Dim sCurStr$
Dim sNewStr As String
Dim oCell as Object
'xRay oEv
giRun = giRun + 1
sCurStr = oEv.source.string
oCell = goSheet.getCellByPosition(2, 2)
If (oCell.getString() <> sCurStr) Then
' only update if it's different.
giUpd = giUpd + 1
goCellR.removeModifyListener(goListener)
oCell.setString(sCurStr)
goCellR.addModifyListener(goListener)
End If
sNewStr =sCurStr & " M2 Run=" & giRun & " Upd=" & giUpd
goSheet.getCellByPosition(2, 13).setString(sNewStr)
End Sub
Sub Modify_disposing(oEv)
MsgBox "In Modify_disposing"
End Sub
Sub Modify2_disposing(oEv)
MsgBox "In Modify2_disposing"
End Sub
Sub RmvListener
MsgBox "In RmvListener"
goCellR.removeModifyListener(goListener)
goCellR2.removeModifyListener(goListener2)
End Sub
Sub AddListener
goSheet = ThisComponent.Sheets.getByIndex(0) 'get leftmost goSheet
'servicesSheet = ThisComponent.Sheets.getByIndex(2)
goCellR = goSheet.getCellrangeByName("C3")
goListener = createUnoListener("Modify_","com.sun.star.util.XModifyListener") 'create a listener
goCellR.addModifyListener(goListener) 'register the listener
goCellR2 = goSheet.getCellrangeByName("C15")
goListener2 = createUnoListener("Modify2_","com.sun.star.util.XModifyListener") 'create a listener
goCellR2.addModifyListener(goListener2) 'register the listener
End Sub
Sub Main
giRun = 0
giUpd = 0
AddListener
End Sub

how to pass cellrange to a user defined macro paramenter

i would like to work with cellranges within my macro.
Function SumIfColor(SumRange)
Dim oRange as object
Dim oSheet as object
' Get Access to the Active Spreadsheet
oSheet = ThisComponent.CurrentController.ActiveSheet
' Get access to the Range listed in Sum Range
oRange = oSheet.getCellRangeByName(SumRange).RangeAddress
End Function
The question is how can I call this function with real cellRange object instead of String. Because getCellRangeByName works only with String variable.
Because when I call the function like this
sumifcolor(B1:B3)
I got the following error:
"Object variable not set"
I read some hint here but it did not helped me.
It is not possible to pass an actual CellRange object. One solution is to pass the row and column number, similar to the second part of #Axel Richter's answer in the link:
Function SumIfColor(lcol1, lrow1, lcol2, lrow2)
sum = 0
oCellRange = ThisComponent.CurrentController.ActiveSheet.getCellRangeByPosition(_
lcol1-1,lrow1-1,lcol2-1,lrow2-1)
For lCol = 0 To oCellRange.Columns.Count -1
For lRow = 0 To oCellRange.Rows.Count -1
oCell = oCellRange.getCellByPosition(lCol, lRow)
If oCell.CellBackColor > -1 Then
sum = sum + oCell.Value
End If
Next
Next
SumIfColor = sum
End Function
To call it:
=SUMIFCOLOR(COLUMN(B1:B3),ROW(B1),COLUMN(B3),ROW(B3))
The sum will be recalculated whenever a value in the range B1:B3 is changed, because of COLUMN(B1:B3). However, apparently changing only the color of a cell does not cause it to be recalculated.

How to end a Basic function properly

I'm trying to write a function in Basic for LibreOffice Calc to get the first letter of each word of the selected cell using the following code:
Function GetFirstLetters(rng) As String
Dim arr
Dim I As Long
arr = Split(rng, " ")
If IsArray(arr) Then
For I = LBound(arr) To UBound(arr)
GetFirstLetters = GetFirstLetters & Left(arr(I), 1)
Next I
Else
GetFirstLetters = Left(arr, 1)
End If
End Function
And it works correctly, unless I try to execute it again, then it seems that the new result gets appended to that of any previous execution and it will return both strings together, example:
It also doesn't matter if I delete some or even all cells, or if I call it using an empty cell or even in another page, it will always append the result to the previous one:
Why does this happen? How can I fix this behaviour?
I don't know anything about Basic, so please don't bash on me if this is something very simple.
The original function is this:
Function GetFirstLetters(rng As Range) As String
'Update 20140325
Dim arr
Dim I As Long
arr = VBA.Split(rng, " ")
If IsArray(arr) Then
For I = LBound(arr) To UBound(arr)
GetFirstLetters = GetFirstLetters & Left(arr(I), 1)
Next I
Else
GetFirstLetters = Left(arr, 1)
End If
End Function
And I got it from here: http://www.extendoffice.com/documents/excel/1580-excel-extract-first-letter-of-each-word.html.
The code you have found is VBA for Excel. Openoffice or Libreoffice uses StarBasic, not VBA. This is similar but not equal. So you can't simply use the same code as in Excel.
First difference is, there is no Range object. This you have noticed and have used rng as an Variant.
But another difference is, function names are like variable names in the global scope. And they will not be reseted if the function is called again. So in StarBasic we better do:
Function GetFirstLetters(sCellValue as String) As String
Dim arr As Variant
Dim I As Long
Dim sResult As String
arr = Split(sCellValue, " ")
If IsArray(arr) Then
For I = LBound(arr) To UBound(arr)
sResult = sResult & Left(arr(I), 1)
Next I
Else
sResult = Left(arr, 1)
End If
GetFirstLetters = sResult
End Function
sResult is reseted (new Dimed) every time the function is called. So even the function's return value.

Combobox is not filled when user form is initialised, but filled after closing and reopening form

For some odd reason, my combo boxes are not "preloaded" with content once the form is initialised, the user has to first close down the form and then open it again for the combo boxes to be prefilled. It's a bug that is starting to irritate the end user, any idea why would this happen? I've attached Boot to a button in my spreadsheet, which initialses the form. Code below:
In UserForm1
Private Sub UserForm1_Initialize()
Call GetPrimaryContact
Call GetSecondaryContact
End Sub
In Module1
Public itm1
Public itm2
Sub Boot()
UserForm1.Show
Call GetPrimaryContact
Call GetSecondaryContact
End Sub
Sub GetPrimaryContact()
Dim Col As New Collection
Dim i As Long
Dim CellVal As Variant
' Clear filters
ThisWorkbook.Sheets("Master").AutoFilter.ShowAllData
' Get last row
LastRow = ThisWorkbook.Sheets("Master").Cells(Rows.Count, "A").End(xlUp).Row
' Loop between all of column F to get unique values
For i = 3 To LastRow
CellVal = ThisWorkbook.Sheets("Master").Range("F" & i).Value
On Error Resume Next
Col.Add CellVal, Chr(34) & CellVal & Chr(34)
On Error GoTo 0
Next i
' Populate the first with primary contacts
For Each itm1 In Col
With UserForm1.ComboBox1
If IsEmpty(itm1) Then .AddItem "No Contact" Else .AddItem itm1
End With
Next
End Sub
Sub GetSecondaryContact()
Dim Col As New Collection
Dim i As Long
Dim CellVal As Variant
' Clear filters
ThisWorkbook.Sheets("Master").AutoFilter.ShowAllData
' Get last row
LastRow = ThisWorkbook.Sheets("Master").Cells(Rows.Count, "A").End(xlUp).Row
' Loop between all of column F to get unique values
For i = 3 To LastRow
CellVal = ThisWorkbook.Sheets("Master").Range("G" & i).Value
On Error Resume Next
Col.Add CellVal, Chr(34) & CellVal & Chr(34)
On Error GoTo 0
Next i
' Populate the first with primary contacts
For Each itm2 In Col
With UserForm1.ComboBox2
If Not IsEmpty(itm2) Then .AddItem itm2
End With
Next
End Sub
You should call the functions GetPrimaryContact and GetSecondaryContact on the form initialize event, this way the control's will be loaded as intended. See the example code below.
Sub Boot()
UserForm1.Show
End Sub
Private Sub UserForm_Initialize()
Call GetPrimaryContact
Call GetSecondaryContact
End Sub
I think your problem is that your Initialize code has Userform1_Initialize. It should only be written like Userform_Initialize and it will work.
And in the Sub boot put userform1.show last instead of first, If you stepping with F8 you will see that when you come to FormShow it stops there so it doesnt load your "Calls" until you close it and thats why you have them next time you start.

vba error 438 but object class does contain sub

I am writing code which is supposed to read in data from an Excel worksheet, save it as strings into variables contained in an object of a class which I have defined and then add this object to an object tree of a class which I have also defined.
Dim ProdTreeMain As New CProdTree
Dim nR As Range
Dim nnR As Range
Set nR = oXS.Range("A1")
Set nnR = oXS.Range("A1")
dim r as integer
r = 1
Do While Not (nR.Text = "" And nnR.Text = "")
If CONDITION IS TRUE:
Dim currProd As New CProduct
ProdTreeMain.addProduct (currProd) '<-- error 438 "Object doesn't support property or method
End If
r = r + 1
Set nR = oXS.Range("A" & CStr(r + 1))
Set nR = oXS.Range("A" & CStr(r + 2))
Loop
The class CProdTree contains a sub "addProduct" which takes an input object of class CProduct by reference.
Public Sub addProduct(ByRef Prod As CProduct)
What the hell is going on? The class is defined, the sub correct, the variable type being passed to the sub is of the correct class and yet I get this error ... :/
You need to drop the parentheses around the argument. My favorite explanation is this Daily Dose of Excel post.
This line:
ProdTreeMain.addProduct (currProd)
becomes:
ProdTreeMain.addProduct currProd