I am having a bit of trouble with some code and was wondering if someone could maybe assist. Basically I have 2 errors which I can't work out myself (too inexperienced with VBA, unfortunately)
Brief overview:
This macro is designed to generate a new workbook with copies of selected sheets from a "source" workbook in order to present to clients as a report batch. Essentially - we have master workbook "A" which may have 50 tabs or so, and we want to quickly select a couple of sheets to "copy" into a new workbook to save and send to a client. The code is a bit of a mess but I am not really sure what is going on/what I can remove etc.
Problems:
When you run the attached code/macro in Excel, it does everything it is supposed to do, however, it ALSO copies the sheet from which you run the macro. (i.e. I might be on sheet 1 in the Workbook. Run the macro to generate reports, checkbox menu appears and I select sheets 2, 5 & 9 - it will then copy into a new Workbook sheets 2, 5 & 9 AND sheet 1. But I never selected sheet 1 from the checkbox menu...)
Once this code has finished running, I am unable to save the Excel file. It just crashes and says "Microsoft Excel has stopped working" and then the file dies and I have to close Excel and recover etc. etc. I combined 2 pieces of code to get this working and I imagine I may be missing something crucial which is causing the problem. We have another piece of code to print sheets out in a similar way to this, and if I run this I am able to save with no problems.
Code:
I have included all the Visual Basic code (i.e. for the generate reports & print sheets macros).
I really don't have any experience with VBA so I hope someone will be able to assist! Thanks in advance :)
Sub PrintSelectedSheets()
Dim i As Integer
Dim TopPos As Integer
Dim SheetCount As Integer
Dim Printdlg As DialogSheet
Dim CurrentSheet As Worksheet, wsStartSheet As Worksheet
Dim CB As CheckBox
Application.ScreenUpdating = False
'Check for protected workbook
If ActiveWorkbook.ProtectStructure Then
MsgBox "Workbook is protected.", vbCritical
Exit Sub
End If
'Add a temporary dialog sheet
Set CurrentSheet = ActiveSheet
Set wsStartSheet = ActiveSheet
Set Printdlg = ActiveWorkbook.DialogSheets.Add
SheetCount = 0
'Add the checkboxes
TopPos = 40
For i = 1 To ActiveWorkbook.Worksheets.Count
Set CurrentSheet = ActiveWorkbook.Worksheets(i)
'Skip empty sheets and hidden sheets
If Application.CountA(CurrentSheet.Cells) <> 0 And _
CurrentSheet.Visible Then
SheetCount = SheetCount + 1
Printdlg.CheckBoxes.Add 78, TopPos, 150, 16.5
Printdlg.CheckBoxes(SheetCount).Text = _
CurrentSheet.Name
TopPos = TopPos + 13
End If
Next i
'Move the OK and Cancel buttons
Printdlg.Buttons.Left = 240
'Set dialog height, width, and caption
With Printdlg.DialogFrame
.Height = Application.Max _
(68, Printdlg.DialogFrame.Top + TopPos - 34)
.Width = 230
.Caption = "Select sheets to print"
End With
'Change tab order of OK and Cancel buttons
'so the 1st option button will have the focus
Printdlg.Buttons("Button 2").BringToFront
Printdlg.Buttons("Button 3").BringToFront
'Display the dialog box
CurrentSheet.Activate
wsStartSheet.Activate
Application.ScreenUpdating = True
If SheetCount <> 0 Then
'the following code will print the selected sheets as multiple print jobs.
'continuous page numbers will therefore not be printed
If Printdlg.Show Then
For Each CB In Printdlg.CheckBoxes
If CB.Value = xlOn Then
Worksheets(CB.Caption).Activate
ActiveSheet.PrintOut
'ActiveSheet.PrintPreview 'for debugging
End If
Next CB
'the following code will print the selected sheets as a single print job.
'This will allow the sheets to be printed with continuous page numbers.
'If Printdlg.Show Then
'For Each CB In Printdlg.CheckBoxes
'If CB.Value = xlOn Then
'Worksheets(CB.Caption).Select Replace:=False
'End If
'Next CB
'ActiveWindow.SelectedSheets.PrintOut copies:=1
'ActiveSheet.Select
Else
MsgBox "No worksheets selected"
End If
'End If
End If
'Delete temporary dialog sheet (without a warning)
Application.DisplayAlerts = False
Printdlg.Delete
'Reactivate original sheet
CurrentSheet.Activate
wsStartSheet.Activate
End Sub
Sub GenerateClientExcelReports()
'1. Declare variables
Dim i As Integer
Dim SheetCount As Integer
Dim TopPos As Integer
Dim lngCheckBoxes As Long, y As Long
Dim intTopPos As Integer, intSheetCount As Integer
Dim intHor As Integer 'this will be for the horizontal position of the items
Dim intWidth As Integer 'this will be for the overall width of the dialog box
Dim intLBLeft As Integer, intLBTop As Integer, intLBHeight As Integer
Dim Printdlg As DialogSheet
Dim CurrentSheet As Worksheet, wsStartSheet As Worksheet
Dim CB As CheckBox
'Dim wb As Workbook
'Dim wbNew As Workbook
'Set wb = ThisWorkbook
'Workbooks.Add ' Open a new workbook
'Set wbNew = ActiveWorkbook
On Error Resume Next
Application.ScreenUpdating = False
'2. Check for protected workbook
If ActiveWorkbook.ProtectStructure Then
MsgBox "Workbook is protected.", vbCritical
Exit Sub
End If
'3. Add a temporary dialog sheet
Set CurrentSheet = ActiveSheet
Set wsStartSheet = ActiveSheet
Set Printdlg = ActiveWorkbook.DialogSheets.Add
SheetCount = 0
'4. Add the checkboxes
TopPos = 40
For i = 1 To ActiveWorkbook.Worksheets.Count
Set CurrentSheet = ActiveWorkbook.Worksheets(i)
'5. Skip empty sheets and hidden sheets
If Application.CountA(CurrentSheet.Cells) <> 0 And _
CurrentSheet.Visible Then
SheetCount = SheetCount + 1
Printdlg.CheckBoxes.Add 78, TopPos, 150, 16.5
Printdlg.CheckBoxes(SheetCount).Text = _
CurrentSheet.Name
TopPos = TopPos + 13
End If
Next i
'6. Move the OK and Cancel buttons
Printdlg.Buttons.Left = 240
'7. Set dialog height, width, and caption
With Printdlg.DialogFrame
.Height = Application.Max _
(68, Printdlg.DialogFrame.Top + TopPos - 34)
.Width = 230
.Caption = "Select sheets to generate"
End With
'8. Change tab order of OK and Cancel buttons
' so the 1st option button will have the focus
Printdlg.Buttons("Button 2").BringToFront
Printdlg.Buttons("Button 3").BringToFront
'9. Display the dialog box
CurrentSheet.Activate
wsStartSheet.Activate
Application.ScreenUpdating = True
If SheetCount <> 0 Then
If Printdlg.Show Then
For Each CB In Printdlg.CheckBoxes
If CB.Value = xlOn Then
Worksheets(CB.Caption).Select Replace:=False
'For y = 1 To ActiveWorkbook.Worksheets.Count
'If WorksheetFunction.IsNumber _
'(InStr(1, "ActiveWorkbook.Sheets(y)", "Contents")) = True Then
'CB.y = xlOn
'End If
End If
Next
ActiveWindow.SelectedSheets.Copy
Else
MsgBox "No worksheets selected"
End If
End If
'Delete temporary dialog sheet (without a warning)
'Application.DisplayAlerts = False
'Printdlg.Delete
'Reactivate original sheet
'CurrentSheet.Activate
'wsStartSheet.Activate
'10. Delete temporary dialog sheet (without a warning)
Application.DisplayAlerts = False
Printdlg.Delete
'11. Reactivate original sheet
CurrentSheet.Activate
wsStartSheet.Activate
Application.DisplayAlerts = True
End Sub
Sub SelectAllCheckBox()
Dim CB As CheckBox
For Each CB In ActiveSheet.CheckBoxes
If CB.Name <> ActiveSheet.CheckBoxes(1).Text Then
CB.Value = ActiveSheet.CheckBoxes(1).Value
End If
Next CB
'ActiveSheet.CheckBoxes("Check Box 1").Value
End Sub
as for problem n°1
add a declaration of a boolean variable
Dim firstSelected As Boolean
and then modify the For Each CB In Printdlg.CheckBoxes loop block code as follows
If CB.Value = xlOn Then
If firstSelected Then
Worksheets(CB.Caption).Select Replace:=False
Else
Worksheets(CB.Caption).Select
firstSelected = True
End If
'For y = 1 To ActiveWorkbook.Worksheets.Count
'If WorksheetFunction.IsNumber _
'(InStr(1, "ActiveWorkbook.Sheets(y)", "Contents")) = True Then
'CB.y = xlOn
'End If
End If
since there's always an ActiveWorksheet when macro starts and thus if you only use Worksheets(CB.Caption).Select Replace:=False statement you keep adding it to the via Printdlg selected sheets.
Related
I have a Cell range Sheets("INVOICE MAKER").Range("D18:D37") (Total 20 Cells), and a little UserForm with name Add Items.
In UserForm there are one Textbox and one Submit Button.
So if I write something in that Textbox and click on Submit Button, Data should be write to next available empty cell in range Sheets("INVOICE MAKER").Range("D18:D37"). And if all 20 cells are filled with data then show a message like "No more rows are available to write data".
Below code don't start writing data from Cell D18, its start writing data from D1.
and doesn't stop after cell D37.
Option Explicit
Dim lRow As Long
Dim ws As Worksheet
Set ws = Worksheets("INVOICE MAKER")
lRow = ws.Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
'check for a part number
If Trim(Me.Textbox.Value) = "" Then
Me.Textbox.SetFocus
MsgBox "Please Type Item Name"
Exit Sub
End If
With ws
.Cells(lRow, 5).Value = Me.Textbox.Value
End With
End Sub
Hope below code will help you:
Public Working_Sheet As Worksheet
Public All_Cell_Value As Boolean
Public Write_Cell_No As Integer
Public Content As String
'When button in the form is clicked
Sub Button1_Click()
Write_Content
End Sub
'validation and content writing function
Public Function Write_Content()
All_Cell_Value = True
Set Working_Sheet = Worksheets("Sheet1")
For i = 18 To 37
If Trim(Working_Sheet.Cells(i, "D")) = "" Then
All_Cell_Value = False
Write_Cell_No = i
Exit For
End If
Next i
If All_Cell_Value = False Then
Content = InputBox("Enter the value")
If Content = "" Then
MsgBox ("No Data")
Else
Working_Sheet.Cells(i, "D").Value = Content
End If
Else
MsgBox ("Sorry content is full")
End If
End Function
Maybe this will help!
EDIT #1
Fix some errors, and the code must to be inside a Form, with the TextBox and the Button
EDIT #2
Added a closing statement for the userform or the macro
Option Explicit
Private Sub CommandButton1_Click()
Dim lRow As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("INVOICE MAKER") 'Set from Thisworkbook
ws.Activate 'activate the Invoice Maker
'lRow = ws.Cells.Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
Dim iniCell As Integer: iniCell = 18
Dim endCell As Integer: endCell = 37
Dim setCol As Integer: setCol = ws.Range("D1").Column
'As you said, the range is D18:D37, then you can manipulate from this 3 vars
Dim iRange As Range
Dim i As Range
Dim isTheRangeFull As Boolean: isTheRangeFull = False
Dim iMsgbox As Integer
If Trim(Me.TextBox.Value) = "" Then
Me.TextBox.SetFocus
'Ask the user to retry or quit.
iMsgbox = MsgBox("Please Type Item Name" & Chr(10) & "Do you want to retry?", vbYesNo + vbDefaultButton1)
If iMsgbox = 6 Then
GoTo InsertData
'if the user say YES
'do it again
ElseIf iMsgbox = 7 Then
End
'if the user say NO
End If
End If
Set iRange = ws.Range(Cells(iniCell, setCol), Cells(endCell, setCol))
'set your working Range
'This Loop do the job!
For Each i In iRange
isTheRangeFull = True
'if there is no empty cell, won't enter the if, and
'the var continue TRUE, so there is no empty cells...
If i.Value = Empty Then
i.Value = Me.TextBox.Value
isTheRangeFull = False
'the Next line (End) Will close the Form and terminate the macro
'End
'The next line just close the userform
'Unload Me
'Decide which one to uncomment.
Exit For
End If
Next i
If isTheRangeFull Then
MsgBox "No more rows are available to write data"
End
End If
'With ws
' .Cells(lRow, 5).Value = Me.TextBox.Value
'End With
InsertData:
End Sub
I created several VBA functions and macros to automate my work, but as more data goes in, I'm noticing a larger delay in running my macros. Are there any things that I can change or alter in my code to improve its efficiency?
Premise of the program:
- Refresh button loops through all worksheets, changes colors based on their completion, and puts information of "Incomplete/Expired" forms in a table (slowest)
'===============
'Refresh Button on MASTER PAGE
'Functions: Updates color of sheets, based on completion/incompletion
' Removes inputs from MASTER page
' Updates Expired Forms cells
'====================
Sub refresh_form()
Dim ws As Worksheet
Dim wb As Workbook
Dim wsMASTER, wsTEMP
Dim complete, incomplete, exp, default 'to store color index's
Dim expName, expDate, expGSA, expStatus 'to store values for expired forms
Dim lastRow As Long 'to store row # for expired & incomplete form
'CLEARS DATA FROM MAIN SHEET
ThisWorkbook.Worksheets("MASTER").Range("C6").Value = "" 'Project name
ThisWorkbook.Worksheets("MASTER").Range("C7").Value = "" 'Address
ThisWorkbook.Worksheets("MASTER").Range("C8").Value = "" 'Date
ThisWorkbook.Worksheets("MASTER").Range("C9").Value = "" 'GSA #
ThisWorkbook.Worksheets("MASTER").Range("C10").Value = "" 'Exp Date
wsMASTER = "MASTER" 'Sets wsMASTER as MASTER worksheet
wsTEMP = "TEMPLATE" 'Sets wsTEMP as TEMPLATE worksheet
complete = 4 'Green
incomplete = 44 'Orange
default = 2 'White
exp = 3 'Red
lastRow = 5 'Expired & Incomplete row starts at 5
For Each ws In ThisWorkbook.Worksheets 'Loops through all worksheets on click
If ws.Name = wsMASTER Or ws.Name = wsTEMP Then 'For MASTER and TEMPLATE sheet, skip
ws.Tab.ColorIndex = default
ElseIf ws.Range("$M12").Value = True And ws.Range("$M$15").Value = True Then 'Applies "Exp" tab color to expired/incomp forms
ws.Tab.ColorIndex = exp
expName = ws.Range("$C$5").Value 'Stores current form's project name
expDate = ws.Range("$C$9").Value '***expiration date
expGSA = ws.Range("$C$8").Value '***GSA number
lastRow = lastRow + 1 'increments lastRow by a value of 1
'VALUES INPUTTED IN EXPIRED & INCOMPLETE FORM
ThisWorkbook.Worksheets("MASTER").Range("K" & lastRow).Value = expGSA ' GSA #
ThisWorkbook.Worksheets("MASTER").Range("L" & lastRow).Value = expName ' Project name
ThisWorkbook.Worksheets("MASTER").Range("M" & lastRow).Value = expDate ' Expiration date
ElseIf ws.Range("$M$12").Value = True Then 'Applies "Incomplete" tab color to incomplete forms
ws.Tab.ColorIndex = incomplete
ElseIf ws.Range("$M$12").Value = False And ws.Range("$N$12").Value = True Then 'Applies "Complete" tab color to complete forms
ws.Tab.ColorIndex = complete
Else 'Applies "Default" tab color to any untouched forms
ws.Tab.ColorIndex = default
End If
Next ws 'End Loop
End Sub 'End Sub
This question is probably best answered at Code Review, but a simple way to increase performance would be to do something like below:
'===============
'Refresh Button on MASTER PAGE
'Functions: Updates color of sheets, based on completion/incompletion
' Removes inputs from MASTER page
' Updates Expired Forms cells
'====================
Sub refresh_form()
Dim ws As Worksheet
Dim wsMaster As Worksheet: Set wsMaster = Worksheets("MASTER")
Dim wb As Workbook
Dim wsTEMP As String
Dim complete As Integer, incomplete As Integer, exp As Integer, default As Integer 'to store color index's
Dim lastRow As Long 'to store row # for expired & incomplete form
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
'CLEARS DATA FROM MAIN SHEET
wsMaster.Range("C6:C10").ClearContents
complete = 4 'Green
incomplete = 44 'Orange
default = 2 'White
exp = 3 'Red
lastRow = 5 'Expired & Incomplete row starts at 5
For Each ws In ThisWorkbook.Worksheets 'Loops through all worksheets on click
If ws.Name = wsMaster.Name Or ws.Name = "TEMPLATE" Then 'For MASTER and TEMPLATE sheet, skip
ws.Tab.ColorIndex = default
ElseIf ws.Range("$M12").Value = True And ws.Range("$M$15").Value = True Then 'Applies "Exp" tab color to expired/incomp forms
ws.Tab.ColorIndex = exp
lastRow = lastRow + 1 'increments lastRow by a value of 1
wsMaster.Range("K" & lastRow).Value = ws.Range("$C$8").Value 'GSA #
wsMaster.Range("L" & lastRow).Value = ws.Range("$C$5").Value 'Project name
wsMaster.Range("M" & lastRow).Value = ws.Range("$C$9").Value 'Expiration date
ElseIf ws.Range("$M$12").Value = True Then 'Applies "Incomplete" tab color to incomplete forms
ws.Tab.ColorIndex = incomplete
ElseIf ws.Range("$M$12").Value = False And ws.Range("$N$12").Value = True Then 'Applies "Complete" tab color to complete forms
ws.Tab.ColorIndex = complete
Else 'Applies "Default" tab color to any untouched forms
ws.Tab.ColorIndex = default
End If
Next ws 'End Loop
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Add this to the beginning of your code below your DIMs
Application.calculation=xlcalculationmanual
application.screenupdating=false
application.displaystatusbar=false
application.enableevents=false
then add this at the end of your code before end sub
Application.calculation=xlcalculationautomatic
application.screenupdating=true
application.displaystatusbar=true
application.enableevents=true
This should help speed up your code.
The majority of your macro is not doing anything extremely intensive. The most intensive operation Excel is doing is updating the UI when it switches between worksheets. You may see a significant improvement if you temporarily disable UI updating.
Before you enter your For Each loop, call
Application.ScreenUpdating = False
And before your Sub Routine exits, restore screen updating
Application.ScreenUpdating = True
There is not a lot else you can do to improve the performance of the code. Other optimization options would be to keep the number of Worksheets to a minimum, or using multiple Workbooks.
Three issues.
This code is running in 4-5 minutes for me with the database that I currently have. Normally it will be a database with 100~ columns. I want to make this faster.
Another issue I have is that I keep getting two different pop-ups:
"File now Available for Editing"
"User is currently editing workbook, would you like to run in read-only mode?"
Very annoying, but nothing I can't live with.
Lastly, I also sometimes get an error on this line:
pptSlide.Shapes.PasteSpecial DataType:=ppPasteHTML, Link:=msoFalse But all I have to do is re-run the program and it'll go away.
I'm looking for any suggestions to make this code run a little faster and smoother, any recommendations are welcome.
Thanks!
Public Sub averageScoreRelay()
' 1. Run from PPT and open an Excel file
' 2. Start at slide 1 and find a box that contains the words "iq_", if it has those words then it will have numbers after it like so "iq_43" or "iq_43, iq_56,iq_72".
' 3. find those words and numbers in the opened Excel file after splitting and re-formating string.
' 3. Copy column into a new sheets and repeat for all "iq_'s" until sheets 2 has a table.
' 4. Copy table from xl Paste Table into ppt
' 5. Do this for every slide
'Timer start
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'Create variables
Dim xlApp As Excel.Application
Dim xlWB As Excel.Workbook
Dim pptSlide As Slide
Dim Shpe As Shape
Dim pptText As String
Dim pptPres As Object
Dim iq_Array As Variant
Dim arrayLoop As Integer
Dim i As Integer
Dim myShape As Object
Dim colNumb As Integer
Dim size As Integer
Dim k As Integer
Dim lRows As Long
Dim lCols As Long
' Create new excel instance and open relevant workbook
Set xlApp = New Excel.Application
'xlApp.Visible = True 'Make Excel visible
Set xlWB = xlApp.Workbooks.Open("file.xlsx", True, False, , , , True, Notify:=False) 'Open relevant workbook
If xlWB Is Nothing Then ' may not need this if statement. check later.
MsgBox ("Error retrieving Average Score Report, Check file path")
Exit Sub
End If
xlApp.DisplayAlerts = False
With xlWB.Worksheets("Sheet1")
colNumb = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
'Create a new blank Sheet in excel, should be "Sheet2"
xlWB.Worksheets.Add After:=xlWB.ActiveSheet
'Make pptPres the ppt active
Set pptPres = PowerPoint.ActivePresentation
'Loop through each pptSlide and check for IQ text box, grab avgScore values and create pptTable
For Each pptSlide In pptPres.Slides
pptSlide.Select
'searches through shapes in the slide
For Each Shpe In pptSlide.Shapes
k = 1
'Identify if there is text frame
If Shpe.HasTextFrame Then
'Identify if there's text in text frame
If Shpe.TextFrame.HasText Then
'Set pptText as the Text in the box, then make it lowercase and trim Spaces and Enters
pptText = Shpe.TextFrame.TextRange
pptText = LCase(Replace(pptText, " ", vbNullString))
pptText = Replace(Replace(Replace(pptText, vbCrLf, vbNullString), vbCr, vbNullString), vbLf, vbNullString)
'Identify if within text there is "iq_"
If InStr(1, pptText, "iq_") > 0 Then
'set iq_Array as an array of the split iq's
iq_Array = Split(pptText, ",")
'Find size of the array
size = UBound(iq_Array) - LBound(iq_Array)
'loop for each iq_ in the array'
For arrayLoop = 0 To size
'Statement that will take iq_'s in the form "iq_9" or "iq_99" or "iq_999"
If iq_Array(arrayLoop) Like "iq_#" Or iq_Array(arrayLoop) Like "iq_##" Or iq_Array(arrayLoop) Like "iq_###" Then
'loops for checking each column
For i = 1 To colNumb
'Copies the first column (role column) for every slide that needs it
If i = 1 And arrayLoop = 0 Then
'copy column
xlWB.Worksheets("Sheet1").Columns(1).Copy
'paste column in Sheet2 which was newly created
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(1)
'If this is not the role column, then check to see if the iq_'s match from ppt to xl
ElseIf xlWB.Worksheets("Sheet1").Cells(1, i) = iq_Array(arrayLoop) And i <> 1 Then
'Serves to paste in the next column of Sheet2 so that we end up with a table
k = k + 1
'same as above
xlWB.Worksheets("Sheet1").Columns(i).Copy
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(k)
'Go to next array
GoTo Line2
End If
Next i
'Same as above, just this one is for iq_'s with form "iq_45,46,47" instead of "iq_45,iq_46,iq_47"
ElseIf (iq_Array(0) Like "iq_#" Or iq_Array(0) Like "iq_##" Or iq_Array(0) Like "iq_###") And (IsNumeric(iq_Array(arrayLoop)) And Len(iq_Array(arrayLoop)) <= 3) Then
For i = 1 To colNumb
If i = 1 And arrayLoop = 0 Then
xlWB.Worksheets("Sheet1").Columns(1).Copy
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(1)
ElseIf xlWB.Worksheets("Sheet1").Cells(1, i) = ("iq_" & iq_Array(arrayLoop)) And i <> 1 Then 'if iq in ppt = iq in xl and if not the first cell then execute
k = k + 1
xlWB.Worksheets("Sheet1").Columns(i).Copy
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(k)
GoTo Line2
End If
Next i
End If
Line2:
Next arrayLoop
End If
End If
End If
Next Shpe
'calculate last row and last column on sheet2. aka. find Table size
With xlWB.Worksheets("Sheet2")
lRows = .Cells(.Rows.Count, 1).End(xlUp).Row
lCols = .Cells(1, .Columns.Count).End(xlToLeft).Column
'If only one column then go to next slide
If lRows = .Cells(1, 1).End(xlUp).Row And lCols = .Cells(1, 1).End(xlToLeft).Column Then
GoTo Line1
End If
'Copy table
.Range(.Cells(1, 1), .Cells(lRows, lCols)).Copy
End With
'Paste Table into ppt
pptSlide.Shapes.PasteSpecial DataType:=ppPasteHTML, Link:=msoFalse
'Recently pasted shape is the last shape on slide, so it will be the same as count of shapes on slide
Set myShape = pptSlide.Shapes(pptSlide.Shapes.Count)
'Set position:
myShape.Left = -200
myShape.Top = 200
'Clear Sheet2 for next slide
xlWB.Worksheets("Sheet2").Range("A1:P10").Clear
Line1:
Next pptSlide
xlWB.Worksheets("Sheet2").Delete
xlWB.Close
xlApp.Quit
xlApp.DisplayAlerts = True
'End Timer
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub
Basically the module Onboarding is asking the path of the tracker i want to update. I am updating details in the
sheet1 of the tracker.
I am setting the values of fields in userform 'OnboardingForm' to blank(so that the values entered last time to the
form is not visible when I am opening the form this time.
Now I am opening the form 'OnboardingForm' and entering values in the subsequent fields.
I have put a check button in my userform 'OnboardingForm' which is invisible to the front end user.
Now in the tracker there is a sheet named 'Project Tracks' which has information of all current projects
Once the submit button is clicked the control will go to the tracker's 'Project Tracks' sheet. It will validate the
track entered in the userform 'OnboardingForm' with the tracks present in the tracker's 'Project Tracks' sheet. Once found the other details against that particular track will get fetched to the tracker's sheet1(this I have done so that I will not have to enter values manually to the userform 'OnboardingForm' so that the form looks simple). There are no chances of the track not
matching.
Now one command button new track has been put in my current userform 'OnboardingForm'. Once clicked this will take the control to
the userform2 'ProjectTracksForm'.This is basically put so that if I am adding a new track, the form takes the detail and enters in the
tracker's 'Project Tracks' sheet.
Question 1> My current userform's Track button is a combo box. How do I add values in the dropdown from the tracker's
'Project Tracker' sheet to the dropdown.
Question 2> Once I add a new track in userform2 'ProjectTracksForm',submit and then when I come back to my current
userform 'OnboardingForm' that added track should be shown in the dropdown of Track combo box.
Please find below my piece of code.
This is my module for onboarding
Public Sub OnBoarding()
On Error GoTo ErrorHandler
Dim Owb As Object
Dim ran As Range
strTalentTrackerPath = shTracker.Cells(2, 2).Value
'Default the form values to null
With OnboardingForm
.combTrackofWork.Value = ""
.txtFirstName.Text = ""
.txtLastName.Text = ""
.combResCat.Value = ""
.combBFTE.Value = ""
.combLevel.Value = ""
.combLocType = ""
.txtAccessInfo.Text = ""
End With
OnboardingForm.Show
SetFocus.combTrackofWork
With OnboardingForm
'Details to be entered in the form'
strTOW = Trim$(.combTrackofWork.Value)
strFN = Trim$(.txtFirstName.Text)
strLN = Trim$(.txtLastName.Text)
strResCat = Trim$(.combResCat.Value)
strBilFTE = Trim$(.combBFTE.Value)
strLevel = Trim$(.combLevel.Value)
strLocType = (.combLocType.Value)
strAccessInfo = (.txtAccessInfo.Text)
End With
If OnboardingForm.chkOKButtonClick = True Then
Set oExcel = New Excel.Application
strMyFolder = strTalentTrackerPath
Set Owb = oExcel.Workbooks.Open(strMyFolder)
IntRowCount = Owb.Sheets(1).UsedRange.Rows.Count
With Owb.Sheets(1)
With Owb.Sheets("Project Tracks")
IntTrackRowCount = .UsedRange.Rows.Count
For IntCurrentRow = 1 To IntTrackRowCount
If .Cells(IntCurrentRow, 1) = strTOW Then
Owb.Sheets(1).Cells(IntRowCount + 1, OnboardingFormcolumn.colTrackofWork) _
= .Cells(IntCurrentRow, ProjectTrackscolumn.colTrack)
Owb.Sheets(1).Cells(IntRowCount + 1, OnboardingFormcolumn.colBPO) = .Cells _
(IntCurrentRow, ProjectTrackscolumn.colBPO)
Owb.Sheets(1).Cells(IntRowCount + 1, OnboardingFormcolumn.colCostCenter) _
= .Cells(IntCurrentRow, ProjectTrackscolumn.colCostCenter)
Owb.Sheets(1).Cells(IntRowCount + 1, OnboardingFormcolumn.colGroup) _
= .Cells(IntCurrentRow, ProjectTrackscolumn.colGroup)
Exit For
End If
Next
End With
End With
.Cells(IntRowCount + 1, OnboardingFormcolumn.colTrackofWork) = strTOW
.Cells(IntRowCount + 1, OnboardingFormcolumn.colFirstName) = strFN
.Cells(IntRowCount + 1, OnboardingFormcolumn.colLastName) = strLN
.Cells(IntRowCount + 1, OnboardingFormcolumn.colResourceCategory) = strResCat
.Cells(IntRowCount + 1, OnboardingFormcolumn.colBilledFTE) = strBilFTE
.Cells(IntRowCount + 1, OnboardingFormcolumn.colLevel) = strLevel
.Cells(IntRowCount + 1, OnboardingFormcolumn.colLocationType) = strLocType
.Cells(IntRowCount + 1, OnboardingFormcolumn.colAccessInformation) = strAccessInfo
Owb.Close True
Set Owb = Nothing
Set oExcel = Nothing
Else
Exit Sub
End If
Exit Sub
ErrorHandler:
If Owb Is Nothing Then
Else
Owb.Close False
End If
If oExcel Is Nothing Then
Else
Set oExcel = Nothing
End If
MsgBox "Unhandled Error. Please Report" & vbCrLf & "Error Description: " & _
Err.Description, vbExclamation
End Sub
This is for cancel button of Onboarding Form
Private Sub cmdbtn_Cancel_Click()
OnboardingForm.Hide
MsgBox ("No data entered")
End Sub
This is for OnboardingForm submit button
Private Sub cmdbtn_Submit_Click()
If Trim(OnboardingForm.combTrackOfWork.Value) = "" Then
OnboardingForm.combTOW.SetFocus
MsgBox ("Track of Work cannot be blank")
Exit Sub
End If
If Trim(OnboardingForm.txtFirstName.Value) = "" Then
OnboardingForm.txtFN.SetFocus
MsgBox ("First name cannot be blank")
Exit Sub
End If
If Trim(OnboardingForm.txtLastName.Value) = "" Then
OnboardingForm.txtLN.SetFocus
MsgBox ("Last name cannot be blank")
Exit Sub
End If
End Sub
Module for Project Tracks
Public Sub prjctTracks()
On Error GoTo ErrorHandler
Dim Owb As Object
strTalentTrackerPath = shTracker.Cells(2, 2).Value
With ProjectTracksForm
.txtTOW = ""
.txtBPO = ""
.txtCOCE = ""
.txtSOW = ""
.txtGroup = ""
End With
ProjectTracksForm.Show
With ProjectTracksForm
strTOW = Trim$(.txtTOW.Text)
strBPO = Trim$(.txtBPO.Text)
strCOCE = Trim$(.txtCOCE.Text)
strSOW = Trim$(.txtSOW.Value)
strGroup = Trim$(.txtGroup.Value)
End With
ProjectTracksForm.Hide
If ProjectTracksForm.chkbtn_OKclick = True Then
Set oExcel = New Excel.Application
strMyFolder = strTalentTrackerPath
Set Owb = oExcel.Workbooks.Open(strMyFolder)
With Owb.Sheets("Project Tracks")
intUsedRowCount = .UsedRange.Rows.Count
.Cells(intUsedRowCount + 1, Trackscolumn.colTrack) = strTOW
.Cells(intUsedRowCount + 1, Trackscolumn.colBPO) = strBPO
.Cells(intUsedRowCount + 1, Trackscolumn.colCostCenter) = strCOCE
.Cells(intUsedRowCount + 1, Trackscolumn.colSOW) = strSOW
.Cells(intUsedRowCount + 1, Trackscolumn.colGroup) = strGroup
End With
Owb.Close True
Set Owb = Nothing
Set oExcel = Nothing
Else
Exit Sub
End If
Exit Sub
ErrorHandler:
If Owb Is Nothing Then
Else
Owb.Close False
End If
If oExcel Is Nothing Then
Else
Set oExcel = Nothing
End If
MsgBox "Unhandled Error. Please Report" & vbCrLf & "Error Description: " & _
Err.Description, vbExclamation
End Sub
Question 1> My current userform's Track button is a combo box. How do
I add values in the dropdown from the tracker's 'Project Tracker'
sheet to the dropdown.
I am calling the combobox "ComboBox1" in this example
The Range to place in the combobox would look like this...
The code to populate the combobox would be in the Userform Module.
Private Sub UserForm_Initialize()
Dim LstRw As Long
Dim Rng As Range
Dim ws As Worksheet
Set ws = Sheets("Project Tracker")
With ws
LstRw = .Cells(.Rows.Count, 1).End(xlUp).Row
Set Rng = .Range("A2:A" & LstRw)
End With
ComboBox1.List = Rng.Value
End Sub
Question 2> Once I add a new track in userform2
'ProjectTracksForm',submit and then when I come back to my current
userform 'OnboardingForm' that added track should be shown in the
dropdown of Track combo box
When you activate your userform again, you can clear the combobox and repopulate it with the new list.
Private Sub UserForm_Activate()
Dim LstRw As Long
Dim Rng As Range
Dim ws As Worksheet
Set ws = Sheets("Project Tracker")
With ws
LstRw = .Cells(.Rows.Count, 1).End(xlUp).Row
Set Rng = .Range("A2:A" & LstRw)
End With
ComboBox1.Clear
ComboBox1.List = Rng.Value
End Sub
I assume that somewhere you would have a code that will add a new item to the List in sheet("Project Tracker"),
Something like:
Private Sub CommandButton1_Click()
'THIS IS IN THE OTHER USERFORM
'add item to first blank cell in column A sheets("Project Tracker")
Dim sh As Worksheet
Dim LstRws As Long
Set sh = Sheets("Project Tracker")
With sh
LstRws = .Cells(.Rows.Count, "A").End(xlUp).Row + 1
.Cells(LstRws, 1) = "SomeThingNew" 'whatever you are adding to the list
End With
End Sub
The code will add something new to the list in your worksheet.
When you show the form again, the new item will be in the combobox.
You can either use a Button, Combobox event, textbox event, to add the item to the new list.
I have use a button (by clicking) to create a new sheet and insert a button on it, but I want to import codes to the new button (here is MyPrecodedButton).
Private Sub CommandButton1_Click()
Dim z As Integer
Dim wb As Workbook
Dim ws2 As Worksheet, wsnew As Worksheet
Set wb = ThisWorkbook
Set ws2 = wb.Sheets("Sheet2")
z = ws2.Cells(2, 1).Value
Set wsnew = Sheets.Add ' Declare your New Sheet in order to be able to work with after
wsnew.Name = "PIAF_Summary" & z
z = z + 1
With wsnew.Range("A1:G1")
.Merge
.Interior.ColorIndex = 23
.Value = "Project Name (To be reviewed by WMO)"
.Font.Color = vbWhite
.Font.Bold = True
.Font.Size = 13
End With
ws2.Cells(2, 1).Value = z
Dim Rngc As Range: Set Rngc = wsnew.Range("F35")
With ActiveSheet.OLEObjects.Add(ClassType:="Forms.CommandButton.1", Link:=False, DisplayAsIcon:=False, Left:=Rngc.Left, Top:=Rngc.Top, Width:=205, Height:=20)
.Name = "MyPrecodedButton" ' change the name
End With
End sub
Here is the code for MyPrecodedButton
Public Sub MyPrecodedButton_Click()
MsgBox "Co-Cooo!"
End Sub
Let's demonstrate briefly what you can do with VBA to Add buttons.
Below code will Add a button to cell B2 if the ActiveSheet is not "Sheet1".
Option Explicit
Sub SayHello()
MsgBox "Hello from """ & ActiveSheet.Name & """"
End Sub
Sub AddButton()
Dim oRng As Range
Dim oBtns As Buttons ' Add "Microsoft Forms 2.0 Object Library" to References if you want intellisense
If ActiveSheet.Name <> "Sheet1" Then ' Only works if it's not "Sheet1"
Set oRng = Range("B2")
Set oBtns = ActiveSheet.Buttons
With oBtns.Add(oRng.Left * 1.05, oRng.Top * 1.05, oRng.Width * 0.9, oRng.Height * 2 * 0.9)
.Caption = "Say Hello!"
.OnAction = "SayHello"
End With
Set oBtns = Nothing
Set oRng = Nothing
End If
End Sub
Before and After screenshots:
Now Clicking on the button:
So, if you code is generic enough (to work with all your possible situations), there is no need to Add Codes via code. i.e. have your codes ready, then just assign the button's OnAction property to call the correct Sub.