link Open/Libre Office button to cell and reference cell in macro - macros

I would like to add a [set of] standardized macro[s] to some of the cells of a custom spredsheet (Open/Libre/Star Office).
Said macro should be activated using a Form PushButton dropped into the relevant cell[s].
I experience several problems all relative to the access of the "relevant cell":
If I try to Anchor to Cell a PushButton it goes to A1 and not to currently selected cell.
I can connect a Basic fragment to the button, but I found no way to retrieve the "relevent cell" (i.e.: the cell containing the button).
What I am trying to do (as a first working example) is to add a button to increment the numeric value of the cell (possibly disabling direct editing; I want that value to go up by one at each button press and no way to otherwise change cell).
Is such a thing possible at all?
Any example (or pointer to docs) very welcome.
NOTE: This question gives some hints on how to solve problem in VBA (Excel), but I found nothing for [L|O|S]Office

You can find the cell containing the button from a handler as follows:
Sub ButtonHandler(oEvent)
Dim sControlName$
Dim oSheet
Dim nCount As Long
Dim i As Long
Dim oPage
Dim oShape
Dim oAnchor
sControlName = oEvent.source.model.Name
oSheet = thiscomponent.currentcontroller.activesheet
nCount = oSheet.drawpage.count
oPage = oSheet.drawpage
For i = 0 To nCount - 1
oShape = oPage.getbyindex(i)
'oControlShape = oPage.getbyindex(i).control
If (oShape.supportsService("com.sun.star.drawing.ControlShape")) Then
If oShape.control.Name = sControlName Then
oAnchor = oShape.anchor
If (oAnchor.supportsService("com.sun.star.sheet.SheetCell")) Then
Print "Button is anchored in cell: " + oAnchor.AbsoluteName
Exit For
End If
End If
End If
Next i
End Sub
I know, it is not pretty is it? I added significant error checking.If you then want to know what cell was active when you clicked the button, you can call this routine
Sub RetrieveTheActiveCell()
Dim oOldSelection 'The original selection of cell ranges
Dim oRanges 'A blank range created by the document
Dim oActiveCell 'The current active cell
Dim oConv 'The cell address conversion service
Dim oDoc
oDoc = ThisComponent
REM store the current selection
oOldSelection = oDoc.CurrentSelection
REM Create an empty SheetCellRanges service and then select it.
REM This leaves ONLY the active cell selected.
oRanges = oDoc.createInstance("com.sun.star.sheet.SheetCellRanges")
oDoc.CurrentController.Select(oRanges)
REM Get the active cell!
oActiveCell = oDoc.CurrentSelection
oConv = oDoc.createInstance("com.sun.star.table.CellAddressConversion")
oConv.Address = oActiveCell.getCellAddress
Print oConv.UserInterfaceRepresentation
print oConv.PersistentRepresentation
REM Restore the old selection, but lose the previously active cell
oDoc.CurrentController.Select(oOldSelection)
End Sub

Related

Need help looping Macro that cut/inserts and deletes a cell range based on a selected row

