I know this is a duplicate, but 30 minutes of googling couldn't find an answer.
In Excel, at times extra cells or rows can become activated - usually by going too far down on a worksheet, "Activating" all 1M + rows. This has a negative impact on performance, both in memory, file size, and usability.
I previously saw a post of how you can "re-size" what Excel thinks is an activated cell, but I can't find it.
How do I resize (Using VBA) an Excel Spreadsheet's activated cells, preferably using VBA? (You can nuke and re-make the sheet... but I'd prefer to avoid that)
To be clear, I'm refering to the set of cells Excel thinks it needs to store and remember. For example, if you go to cell A1048576, put a period in the cell, hit enter, then delete it and scroll up, Excel "Remembers" that all 1048576 rows are now activated, and will continue to keep them around. You can tell this is happening partially due to the scroll bar.
A third way - I'd like to re-define where on the spreadsheet Excel takes me when I hit Ctr+End - it brings you to what it currently thinks is the last row and the last column, but it's incorrect, and I'd like to remind Excel what the correct boundaries are.
you are talking about UsedRange
to reduce it, you have to
1) clear everything from range (including formating; you can just delete rows/columns)
2) save document
In order to reset the last cell in an worksheet using VBA, you can use the following code that will clear the excess formatting:
Sub ClearExcessRowsAndColumns()
Dim ar As Range, r As Long, c As Long, tr As Long, tc As Long, x As Range
Dim wksWks As Worksheet, ur As Range, arCount As Integer, i As Integer
Dim blProtCont As Boolean, blProtScen As Boolean, blProtDO As Boolean
Dim shp As Shape
If ActiveWorkbook Is Nothing Then Exit Sub
On Error Resume Next
For Each wksWks In ActiveWindow.SelectedSheets 'Applies only to selected sheets (can be more than one)
Err.Clear
Set ur = Nothing
'Store worksheet protection settings and unprotect if protected.
blProtCont = wksWks.ProtectContents
blProtDO = wksWks.ProtectDrawingObjects
blProtScen = wksWks.ProtectScenarios
wksWks.Unprotect ""
If Err.Number = 1004 Then
Err.Clear
MsgBox "'" & wksWks.Name & _
"' is protected with a password and cannot be checked." _
, vbInformation
Else
Application.StatusBar = "Checking " & wksWks.Name & _
", Please Wait..."
r = 0
c = 0
'Determine if the sheet contains both formulas and constants
Set ur = Union(wksWks.UsedRange.SpecialCells(xlCellTypeConstants), _
wksWks.UsedRange.SpecialCells(xlCellTypeFormulas))
'If both fails, try constants only
If Err.Number = 1004 Then
Err.Clear
Set ur = wksWks.UsedRange.SpecialCells(xlCellTypeConstants)
End If
'If constants fails then set it to formulas
If Err.Number = 1004 Then
Err.Clear
Set ur = wksWks.UsedRange.SpecialCells(xlCellTypeFormulas)
End If
'If there is still an error then the worksheet is empty
If Err.Number <> 0 Then
Err.Clear
If wksWks.UsedRange.Address <> "$A$1" Then
wksWks.UsedRange.EntireRow.Hidden = False
wksWks.UsedRange.EntireColumn.Hidden = False
wksWks.UsedRange.EntireRow.RowHeight = _
IIf(wksWks.StandardHeight <> 12.75, 12.75, 13)
wksWks.UsedRange.EntireColumn.ColumnWidth = 10
wksWks.UsedRange.EntireRow.Clear
'Reset column width which can also _
cause the lastcell to be innacurate
wksWks.UsedRange.EntireColumn.ColumnWidth = _
wksWks.StandardWidth
'Reset row height which can also cause the _
lastcell to be innacurate
If wksWks.StandardHeight < 1 Then
wksWks.UsedRange.EntireRow.RowHeight = 12.75
Else
wksWks.UsedRange.EntireRow.RowHeight = _
wksWks.StandardHeight
End If
Else
Set ur = Nothing
End If
End If
'On Error GoTo 0
If Not ur Is Nothing Then
arCount = ur.Areas.Count
'determine the last column and row that contains data or formula
For Each ar In ur.Areas
i = i + 1
tr = ar.Range("A1").Row + ar.Rows.Count - 1
tc = ar.Range("A1").Column + ar.Columns.Count - 1
If tc > c Then c = tc
If tr > r Then r = tr
Next
'Determine the area covered by shapes
'so we don't remove shading behind shapes
For Each shp In wksWks.Shapes
tr = shp.BottomRightCell.Row
tc = shp.BottomRightCell.Column
If tc > c Then c = tc
If tr > r Then r = tr
Next
Application.StatusBar = "Clearing Excess Cells in " & _
wksWks.Name & ", Please Wait..."
If r < wksWks.Rows.Count And r < wksWks.Cells.SpecialCells(xlCellTypeLastCell).Row Then
Set ur = wksWks.Rows(r + 1 & ":" & wksWks.Cells.SpecialCells(xlCellTypeLastCell).Row)
ur.EntireRow.Hidden = False
ur.EntireRow.RowHeight = IIf(wksWks.StandardHeight <> 12.75, _
12.75, 13)
'Reset row height which can also cause the _
lastcell to be innacurate
If wksWks.StandardHeight < 1 Then
ur.RowHeight = 12.75
Else
ur.RowHeight = wksWks.StandardHeight
End If
Set x = ur.Dependents
If Err.Number = 0 Then
ur.Clear
Else
Err.Clear
ur.Delete
End If
End If
If c < wksWks.Columns.Count And c < wksWks.Cells.SpecialCells(xlCellTypeLastCell).Column Then
Set ur = wksWks.Range(wksWks.Cells(1, c + 1), _
wksWks.Cells(1, wksWks.Cells.SpecialCells(xlCellTypeLastCell).Column)).EntireColumn
ur.EntireColumn.Hidden = False
ur.ColumnWidth = 18
'Reset column width which can _
also cause the lastcell to be innacurate
ur.EntireColumn.ColumnWidth = _
wksWks.StandardWidth
Set x = ur.Dependents
If Err.Number = 0 Then
ur.Clear
Else
Err.Clear
ur.Delete
End If
End If
End If
End If
'Reset protection.
wksWks.Protect "", blProtDO, blProtCont, blProtScen
Err.Clear
Next
Application.StatusBar = False
MsgBox "'" & ActiveWorkbook.Name & _
"' has been cleared of excess formatting." & Chr(13) & _
"You must save the file to keep the changes.", vbInformation
End Sub
NOTE: This code was slightly adapted from the code provided in the XSFormatCleaner add-in made by AKeeler. It used to be available on CodePlex before the platform got discontinued (Archive).
Related
Hi I'm trying to create macro that will collect data from excell table. I create strings with similar names and ended by number. Please is there a way to loop over this strings ? This Code is not working, but will explain what I want to do.
Sub vzor()
Dim i As Integer
Dim input1, input2, input3, input4, input5, input6, input7, _
input8, input9, input10, input11, input12, input13, input14, _
input15, input16, input17, input18, input19, input20, input21, _
input22, input23, input24, input25, input26, input27, input28, _
input29, input30, input31, input32, input33, input34, input35, _
input36, input37, input38, input39, input40, input41, input42, _
input43, input44, input45, input46, input47, input48, input49, _
input50, input51, input52, input53, input54, input55, input56, _
input57, input58, input59, input60, input61, input62, input63, _
input64, input65, input66, input67 As String
For i = 2 To 67
If Range("B" & i).Value = "" Then
MsgBox "Please fill all required data (The cells with red fill)", vbOKOnly, "Missing data"
Range("B" & i).Select
Else
input & i = Range("B" & i).Value
End If
Next
This seems to be a perfect example for a dictionary
Option Explicit
Dim wb As Workbook, ws As Worksheet
Dim inputdict As Variant
Dim i As Long
Set inputdict = CreateObject("Scripting.Dictionary")
Set wb = ThisWorkbook 'Change if necessary
Set ws = wb.Sheets(1) 'Change if necessary
For i = 1 To 67
If ws.Cells(i, "B").Value = vbNullString Then
MsgBox "Please fill all required data (The cells with red fill)", vbOKOnly, "Missing data"
ws.Cells(i, "B").Select
Else
inputdict.Add i, ws.Cells(i, "B").Value
End If
Next i
This creates a dictionary (inputdict). The keys for this dictionary are integers defined by i ranging from 1 to 67. The values are the values in the cells as you specified in your code already.
I need some help.
I have an output from a very old system which gives us trucking manifests. Think of them like shipping slips.
Each one starts with the Title " Manifest Number: ##### " Where #### is the actual number.
The catch is that when these get printed, they need to be spaced out so it's one per page. (which is never the case in the raw output.) Page one always has the first manifest (ok) but also, the second manifest. I find myself, daily, counting the spaces and inserting rows above the second manifest title until it is on the second page. Then I count the rows between the 2nd and 3rd and insert spaces above the third title until it is on page 3 and so on. When doing 18 pages, this gets tedious.
I'm looking to see if you can help me with a macro that searches out the title "MANIFEST NUMBER" then, counts the rows before the next page break (page breaks are every 47 lines) and inserts that number of rows above. Then perform that action again for the next one.
I can provide an example file if needed.
EDIT: I have solved my problem. Please see below for the code that I used.
Sub ManifestSplit()
'
' ManifestSplit Macro
Dim gap As Long
gap = 7
Dim searchText As String
searchText = "*MANIFEST NUMBER*"
Dim originalRange As Range
Set originalRange = Range("A" & (gap + 1) & ":A1000")
Dim manifestTotal As Long
manifestTotal = Application.WorksheetFunction.CountIf(originalRange, searchText)
MsgBox ("The total number of Manifests is " & manifestTotal + 1)
Dim manifestLocation As Long
Dim numberofrowstoPage
Dim newRange As Range
Dim counter As Long
If manifestTotal = 0 Then
MsgBox ("There is only one Manifest. Macro will end.")
Else
For counter = 1 To manifestTotal
Set newRange = Range("A" & (gap + 1) & ":A1000")
manifestLocation = Application.WorksheetFunction.Match(searchText, newRange, 0) + gap
' MsgBox ("The " & counter & " manifest is on line " & manifestLocation)
numberofrowstoPage = counter * 47 - manifestLocation
' MsgBox ("The Number of Rows that will be inserted is " & numberofrowstoPage)
Rows(manifestLocation & ":" & (manifestLocation + numberofrowstoPage + 1)).Insert Shift:=xlDown
gap = 47 * counter + 2
Next counter
End If
End Sub
No need to insert an arbitrary number of rows; just set a page beak.
Sub makeReadyForPrinting()
Dim r As Long, fr As Long
With Worksheets("sheet6")
.ResetAllPageBreaks
With .Columns(1).Cells
fr = .Find(What:="Manifest Number:*", After:=.Cells(.Cells.Count), LookAt:=xlWhole, _
SearchDirection:=xlNext, SearchOrder:=xlByRows).Row
r = .FindNext(After:=.Cells(fr)).Row
Do While fr <> r
.Parent.HPageBreaks.Add Before:=.Cells(r)
r = .FindNext(After:=.Cells(r)).Row
Loop
End With
End With
End Sub
The example in your narrative included a leading space in " Manifest Number: ##### ". I've assumed that was a typo and removed it.
I have an excel workbook with multiple sheets.
I have to get counts of certain entries by using filters(i'm searching text instead of using filters here)
The "Main" sheet is where the count is updated. The strings are searched from other sheets in the workbook
The cells where the count should be updated varies.
The search criteria,keyword,sheet,range, etc is given in the sample code which I have posted.
Example from code:
In Cell, AE43, the count is updated only when the sheet "TT" meets the criteria mentioned.
So, similarly I'll have to use the same kind of code 30+ times for different cells to get the data.
So instead of typing the code for similar search, I want to know whether we can use "Dictionary" function (hashing in other languages) here, so that a cell can be updated automatically if it meets the criteria.
Sub WBR()
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
With ActiveWorkbook.Worksheets("TT") 'no of tickets processed - summary
[AE43] = wf.CountIfs(.Range("I:I"), "<>Duplicate TT", _
.Range("G:G"), "<>Not Tested", _
.Range("U:U"), "Item")
End With
With ActiveWorkbook.Worksheets("TT") 'not tested tickets - summary
[AE44] = wf.CountIfs(.Range("G:G"), "Not Tested")
End With
With ActiveWorkbook.Worksheets("TT") 'Tickets moved back- outdated OS and App Versions - summary
[AE45] = wf.CountIf(.Range("I:I"), "Outdated App Version") + wf.CountIf(.Range("I:I"), "Outdated OS")
End With
Here's a basic example which should get you started.
Sub showing how to call the code:
Sub Tester()
With ThisWorkbook.Sheets("Main")
.Range("A1") = GetCount("TT", False, "A:A", "Blue")
.Range("A2") = GetCount("TT", False, "A:A", "Blue", "C:C", "Red")
.Range("A3") = GetCount("TT", True, "A:A", "Blue", "C:C", "Red")
End With
End Sub
Generalized version of your use cases:
'If addValues is True and there are >1 set of criteria then
' sum up a bunch of COUNTIF(), else use COUNTIFS() so all
' criteria are applied at the same time
Function GetCount(shtName As String, addValues As Boolean, _
ParamArray crit()) As Long
Dim sht As Worksheet, f As String, num As Long, i As Long
Set sht = ThisWorkbook.Sheets(shtName)'<< counting things on this sheet
num = UBound(crit)
If num = 1 Or addValues Then
f = "COUNTIF(" & crit(0) & ",""" & crit(1) & """)"
End If
If num > 1 Then
If addValues Then
'already got the first pair: add the rest
For i = 2 To num Step 2
f = f & " + COUNTIF(" & crit(i) & ",""" & crit(i + 1) & """)"
Next i
Else
f = "COUNTIFS("
For i = 0 To num Step 2
f = f & crit(i) & ",""" & crit(i + 1) & """"
If i <> num - 1 Then f = f & ","
Next i
f = f & ")"
End If
End If
If f <> "" Then
Debug.Print f
GetCount = sht.Evaluate(f) '<<do not use Application.Evaluate here
Else
GetCount = -1 '<< something went wrong...
End If
End Function
Debug output:
COUNTIF(A:A,"Blue")
COUNTIFS(A:A,"Blue",C:C,"Red")
COUNTIF(A:A,"Blue") + COUNTIF(C:C,"Red")
Probably could use some error-handling and if there are other use cases you'll need to add those in.
I am getting an error I can't figure out:
After I run the macro below, two certain string values are pasted into the same two cells in ALL sheets, although I am sure that the sheets are not grouped or do not contain individual code of their own. Specifically, the items "B12" and "B25" are pasted on all pages at the same cells (A29 and A30) (See code). "B12" and "B25" have nothing to do with a cell location but are just identifiers unique to my application. They are values which are copied+pasted from one sheet into another. If it is a copy+paste error in the code, then I would expect all the items to have the same error because the "algorithm" subroutine is called for every sheet.
Sometimes, this also occurs without execution of the macro. And when I try to edit my workbook back to how it was before fields were pasted over (by clicking each cell and typing what used to be there), it still makes those changes to all sheets, even though I am sure they are not grouped or running code.
' Title: DSR AutoFill Macro
Sub autofill_DSR()
' Variable Declarations:
Dim x_count As Long
Dim n As Long
Dim item_a As String
Dim item_b As String
'Dim test_string As String
' Variable Initializations:
x_count = 0
Process_Control_NumRows = 15
Electrical_NumRows = 8
Environmental1_NumRows = 17
Env2_Regulatory_NumRows = 14
FIRE_NumRows = 15
Human_NumRows = 16
Industrial_Hygiene_NumRows = 16
Maintenance_Reliability_NumRows = 10
Pressure_Vacuum_NumRows = 16
Rotating_n_Mechanical_NumRows = 11
Facility_Siting_n_Security_NumRows = 10
Process_Safety_Documentation_NumRows = 3
Temperature_Reaction_Flow_NumRows = 18
Valve_Piping_NumRows = 22
Quality_NumRows = 10
Product_Stewardship_NumRows = 20
fourB_Items_NumRows = 28
'test_string = "NN"
' Main Data Transfer Code:
Sheets(Array("SUMMARY P.1", "SUMMARY P.2", "Process Control", _
"Electrical", "Environmental1", "Env.2 - Regulatory", "FIRE", _
"Human", "Industrial Hygiene", "Maintenance_Reliability", _
"Pressure_Vacuum", "Rotating & Mechanical", _
"Facility Siting & Security", "Process Safety Documentation", _
"Temperature-Reaction-Flow", "Valve-Piping", "Quality", _
"Product Stewardship", "4B ITEMS")).Select 'Create Array of all Sheets
'Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select ' For testing
' Process Control Sheet:
For n = 0 To (Process_Control_NumRows - 1) 'Cycle 16 times for each
'item row in process controls tab
Sheets("Process Control").Activate 'Choose specific sheet
Range("D15").Select 'Choose starting cell of "Yes" column
Call Module2.algorithm(n, x_count) 'Call on subroutine (see algorithm code)
Next n 'increment index to account for offset
' Electrical Sheet:
For n = 0 To (Electrical_NumRows - 1)
Sheets("Electrical").Activate
Range("D15").Select
Call Module2.algorithm(n, x_count)
If (x_count > 21) Then 'Abort autofill if too many items to hold
Sheets("SUMMARY P.1").Activate 'on both summary pages put together (21 count)
GoTo TooMany_Xs
End If
Next n
This continues for all the sheets...
' 4B ITEMS Sheet:
For n = 0 To (fourB_Items_NumRows - 1)
Sheets("4B ITEMS").Activate
Range("D16").Select ' NOTE: Starting cell is "D16"
Call Module2.algorithm(n, x_count)
If (x_count > 21) Then
Sheets("SUMMARY P.1").Activate
GoTo TooMany_Xs
End If
Next n
If (x_count > 5) Then 'Bring user back to last logged sheet
Sheets("SUMMARY P.2").Activate
Else
Sheets("SUMMARY P.1").Activate
End If
TooMany_Xs:
If Err.Number <> 0 Then
Msg = "you put more than 21 Items on the Summary Pages." & Chr(13) & _
"Consider editing your DSR or taking some other action."
MsgBox Msg, , "Error", Err.HelpFile, Err.HelpContext
End If
End Sub
And then this following macro is located in Module2:
Sub algorithm(n As Long, x_count As Long)
'If an "x" or "X" is marked in the "Yes" column,
'at descending cells down the column offset by the for loop index, n
If (ActiveCell.Offset(n, 0) = "x" Or ActiveCell.Offset(n, 0) = "X") Then
item_a = ActiveCell.Offset(n, -3).Value ' Store Letter value
item_a = Replace(item_a, "(", "") ' Get rid of "(", ")", and " " (space)
item_a = Replace(item_a, ")", "") ' characters that are grabbed
item_a = Replace(item_a, " ", "")
item_b = ActiveCell.Offset(n, -2).Value ' Store number value
item_b = Replace(item_b, "(", "") ' Get rid of "(", ")", and " " (space)
item_b = Replace(item_b, ")", "") ' characters that are grabbed
item_b = Replace(item_b, " ", "")
x_count = x_count + 1 ' increment the total x count
If (x_count > 5) Then ' If there are more than 5 "x" marks,
Sheets("SUMMARY P.2").Activate ' then continue to log in SUMMARY P.2
Range("A18").Select ' Choose "Item" column, first cell
ActiveCell.Offset((x_count - 6), 0).Value = (item_a & item_b)
'Insert cocatenated value of item_a and item_b
'(for example "A" & "1" = "A1")
'at the cells under the "Item" column, indexed by x_count
Else ' If there are less than 5 "x" marks,
Sheets("SUMMARY P.1").Activate ' log in SUMMARY P.1
Range("A25").Select
ActiveCell.Offset((x_count - 1), 0).Value = (item_a & item_b)
End If
End If
End Sub
By selecting all the sheets in your array, you are grouping them, and anything you write to a cell in any sheet will be written to all sheets.
This is the culprit:
Sheets(Array("SUMMARY P.1", "SUMMARY P.2", "Process Control", _
"Electrical", "Environmental1", "Env.2 - Regulatory", "FIRE", _
"Human", "Industrial Hygiene", "Maintenance_Reliability", _
"Pressure_Vacuum", "Rotating & Mechanical", _
"Facility Siting & Security", "Process Safety Documentation", _
"Temperature-Reaction-Flow", "Valve-Piping", "Quality", _
"Product Stewardship", "4B ITEMS")).Select
The fact that your issue occurs even if the code you posted hasn't been run makes me think there is something else going on after you've selected all the sheets.
Note that selecting and activating are a really bad idea. Declare variables for the objects you want to work with and interact with them that way instead of selecting them.
Here is a quick example of how you can loop through all the sheets in a workbook and modify them without selecting or activating. You can modify your code to use this pattern:
Sub LoopThroughAllSheets()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
For Each ws In wb.Sheets
ws.Range("D15").Value = ws.Name
Next ws
End Sub
Please read the following to get you started on writing cleaner, more efficient VBA code:
Beginning VBA: Select and Activate
Excel macro - Avoid using Select
Sheets(Array("SUMMARY P.1", "SUMMARY P.2", "Process Control", _
"Electrical", "Environmental1", "Env.2 - Regulatory", "FIRE", _
...
"Product Stewardship", "4B ITEMS")).Select
is grouping all these worksheets. At some point you need to Ungroup them by selecting a single worksheet:
Worksheets("Whatever").Select
You should also examine your code to check whether grouping the worksheets is actually necessary.
I am new to Excel macros..
Can anyone tell me what this macro does?
Sub People_Add_Document()
prow = ActiveCell.row
num = Cells(prow, 1).Value
wshet = ActiveSheet.Name
If (Val(num) > 0) _
And (Cells(4, 1).Value = "#") _
And (wsheet = People_wsheet) _
Then
people_select_link_to_Document process_wbook_path, prow
End If
End Sub
Sub people_select_link_to_Document(process_wbook_path, prow)
If Len(Cells(prow, DocumentFile).Value) = 0 Then
Fname = Application.GetOpenFilename("Document Files (*.doc;*.pdf),*.doc;*.pdf", 1, "Select the Document file..")
If Fname <> False Then
Cells(prow, DocumentFile).Value = Fname 'global path
End If
End If
End Sub
Get the row number of the active cell:
prow = ActiveCell.row
Get the value in column 1 of that row:
num = Cells(prow, 1).Value
Read the name of the active worksheet (there is an error here, should read wsheet rather than wshet):
wshet = ActiveSheet.Name
Test if num is greater than 0, and the cell A4 contains "#" and the active worksheet is equal to a variable or constant called People_wsheet. And if so, a subroutine called people_select_link_to_Document is called with parameters process_wbook_path and prow
If (Val(num) > 0) _
And (Cells(4, 1).Value = "#") _
And (wsheet = People_wsheet) _
Then
people_select_link_to_Document process_wbook_path, prow
End If
Now, that subroutine first of all checks to see if the DocumentFile column of the active row is empty. Actually it's a rather lame way to test emptyness, but it will probably do.
If Len(Cells(prow, DocumentFile).Value) = 0 Then
And if it is empty then we show a file dialog to obtain a file name:
Fname = Application.GetOpenFilename("Document Files (*.doc;*.pdf),*.doc;*.pdf", 1, "Select the Document file..")
If a filename has been selected (i.e. the dialog is not cancelled) then we save that file name in the DocumentFile column of the active row for future reference:
If Fname <> False Then
Cells(prow, DocumentFile).Value = Fname 'global path
End If
And that's it!