How to fire on unhiding rows or columns - vba

Some users hide columns/rows and forget to unhide them before saving a workbook. I want to catch that with unhide all columns/rows on the save event with
Sub ReInvisible()
ThisWorkbook.Worksheets(1).UsedRange.EntireRow.Hidden = False
ThisWorkbook.Worksheets(1).UsedRange.EntireColumn.Hidden = False
End Sub
this works fine but I would like to inform the user that all hidden columns/rows are now visible. Now I am looking for a way to trigger on unhiding a column/row so as soon as at least one column or row is made visible I want to fire a messagebox.
In VB.NET I would try to write my own event but in VBA I do not know how I can do a workaround. Does anyone have an idea?

Something like this should do it:
Sub ReInvisible()
Dim lVisColCount As Long
Dim lVisRowCount As Long
With ThisWorkbook.Worksheets(1).UsedRange
lVisColCount = .Rows(1).SpecialCells(xlCellTypeVisible).Count
lVisRowCount = .Columns(1).SpecialCells(xlCellTypeVisible).Count
.EntireRow.Hidden = False
.EntireColumn.Hidden = False
If .Rows(1).SpecialCells(xlCellTypeVisible).Count <> lVisColCount Then MsgBox "Columns unhidden"
If .Columns(1).SpecialCells(xlCellTypeVisible).Count <> lVisRowCount Then MsgBox "Rows unhidden"
End With
End Sub

Related

VBA "All" checkbox with listbox deselect loop issues

I have a check box that is used for "ALL" (selected by default) selections in a muliselect listbox that is deselected when an item in the listbox is selected. I also coded it so when "ALL" is selected, then it clears the listbox selections and checks the box. It ends up looping through the different subs and makes it annoying for the user.
For instance, when I click an item in the listbox it selects that value, then deselects the checkbox. Since the checkbox is deselected, it goes back through the listbox and deselects the selected item. It loops between the two subs a couple times and ends up only working correctly half the time.
Can I prevent entering the other sub?
Is there better logic so it won't loop as it does?
or maybe an better method to achieve this?
Multiselect listbox:
Private Sub Mkts_Change()
If Me.cheMkts.Value = True Then
Me.cheMkts.Value = False
End If
End Sub
Checkbox:
Private Sub cheMkts_Click()
Dim i As Integer
For i = 0 To Mkts.ListCount - 1
If Me.Mkts.Selected(i) = True Then
Me.Mkts.Selected(i) = False
End If
Next
End Sub
What about adding an If statement around your cheMtks_Click()?
This way when your code deselects it automatically it shouldn't trigger the loop.
Private Sub cheMkts_Click()
If Me.cheMkts.Value = True Then
Dim i As Integer
For i = 0 To Mkts.ListCount - 1
If Me.Mkts.Selected(i) = True Then
Me.Mkts.Selected(i) = False
End If
Next
End If
End Sub
Thanks for your help, Ruben. That corrects the error on the one end, but I am still having issues on the other side. When I have a selection and click the "ALL" box it deselects the check.
I came up with this code, which works beautifully in combo to your suggestion, but only when I have one item selected. If there is anything more, then it still goofs up. Figured I would post to see if you or someone else could advise a solution for multiple selections.
Private Sub Mkts_Change()
Dim i As Integer, count As Integer
For i = 0 To Mkts.ListCount - 1
If Me.Mkts.Selected(i) = False Then
count = count + 1
End If
Next
If Me.cheMkts.Value = True And count <> Mkts.ListCount Then
Me.cheMkts.Value = False
End If
End Sub

Call a sub after this one has finished