This Macro is used to cut, insert and delete a cell range section of a workbook.
The problem I was trying to solve and gave up with the lack of response in another thread is why copying multiple non-adjacent rows to the MS clipboard often loses their row line-breaks when pasting.
E.g. Since trying to paste 3 non-adjacent rows into row 10, 11 and 12, often puts all 3 rows into row 10 with one row in fields A10-P10, the next row in Q10-AF10 and the last row into AG10-AV10...
I edited the Macro below to fix this mistake when this happens.
So, for example, I can now highlight row 10 and run the macro to cut/insert the fields Q10-AF10 to A11-P11 and delete/shift left the blank fields now in Q10-AF10.
I'm hoping for help to loop this process until there's no data outside Column A-P. In this case, no data outside cell P10.
Sub FixAllOnLine1OneRowAtATimeInsertToNextRow()
Application.ScreenUpdating = False
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Set copySheet = ActiveSheet
Set pasteSheet = ActiveSheet
copySheet.Range("Q" & ActiveCell.Row & ":AF" & ActiveCell.Row).Copy
Range("Q" & ActiveCell.Row & ":AF" & ActiveCell.Row).Offset(1).Select
pasteSheet.Cells(ActiveCell.Row, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
Columns("Q:AF").Select
Selection.Delete Shift:=xlToLeft
End Sub
Ok, I made some headway. I just have one super easy issue and then I need to loop it.
The first issue is that it cuts Column Q:AF correct of the row I've highlighted and shifts the entire Column Q:AF to the left, but it INSERTS the cut cells into the fixed range, A2:P2. I want to INSERT the cut cells down ONE row from my selection. I KNOW this is a couple characters in the Offset, I just can't get it.
Then, once it's working properly...say I highlight row 10, it cuts Q10:AF10 and instead INSERTS the cells into A11:P11 and shifts "Q:AF" to the left, then I need to figure out how to get it to loop until there's no more data to right of Column P. When this problem occurs pasting multiple rows from the clipboard all into the first row losing the row line-breaks, it's always quite a few rows.
Any ideas?
Thanks so much!
Mark
Sub FixAllOnLine1OneRowAtATimeInsertToNextRow()
Dim ws As Worksheet
Dim lNextRow As Long
Application.ScreenUpdating = False
Set ws = ActiveSheet
ws.Range("Q" & ActiveCell.Row & ":AF" & ActiveCell.Row).Copy 'Copy the row of the selected cell from Q:AF
ws.Range("Q" & ActiveCell.Row & ":AF" & ActiveCell.Row).Offset(1).Select 'Select the cells you have just copied. Not needed
ws.Cells(ActiveCell.Row, 1).End(xlUp).Offset(1, 0).Insert xlShiftDown ' Paste the copied values in to column "A" on next row?
'lNextRow = ws.Range("A" & Rows.count).End(xlUp).Row + 1 'Get Next Row number
'Range("A" & lNextRow).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Range("Q:AF").Delete Shift:=xlToLeft
'Columns("Q:AF").Select
'Selection.Delete Shift:=xlToLeft
Application.ScreenUpdating = True
ActiveCell.Offset(RowOffset:=-1, columnOffset:=0).Activate 'Added to move active cell up one row to run it again for multiple groups to apply fix.
End Sub
Here's a solution in another direction just in case someone from the engines needs it...
Sub ReduceNoOfColumns()
Dim iRow As Integer 'Row to be manipulated
Dim iRowToPasteTo 'Row number to paste the copied cells
Dim iCurCol As Integer 'Current Column number of first cell with a value to cut
Dim NoOfCols As Integer 'integer to hold max number of columns
Dim sAddress As String
iRow = ActiveCell.Row
iRowToPasteTo = iRow + 1
NoOfCols = 16 'Set this number to the total number of columns you wish to have (in your case 16)
iCurCol = NoOfCols + 1
Do Until Cells(iRow, iCurCol).Value = "" 'Keep looping until we get to an empty column
sAddress = ColNoToLetter(iCurCol) & iRow & ":" & ColNoToLetter(iCurCol + NoOfCols - 1) & iRow
Rows(iRowToPasteTo & ":" & iRowToPasteTo).Insert Shift:=xlDown
Range(sAddress).Copy
Range("A" & iRowToPasteTo).PasteSpecial xlPasteAll
Range(sAddress).Clear
iCurCol = iCurCol + NoOfCols
iRowToPasteTo = iRowToPasteTo + 1
Loop
End Sub
Function ColNoToLetter(iCol As Integer) As String
Dim vArr
vArr = Split(Cells(1, iCol).Address(True, False), "$")
ColNoToLetter = vArr(0)
End Function

Conditional formatting on Access form looking up a value

I've created a form within Access which uses a cross-tab query as its data source.
The column headings for the query are 1, 2, 3, 4 and 5 representing week numbers.
The values display items such as 3/3 = 100.00% or 0/13 = 0.00% or 3/14 = 21.00%.
I've added conditional formatting to the text boxes on the form.
Expression Is Right([2],7)="100.00%" works and displays the figure in bold red when the percentage is 100.
Expression is Val(Right([2],7))=100 also works - converting the text value to a numeric value.
The problem I'm having is that I'm not always looking for 100% - it depends on the value within a table. What I'm trying to do is
Val(Right([2],7))=(SELECT ParamValue*100 FROM tbl_System WHERE Param='SampleSize') - this doesn't work.
Neither does:
Eval(Val(Right([2],7))=(SELECT ParamValue*100 FROM tbl_System WHERE Param='SampleSize'))
or
Val(Right([2],7))=EVAL(SELECT ParamValue*100 FROM tbl_System WHERE Param='SampleSize')
or
Val(Right([2],7))=DLookUp("ParamValue","tbl_System","Param= 'SampleSize'")*100
or
Val(Right([2],7))=Eval(DLookUp("ParamValue","tbl_System","Param= 'SampleSize'")*100)
The SQL for the cross-tab query is:
TRANSFORM NZ(Sum(Abs([Include])),0) & "/" & NZ(Count(*),0) & " = " &
FormatPercent(NZ(Round(Sum(Abs(Include))/Count(*),2),0),2)
SELECT tbl_TMP_PrimaryDataSelection.TeamMember
FROM tbl_TMP_PrimaryDataSelection
GROUP BY tbl_TMP_PrimaryDataSelection.TeamMember
PIVOT tbl_TMP_PrimaryDataSelection.WeekNum In (1,2,3,4,5)
I don't think you can use a function in there, be it system or user-defined.
But you can define the FormatCondition dynamically at runtime, like this:
Dim txtFld As TextBox
Dim objFrc As FormatCondition
Dim strExpr As String
Set txtFld = Me!myTextBox
' Remove existing FormatConditions
txtFld.FormatConditions.Delete
' The dynamic expression
strExpr = "Val(Right([2],7))=" & DLookUp("ParamValue","tbl_System","Param='SampleSize'")*100
' Assign a new FormatCondition to text box
Set objFrc = txtFld.FormatConditions.Add(acExpression, , strExpr)
' Set the format
objFrc.ForeColor = &HFF0000
This example simply removes and recreates all FormatConditions. If you have a fixed number of conditions, you can also use the FormatCondition.Modify method (see online help).
Edit:
The final code I have used executes on the Form_Load event and adds a format to each of the five weekly text boxes:
Private Sub Form_Load()
Dim aTxtBox(1 To 5) As TextBox
Dim x As Long
Dim oFrc As FormatCondition
Dim sExpr As String
With Me
Set aTxtBox(1) = .Wk1
Set aTxtBox(2) = .Wk2
Set aTxtBox(3) = .Wk3
Set aTxtBox(4) = .Wk4
Set aTxtBox(5) = .Wk5
For x = 1 To 5
aTxtBox(x).FormatConditions.Delete
sExpr = "Val(Right([" & x & "],7))>=" & DLookup("ParamValue", "tbl_System", "Param='SampleSize'") * 100
Set oFrc = aTxtBox(x).FormatConditions.Add(acExpression, , sExpr)
oFrc.ForeColor = RGB(255, 0, 0)
Next x
End With
End Sub
Edit 2
Yes, defining FormatConditions via VBA is especially useful when dealing with multiple controls in a loop. You can do this in Design View too and save the FormatConditions permanently, simply to avoid going through the FormatConditions dialogs one by one. Or if the customer later decides that he'd rather have a different color. :)
Note: You could use Set aTxtBox(x) = Me("Wk" & x) in the loop. But actually you don't need multiple TextBox variables, you can simply re-use it.

Excel VBA code understanding

I'm trying build a excel based input form, I have found something online and I'm trying to understand these codes:
Dim Hsheet,Isheet As Worksheet
Dim NextRow, oCol As Long
Dim MyRng, MyCell As Range
Dim MyCopy, ClearCells As String
Set Hsheet = Worksheet("InputForm")
Set ISheet = Worksheet("Database")
This is the part I don't understand, can someone explain to me please?
With Hsheet
nextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
End With
With Isheet
Set myRng = .Range(MyCopy)
If Application.CountA(myRng) <> myRng.Cells.Count Then
MsgBox "Please fill in all the cells!"
Exit Sub
End If
End With
And also this part, can someone explain to me please?
With Hsheet
.Cells(nextRow, "a").Value = Application.UserName
oCol = 1
For Each myCell In MyRng.Cells
Hsheet.Cells(NextRow, oCol).Value = myCell.Value
oCol = oCol + 1
Next myCell
End With
Thanks in advance :)
With Isheet
On Error Resume Next
With .Range(ClearCells).Cells.SpecialCells(xlCellTypeConstants)
.ClearContents
Application.Goto .Cells(1) ', Scroll:=True
End With
On Error GoTo 0
End With
I can explain what the code does but there are few things which I would like to mention :)
A
Dim Hsheet,Isheet As Worksheet
Dim NextRow, oCol As Long
Dim MyRng, MyCell As Range
Dim MyCopy, ClearCells As String
This is not the right way to declare the variables/objects For example if you consider this line which is
Dim Hsheet,Isheet As Worksheet
Here, only Isheet has been declared as a worksheet and not Hsheet. The Hsheet automatically becomes a variant. The right way is
Dim Hsheet As Worksheet, Isheet As Worksheet
Dim NextRow As Long, oCol As Long
Dim MyRng As Range, MyCell As Range
Dim MyCopy As String, ClearCells As String
B
With Hsheet
nextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
End With
What this code does is it tries to find the last row which has data in Col A and then offsets one row down to get the next empty row so that you can write to it.
Another way to write the same thing is mentioned here So the above code can also be written as
With Hsheet
nextRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
End With
C
With Isheet
Set myRng = .Range(MyCopy)
If Application.CountA(myRng) <> myRng.Cells.Count Then
MsgBox "Please fill in all the cells!"
Exit Sub
End If
End With
I believe MyCopy is supposed to hold some value which I cannot see it in your code. Assuming that it holds a valid cell address, what the code is trying to do is to ensure that all cells are filled up by comparing the cells count vs the number of cells filled up.
D
With Hsheet
.Cells(NextRow, "a").Value = Application.UserName
oCol = 1
For Each myCell In MyRng.Cells
Hsheet.Cells(NextRow, oCol).Value = myCell.Value
oCol = oCol + 1
Next myCell
End With
This is also pretty straightforward. The code stores the UserName in the next available cell in Col A and then stores the values from Range MyRng in Sheet Isheet in Col A of Sheet Hsheet
HTH
Your piece of code seems to do some copy from one sheet to another, adding the user name on top of a range.
However the string variable MyCopy does not seem to be initialized, therefore I don't think running this macro as is would produce any desired result (unless the Range function returns some cells when called with an empty string ? I don't know its specs).
I don't remember Excel VBA perfectly, but I think that :
Cells(.Rows.Count, "A") selects the cell located on the last row, column "A".
End(xlUp) moves the selection to the top of the range of contiguous non-empty cells (like pressing CTRL + UP in Excel, I think).
Offset(1, 0) moves the selection to one cell to the bottom.
Row returns that cell's row number.
So your first code block sets the row number of the second row of the last range of non-empty cells in column A, to the variable nextRow .
You can follow the same reasoning to understand the purpose of all the other code blocks. I suggest your search MSDN's VBA for Excel documentation websites to get more information about the meaning of each function you don't understand yet.

