Hello stackoverflow users,
I am facing the following problem, I receive a very big Excel table every day and would like to simplify it. So I decided to automatize this task, I wrote a VBA script and saved it as a module.
I open and execute it, sometimes it works. I am searching for hours already for any hint.
Function HideRows()
ActiveSheet.Rows("2:2").EntireRow.Hidden = True
ActiveSheet.Rows("5:5").EntireRow.Hidden = True
ActiveSheet.Rows("8:8").EntireRow.Hidden = True
ActiveSheet.Rows("10:10").EntireRow.Hidden = True
ActiveSheet.Rows("11:11").EntireRow.Hidden = True
ActiveSheet.Rows("24:24").EntireRow.Hidden = True
ActiveSheet.Rows("29:29").EntireRow.Hidden = True
ActiveSheet.Rows("30:30").EntireRow.Hidden = True
ActiveSheet.Rows("31:31").EntireRow.Hidden = True
ActiveSheet.Rows("37:37").EntireRow.Hidden = True
End Function
Function HideColumns()
Dim rng As Range
For Each rng In Range("C:J").Columns
rng.EntireColumn.Hidden = True
Next rng
For Each rng In Range("L:M").Columns
rng.EntireColumn.Hidden = True
Next rng
End Function
Function FilterByAttributes()
beginRow = 1
EndRow = Cells(Rows.Count, 1).End(xlUp).row
ActiveSheet.Range("K" & EndRow).AutoFilter Field:=11, Criteria1:=RGB(255, 0, 0), Operator:=xlFilterCellColor
End Function
Private Sub CommandButton1_Click()
Call HideColumns
Call HideRows
Call FilterByAttributes
End Sub
Is there any better possibility to format the table with less amount of clicks according to the conditions in my script?
UPDATE: the algorithm of my actions:
Download excel table from my email
Open this excel table
Open "Developer tools tab"->Visual Basic-> File-> Import->Select module->Execute Module. This step has to be somehow simplified, have no ideas how
Continue working with the resultant table
I would like to make as less clicks as possible for the "special filter"
Thanks in advance
Some thoughts:
1) Consider adding the macro to a personal workbook instead of importing it every day to a new excel file.
2) You don't need a loop to hide columns: ActiveSheet.Columns("C:J").Hidden = True, and similarly for .Columns("L:M").
3) The Call keyword can be dropped.
4) Add Option Explicit to the top of the module and declare all variables, specifically beginRow and EndRow.
Related
I've been making what started out as a basic asset tracker, but as its progressed more and more has been added.
before this I've never done anything with excel, meaning most of what i have done has been through searching, copying, and making slight changes to code found online.
Im now trying to search for a client name held on a "Database" sheet and display all assets assigned to that client on a "Reports" sheet
I have "Userform5" with a command box auto populating the name of the client as you begin typing
Client name is often referred to differently, so the official client name is loaded into the command box, however on the spreadsheet it may have been inputted as a shortened version of the name. Eg, Tarmac Trading Limited is the official name loaded into the command box, on the Database this may be input as just Tarmac or another slight variation.
i have a code that works fine, but only for exact matches, and will even disregard if a space has been entered at the end of the client name on "database"
is there a way to change the below code to search for partial matches?
Sub ClientSearch_Click()
Application.ScreenUpdating = False
Dim Client As String
Dim finalrow As Integer
Dim i As Integer
Sheets("Reports").Range("A2:aj10000").ClearContents
Client = Trim(Cmbclient.Text)
Sheets("Database").Activate
finalrow = Sheets("Database").Range("A10000").End(xlUp).Row
For i = 2 To finalrow
'Function equalsIgnoreCase(str1 As String, str2 As String) As Boolean
' equalsIgnoreCase = LCase(str1) = LCase(str2)
'End Function
If LCase(Sheets("Database").Cells(i, 4)) = LCase(Client) Then
Range(Cells(i, 1), Cells(i, 50)).Copy
Sheets("Reports").Range("A100").End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
End If
Next i
Sheets("Reports").Activate
Range("A:AZ").EntireColumn.Hidden = True
Range("B:B,D:D,E:E,F:F,G:G,H:H,I:I,J:J,K:K,L:L,M:M,N:N,Q:Q,AB:AB,AF:AF,AG:AG,AH:AH,AI:AI,AJ:AJ").EntireColumn.Hidden = False
Application.ScreenUpdating = True
Unload UserForm5
End Sub
Replace
If LCase(Sheets("Database").Cells(i, 4)) = LCase(Client) Then
with
If LCase(Sheets("Database").Cells(i, 4)) = LCase(Client) Or InStr(LCase(Client), LCase(Sheets("DataBase"), Cells(i, 4))) > 0 Then
I have a UserForm which lets the user input a count of product defects into a textbox. This is done as part of monthly reporting, so I have option buttons to select the Month (12 options). There are also option buttons for selecting Product Type. The code basically evaluates what options are selected and copies the textbox values (defect counts) into specific cells in another spreadsheet (for reporting purposes). Not all TextBoxes are required to have values entered by the User.
You can check out a screenshot of the UserForm https://imgur.com/a/6QefjCp.
As you can see from the code, I'm using a bunch of IF statements to perform the decision making - I would like to reduce the length of this code, but I don't know where to start.
I have never really used VBA prior to this, so haven't really attempted a solution. In its current state, the code works flawlessly. Just looking to reduce and clean-up.
Private Sub OKButton_Click() 'This is the button the user clicks to finalize
'the data entry
'Calling the Product type modules
Call Product1Module
Call Product2Module
Call Product3Module
End Sub
Sub Product1Module() 'All product modules will look almost exactly like this
'except the cell ranges will be different
If UserForm.Product1Button.Value = True Then 'Checking for Product1 Option button
If UserForm.JANButton.Value = True Then
'Record value to textbox if JAN is selected
Sheets("Sheet1").Range("B1107").Value = UserForm.TextBox1.Value
Sheets("Sheet1").Range("B1115").Value = UserForm.TextBox2.Value
Sheets("Sheet1").Range("B1108").Value = UserForm.TextBox3.Value
Sheets("Sheet1").Range("B1116").Value = UserForm.TextBox4.Value
Sheets("Sheet1").Range("B1109").Value = UserForm.TextBox5.Value
Sheets("Sheet1").Range("B1117").Value = UserForm.TextBox6.Value
Sheets("Sheet1").Range("B1111").Value = UserForm.TextBox7.Value
ElseIf UserForm.FEBButton.Value = True Then
Sheets("Sheet1").Range("C1107").Value = UserForm.TextBox1.Value
Sheets("Sheet1").Range("C1115").Value = UserForm.TextBox2.Value
Sheets("Sheet1").Range("C1108").Value = UserForm.TextBox3.Value
Sheets("Sheet1").Range("C1116").Value = UserForm.TextBox4.Value
Sheets("Sheet1").Range("C1109").Value = UserForm.TextBox5.Value
Sheets("Sheet1").Range("C1117").Value = UserForm.TextBox6.Value
Sheets("Sheet1").Range("C1111").Value = UserForm.TextBox7.Value
...
End If
End If
End Sub
Give each of your option buttons a Tag property value - e.g. make JANButton.Tag be "B", then make FeBButton.Tag be "C", etc.
Then you can do this:
Dim targetColumn As String
Select Case True
Case UserForm.JANButton
targetColumn = UserForm.JANButton.Tag
Case UserForm.FEBButton
targetColumn = UserForm.FEBButton.Tag
'...
End Select
With Worksheets("Sheet1") '<~ which workbook is that in? whatever is active?
.Range(targetColumn & "1107").Value = UserForm.TextBox1.Value
.Range(targetColumn & "1115").Value = UserForm.TextBox2.Value
'...
End With
It is very simple but yet I can't figure it out. Maybe because it cannot be done? Regardless here we go:
I would like to use a combobox that will, when selected, input cells with text values, functions and reference to external cells.
First line of the options would be to have the name populated.
Second line is a formula that would change from course to course.
Third line would provide a cell with a reference to another cell's content from another file. So if multiple course file are used I can have one master file that if I change the content of a cell the change will reflect on all the course file cells that are referring to it once updated.
This is in crude code form what I would like it to perform.
Private Sub ComboBox1_Change()
If Me.ComboBox1.Value = "ITCourse" Then
Worksheets("PARADE STATE").Range("I1").Value = "ITCourse"
Worksheets("Data Base").Range("C1").Value = IF(V9>70,"Prep Week",IF(V9>65,"Week 1",IF(V9>60,"Week 2",IF(V9>55,"Week 3",IF(V9>50,"Week 4",IF(V9>45,"Week 5",IF(V9>40,"Week 6",IF(V9>35,"Week 7",IF(V9>30,"Week 8",IF(V9>25,"Week 9",IF(V9>20,"Week 10",IF(V9>15,"Week 11",IF(V9>10,"Week 12",IF(V9>5,"Week 13",IF(V9>0,"Week 14")))))))))))))))
Worksheets("Week 1").Range("B2").Value = 'N:\ITcourse\00 - Data Base\[ITcourse.xlsx]Sheet'!$A$3
End If
If Me.ComboBox1.Value = "HRCourse" Then
Worksheets("PARADE STATE").Range("I1").Value = "HRCourse"
Worksheets("Data Base").Range("C1").Value = IF(V9>40,"Prep Week",IF(V9>35,"Week 1",IF(V9>30,"Week 2",IF(V9>25,"Week 3",IF(V9>20,"Week 4",IF(V9>15,"Week 5",IF(V9>10,"Week 6",IF(V9>5,"Week 7",IF(V9>5,"Week 8")))))))))
Worksheets("Week 1").Range("B2").Value = 'N:\ITcourse\00 - Data Base\[HRcourse.xlsx]Sheet'!$A$3
End If
End Sub
Thank you!
You need a function that returns the number of weeks for any given course name. This function should use a Dictionary to store the information, and the dictionary may be loaded from a dedicated worksheet.
Function WeeksPerCourse(courseName As String) As Long
Static dict As Scripting.Dictionary
If dict Is Nothing Then
' Fill the dictionary here. Note that it is better
' to load the values from a dedicated, hidden worksheet
Set dict = CreateObject("Scripting.Dictionary")
dict("ITCourse") = 14
dict("HRCourse") = 8
' Etc...
End If
WeeksPerCourse = dict(courseName)
End Function
With this function available, your procedure can be simplified like follows:
Private Sub ComboBox1_Change()
Dim course As Sting: course = Trim(ComboBox1.value)
Worksheets("PARADE STATE").Range("I1").value = course
'Dim nWeek As Long
'nWeek = WeeksPerCourse(course) - Worksheets("PARADE STATE").Range("V9").value / 5
'Worksheets("Data Base").Range("C1").value = IIf(nWeek < 1, "Prep Week", "Week " & nWeek)
Worksheets("Data Base").Range("C1").Formula = "= ""Week "" & INT((WeeksPerCourse('PARADE STATE'!I1) - 'PARADE STATE'!V9)/5)"
Worksheets("Week 1").Range("B2").Formula= "='N:\ITcourse\00 - Data Base\[" & course & ".xlsx]Sheet'!$A$3"
End Sub
I have this very simple function
Public Function HRows(xx As String)
BeginRow = 2
EndRow = 10
' HideRows
For RowCnt = BeginRow To EndRow
Cells(RowCnt,ChkCol).EntireRow.Hidden = True
Next RowCnt
End Function
When invoked from a command button it works fine, when invoked as a formula, e.g =HRows(A1), from a worksheet cell it doesn't do anything on Excel 2003, it does work in Open Office Calc 4.1
This happens on an otherwise empty spreadsheet - no protection, no comments, no shapes (which have been suggested as inhibitors in other questions)
Eventually, I want to hide/show the relevant sections of a spreadsheet, depending on what the user enters in certain key cells - I don't want to have to add command buttons to control the hiding.
I've already introduced this method here https://stackoverflow.com/a/23232311/2165759, for your purpose a code will be as follows:
Place code to one of the module of VBAProject:
Public Tasks, PermitNewTasks, ReturnValue
Function HideRowsUDF(lBegRow, lEndRow) ' Use this UDF on the sheet
If IsEmpty(Tasks) Then TasksInit
If PermitNewTasks Then Tasks.Add Application.Caller, Array(lBegRow, lEndRow)
HideRowsUDF = ReturnValue
End Function
Function HideRows(lFrom, lUpTo) ' actually all actions performed within this function, it runs without UDF limitations
Range(Rows(lFrom), Rows(lUpTo)).EntireRow.Hidden = True
HideRows = "Rows " & lFrom & "-" & lUpTo & " were hidden"
End Function
Sub TasksInit()
Set Tasks = CreateObject("Scripting.Dictionary")
ReturnValue = ""
PermitNewTasks = True
End Sub
Place code to ThisWorkbook section of Microsoft Excel Objects in VBAProject:
Private Sub Workbook_SheetCalculate(ByVal Sh As Object)
Dim Task, TempFormula
If IsEmpty(Tasks) Then TasksInit
Application.EnableEvents = False
PermitNewTasks = False
For Each Task In Tasks
TempFormula = Task.FormulaR1C1
ReturnValue = HideRows(Tasks(Task)(0), Tasks(Task)(1))
Task.FormulaR1C1 = TempFormula
Tasks.Remove Task
Next
Application.EnableEvents = True
ReturnValue = ""
PermitNewTasks = True
End Sub
I'm creating a small piece of VBA code with a specific formula, however it has a couple of if statements, one of which originates a double-line string (with vbNewLine)
The issue is that I can't see the text.
So I wanted to word wrap it, but each time I set the ActiveCell.WrapText = True, nothing happens.
I checked with a message box. I set the WrapText to True, I return the property value with the MessageBox to confirm, and it's still False.
I've been told to use ActiveCell.Rows.AutoFit as well, but AutoFit does nothing if the text isn't wrapped.
Any idea what I might be doing wrong here?
try:
Sub WrapandFit()
ActiveCell.WrapText = True
ActiveCell.EntireRow.AutoFit
End Sub
It worked for me. Make sure that your screenupdating is also set to true.
For me, the code below worked. (only set to change header row, (change range))
ActiveSheet.Range("A1:R1").Select
With Selection
.WrapText = True
End With
UDFs (procedures that use the keyword Function) only return values. They cannot change other parts of the Excel object model, like cell formatting. Only Subroutines (procedures that use the keyword Sub) can do that.
You need to have your cells formatted properly before you enter your UDF. Or you could use a worksheet change event sub to format them after the fact.
Turn off/On word wrap for whole sheet row can be done by VB code shown below:
If the first row is set true, excel inherits that property for whole sheet, unless you specifically turned it off using another code.
MyWorkSheet.Rows.WrapText = True
To turn off wrapping property of a specific row:
MyWorkSheet.Rows(8).WrapText = False
I suspect that you are trying to wrap text in merged cells. If yes, you cannot simply call:
MyWorkSheet.Rows.WrapText = True
Instead, you have to simulate the wrapping operations. I found the code from http://blog.contextures.com/archives/2012/06/07/autofit-merged-cell-row-height/ helped me last year.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim MergeWidth As Single
Dim cM As Range
Dim AutoFitRng As Range
Dim CWidth As Double
Dim NewRowHt As Double
Dim str01 As String
str01 = "OrderNote"
If Not Intersect(Target, Range(str01)) Is Nothing Then
Application.ScreenUpdating = False
On Error Resume Next
Set AutoFitRng = Range(Range(str01).MergeArea.Address)
With AutoFitRng
.MergeCells = False
CWidth = .Cells(1).ColumnWidth
MergeWidth = 0
For Each cM In AutoFitRng
cM.WrapText = True
MergeWidth = cM.ColumnWidth + MergeWidth
Next
'small adjustment to temporary width
MergeWidth = MergeWidth + AutoFitRng.Cells.Count * 0.66
.Cells(1).ColumnWidth = MergeWidth
.EntireRow.AutoFit
NewRowHt = .RowHeight
.Cells(1).ColumnWidth = CWidth
.MergeCells = True
.RowHeight = NewRowHt
End With
Application.ScreenUpdating = True
End If
End Sub
This may not be exactly what the OP had in mind but I figured I'd share my VBA Word Wrap function since I couldn't find anything on the web to do what I wanted.
This function insert CR+LF's into the string in order to wrap it, so the word wrap is maintained if the text is copied to another application, text-based or otherwise.
Function wrapText(strIn As String, Optional maxLen As Long = 110) As String
Dim p As Long: wrapText = strIn
Do
p = InStrRev(wrapText, " ", p + maxLen) - 1
wrapText = Left(wrapText,p) & vbCrLf & Right(wrapText, Len(wrapText)-p-1)
Debug.Print Mid(Replace(wrapText, vbCrLf, "||"), p - 20)
'Stop
Loop While p + maxLen < Len(wrapText)
End Function
It defaults to maximum width of 115 characters but can optionally be changed to anything. It only breaks on spaces (the last one that appears on/before position #115), and it only inserts CR + LF's (with the constant vbCrLf), but they can be adapted as required.
As an example of application, I was building complex SQL queries in Excel and wanted to copy the SQL over to the server app neat & tidy, instead of one giant line.