I have a userform looping through a range with 2 settings; manual and automatic.
When I have an option button on my form set to manual, and click a next command button, I check the next cell in the range, change the contents of the form accordingly, then wait for the next button press.
However if I find the option button is set to automatic then instead of finishing up my code and waiting for the next button press, I have to call the next button press programmatically. That means that the previous subs each calling the next code slowly build up in the call stack, and I worry this will have some memory implications if I'm looping over a large range.
In code:
Private Sub nextItem()
Dim willShow As Boolean
returnResults 'return details from the form to the sheet
clearMemory 'clear out previous items on form
itemNo = itemNo + 1 'iterate over the range
SetupParts 'place new items on form
'do what next
Select Case displaySetting 'this variable holds the result from my option button in a custom enum "dispMode"
Case dispMode.Manual 'always show form
willShow = True
Case dispMode.SemiAutomatic 'show form based on condition
willShow = (Data.Count = 0) 'if SetupParts returns no data, display form, otherwise keep hidden
Case dispMode.Automatic 'don't show form
willShow = False
End Select 'there are actually a few more options here, but I've simplified
If willShow = False Then
If Me.Visible = True Then 'if needs to hide, and currently visible, then hide the form
Me.Hide
nextItem 'this is the problem, I call this code again, so the call stack grows
Else
'form is already hidden, do nothing
End If
ElseIf Me.Visible = False Then 'willShow must be True
Me.Show 'then wait for button click
End If
End Sub
Private Sub commandBtnNext_Click()
nextItem
End Sub
To avoid this problem, is there any way of getting nextItem to run immediately after the previous call of nextItem has run; ie. to tell a sub to run immediately after this one has finished (without introducing time delays). Or maybe this isn't an issue; if so, please explain why.
UPDATE
There is also a SemiAutomatic check to see which mode to use based on the contents of the userform. This is fine when calling recursively, but I can't see how to incorporate it into a looping approach.

VBA Excel Toggle Button: With 10 rows, how do I unhide each row separately and not in a group?

I'm working in VBA and Excel 2007 with active control toggle button, which I'm trying to figure out how to get to function the way I need it to. Will someone please help me out?
This works for only unhiding a single row at a time for two hidden rows:
Private Sub ToggleButton1_Click()
If ToggleButton1 Then
Rows(76).EntireRow.Hidden = False
Else
Rows(77).EntireRow.Hidden = False
End If
End Sub
This does not work for only unhiding a single row at a time for more than two hidden rows:
Private Sub ToggleButton1_Click()
If ToggleButton1 Then
Rows(76).EntireRow.Hidden = False
Else
Rows(77).EntireRow.Hidden = False
Else
Rows(78).EntireRow.Hidden = False
End If
End Sub
What do I need to do to get this to work? This is all I need the toggle button to do. Each row has identical information (text fields, field names, etc.), but I need each row to only become visible upon clicking just one toggle button. I know multiple toggle buttons will works like a breeze, but I really am wanting to just use one toggle button to unhide each row, one at a time. By default, the rows will be hidden first, too.
You can use a counter and then count down and you should be able to do what you are looking for. In this example if the counter is 0 we hide the rows 1-3 and then show each row untill we are back to 0 and then hide them again when we click the button.
Dim counter As Integer
Private Sub ToggleButton1_Click()
If counter = 0 Then
Rows(1).EntireRow.Hidden = True
Rows(2).EntireRow.Hidden = True
Rows(3).EntireRow.Hidden = True
counter = 3
Else
Rows(counter).EntireRow.Hidden = False
counter = counter - 1
End If
End Sub
Hope it helps
//KH
Try this:
Private Sub ToggleButton1_Click()
Dim rng As Range
Dim myrow As Range
Set rng = Me.Rows("75:85")
If Me.ToggleButton1 Then
For Each myrow In rng
If Not myrow.Hidden Then
myrow.Hidden = True
If myrow.row = 85 Then
myrow.Offset(-10, 0).Hidden = False
Else
myrow.Offset(1, 0).Hidden = False
End If
Exit For
End If
Next
End If
End Sub
This hides and unhides Rows76-85 simultaneously.
You start with all 10 Rows hidden.
After 1st click, Row 76 will appear and so on.
All will be hidden again after Row 85 was shown.
I used 10 rows based on your question title, adjust it to suit your needs.
Edit1:
Private Sub ToggleButton1_Click()
Dim rng As Range
Dim myrow As Range
Set rng = Me.Rows("76:85")
If Me.ToggleButton1 Then
For Each myrow In rng
If myrow.Hidden Then
myrow.Hidden = False
Exit Sub
End If
Next
rng.Hidden = True
End If
End Sub
Above will work as you want it.
Although you already accepted an answer, I still would want to correct my code.
At least for you and others reference.