Getting the text of all check boxes in a tabcontrol to a string

I am extremely new to all of this, and whilst I have tried searching I cant find anything that has helped me achieve what I am after.
I have a form in VB with the following:
1 x tabcontrol
10 x checkboxes which sit in various tabs on the tab control
1 x listbox
When i tick any of the check boxes, I want their text to be added to the listbox, and when I untick, their text to be taken from the listbox.
I can achieve this very easily using if statements for the changedcheck event for each checkbox but I have to do that for every single checkbox which isn't very efficient as potentially i could have 20,30 40+ check boxes. Plus if I add one at a later stage I would have to remember to add its code.
Ideally i want a method that's says: check all the checkboxes in tabcontrol if there value is true write their text to a string, if there value is false, take there text from the string. put the string in the listbox.
I started with something like this...
Dim chk As CheckBox
Dim txt As String = ""
For Each chk In TabControl1.Controls
If chk.Checked = True Then
txt = txt + chk.Text +vbCrLF
Else
txt = replace(txt, chk.text + vbCrLf, "")
End If
Next
End Sub
First problem is that the above obviously doesn't work! so any guidance there is appreciated - i put it together from reading scraps from other code.
Second problem is, i can't get my head round how the list box will be updated, as previously i was using the CheckedChanged event for each control, which if i do what i want, then there wont be a specific CheckedChanged event, as it could be any of the checkboxes (hopefully that makes sense!). I don't want to have to press a button to add the checked checkboxes to the listbox, i want it to be dynamic
any help is very much appreciated.
For your first problem add
Dim chk As Control
Dim txt As String = ""
For Each chk In TabControl1.Controls
If TypeOf chk Is CheckBox
If DirectCast(chk, CheckBox).Checked = True Then
txt = txt + chk.Text +vbCrLF
Else
txt = replace(txt, chk.text + vbCrLf, "")
End If
End If
Next
End Sub
For your second problem in CheckedChanged event you can do something like this:
Private Sub OnCheckedChanged(sender as Object, e as EventArgs) _
Handles CheckBox1.CheckedChanged
Dim chk As CheckBox = TryCast(s, CheckBox)
Dim txt as string
If c.Checked = True Then
txt = chk.Text
EndIf
End Sub