Pausing a VBA loop to allow editing of worksheet, with or without userform

I have a procedure that consists of several do and for loops and i would like to find an easy way to 'pause' the routine and allow the user to edit the sheet, with a msgbox or userform to resume execution where it left off.
I would like to do something like this
dim pause as boolean
pause=false
For i = 1 To 40
Worksheets("sheet1").Range("A" & i) = i
If i = 20 Then
UserForm1.Show vbmodeless
Pause = true
Do until pause = false
loop
Else
End If
Next i
End Sub
Where the pause condition would be set by a sub on the userform. This do loop just crashes.
Ideally i would like the userform to have buttons that can run subs but also allow direct editing of cells while execution is paused.
Here is a typical control structure that allows the user to perform some actions in the middle of a macro. When the user is done, they run OKToContinue to allow the macro to continue with the second part:
Dim AllowedToContinue As Boolean
Sub FirstPartSecondPart()
AllowedToContinue = False
MsgBox "allow user to perform actions"
Do Until AllowedToContinue
DoEvents
Loop
MsgBox "doing second part"
End Sub
Sub OKToContinuw()
AllowedToContinue = True
End Sub

Printing from Excel VBA

Trying to print the same excel sheet a number of times (e.g 100) with a cell incremented each time (e.g cell 4F).
I tried using
Workbook_BeforePrint
to increment the cell, but it requires interaction with the "select printer" dialog for each printed sheet.
Would it be possible to make something like:
a = getIntegerUserInput()
for i in 1..a
increment 4F with one
print the sheet suppressing the "select printer" dialog
end for
Cheers
Have you selected a default printer?
I used this:
Sub printinc()
For i = 0 To 3
Range("A1").Value = Range("A1").Value + 1
Sheets("Sheet1").PrintOut
Next
End Sub
It printed 4 copies incrementing the value in cell A1 each time without prompting me for settings or printer selection.
To print a sheet, you can use this kind of code (assuming you know on which printer you want to print) using PrintOut:
Sub PrintFile()
Dim curPrinter As String
curPrinter = Application.ActivePrinter
Application.ActivePrinter = "Myprinter"
ActiveWindow.SelectedSheets.PrintOut
Application.ActivePrinter = curPrinter
End Sub
Hence, you can create a loop to increase a cell and print your worksheet with the increment.
By the way, you could do it using Before_print and if you don't want to display the print dialog, you can set Cancel to False while calling the procedure Private Sub Workbook_BeforePrint( Cancel As Boolean) (ref on MSDN)
You can also read this SO thread to prevent displaying the printing dialog: How do you prevent printing dialog when using Excel PrintOut method.
[EDIT] see Seyren's answer for a working solution on what you want. Yet, take care about the performance if you really wanted to loop 100 times.
Private Sub Workbook_BeforePrint(Cancel As Boolean)
'//supress recursion into this event when we print
Application.EnableEvents = False
'//increment
If Not IsNumeric(ActiveSheet.Range("A1").Value) Then ActiveSheet.Range("A1").Value = 0
ActiveSheet.Range("A1").Value = ActiveSheet.Range("A1").Value + 1
'//do a default print
ActiveSheet.PrintOut
Application.EnableEvents = True
'//prevent the default print
Cancel = True
End Sub