Is Null Conditional Formatting - MS Access Forms

Something that I would assume comes up a lot...
I'd like to know if there's a way to, in Access' Conditional Formatting, format all blank fields. In my case, all fields generally need to be entered, but not in all cases. So, instead of writing a bunch of conditional code to restrict the user to writing it in there, I just want some red backgrounds in my fields as a reminder "hey, there's nothing in here.. sure that's what you wanted?"It's on a tablet so Message Boxes would be annoying. So conditional formatting it is. I know you can have "Is Null([Field]) but that requires me to go through my 20+ forms on 30+ fields and ensure proper field names etc, then type the condition for them individually. Is there a way I can simply multi-select my fields, do a conditional format on Multiple, and use maybe "Is Equal To: NULL"?
I've tried "equal to: Null" and it doesn't work.. nor does "equal to: "" " (using the Access constants). Ideas why? Or how I can get around this? Also, it's only necessary for non-touched fields, so if the user starts to type then deletes back to blank, I don't care; it can stay unformatted or go back to red, so if there's a better way to do this I'm all eyes.
EDIT: I've started doing some VBA code which I will paste into all my forms:
Private Sub Form_Load()
Dim ctl As Control
Dim reqCol As Long
Dim focusCol As Long
Dim doneCol As Long
Dim format As FormatCondition
reqCol = RGB(246, 180, 180)
focusCol = RGB(252, 249, 238)
doneCol = RGB(255, 255, 255)
For Each ctl In Me.Controls
With ctl
Me.Controls(ctl.Name).FormatConditions.Delete 'Delete the existing conditions.
Me.Controls(ctl.Name).BackColor = doneCol 'Set the background color to the done color.
Select Case .ControlType
Case acTextBox
'Create the format objects.
format = Me.Controls(ctl.Name).FormatConditions.Add(acFieldValue, acEqual, "")
format = Me.Controls(ctl.Name).FormatConditions.Add(acFieldHasFocus)
'Format the filled in boxes (ie set back to red)
With Me.Controls(ctl.Name).FormatConditions(0)
.BackColor = reqCol
.Enabled = True
End With
'Format the current field color (ie set to beige)
With Me.Controls(ctl.Name).FormatConditions(1)
.BackColor = focusCol
.Enabled = True
End With
End Select
End With
Next ctl
End Sub
Problem is that FormatConditions.Add(acFieldValue, acEqual, "") doesn't work for the same reason... how do I get around this? Seeing as VBA and the built-in conditions are both flawed, seems like a bug. Or I'm missing something right in front of me..
In Access 2016 I was unable to find the default formatting option that is the solution provided by #SeanC. Instead I found that to get my Combo Box to format properly I had to use an Expression with ISNULL.
Set default format to the way to want zero length data to appear.
use
Field Value Is greater than ''
for the conditional formatting and set that format to how it should appear with text in the field.
You can select multiple fields with Shift+click in design view to select all the appropriate fields that this needs to be applied to
Solved. Put this in my forms (might look into making it a module; new to this, not sure how yet)
Private Sub Form_Load()
On Error Resume Next
Dim ctl As Control
Dim reqCol As Long
Dim focusCol As Long
Dim doneCol As Long
Dim format As FormatCondition
Dim expr As String
reqCol = RGB(246, 180, 180)
focusCol = RGB(252, 249, 238)
doneCol = RGB(255, 255, 255)
For Each ctl In Me.Controls
With ctl
'Delete the existing formatting
Me.Controls(ctl.Name).FormatConditions.Delete
Me.Controls(ctl.Name).BackColor = doneCol
Select Case .ControlType
Case acTextBox
expr = "IsNull(" & ctl.Name & ") = True"
'Create the format objects.
format = Me.Controls(ctl.Name).FormatConditions.Add(acFieldHasFocus)
format = Me.Controls(ctl.Name).FormatConditions.Add(acExpression, , expr)
'Format the filled in boxes (ie set back to focus color)
With Me.Controls(ctl.Name).FormatConditions(0)
.BackColor = focusCol
.Enabled = True
End With
'Format the current field color (ie set to required color)
With Me.Controls(ctl.Name).FormatConditions(1)
.BackColor = reqCol
.Enabled = True
End With
End Select
End With
Next ctl
End Sub
The trick was how to enter it into FormatConditions.Add(...). Works exactly how I'd like it to now.