Run macro in selected cells - excel

I need to create a simple macro to clean my worksheets. Basically, if there are multiple orders on 1 shipment, I need those orders to be displayed vertically instead of horizontally example:
excel example
I created a macro that will copy/paste the 1st row into the row below it and then change the 2nd order with another copy/paste.
Pretty simple. My problem is the macro is binded to the ranges I created the macro in.
How can I make it so I can run this macro on selected ranges. Rather than manually copy and pasting every row with multiple orders, I'd rather highlight the rows with multiple orders and run the macro.
This is the code:
ActiveCell.Range("A1:M1").Select
Selection.Copy
ActiveCell.Offset(1, 0).Rows("1:1").EntireRow.Select
Selection.Insert Shift:=xlDown
ActiveCell.Offset(0, 3).Range("A1").Select
Application.CutCopyMode = False
Selection.Copy
ActiveCell.Offset(0, -1).Range("A1").Select
ActiveSheet.Paste
ActiveCell.Offset(-1, 1).Range("A1:A2").Select
Application.CutCopyMode = False
Selection.ClearContents
vba code

If I understand you correctly, maybe something like this ?
need those orders to be displayed vertically instead of horizontally
Sub test1()
Dim rg As Range
Dim cc As Range
Set rg = Range("C2")
Do
If rg.Offset(0, 1).Value <> "" Then
Set cc = Range(rg, rg.End(xlToRight))
Rows(rg.Row).Copy
Rows(rg.Row + 1 & ":" & rg.Row + cc.Columns.Count - 1).Insert Shift:=xlDown
rg.Resize(cc.Columns.Count, 1).Value = Application.Transpose(cc)
rg.Offset(0, 1).Resize(cc.Columns.Count, cc.Columns.Count - 1).ClearContents
Set rg = rg.Offset(cc.Columns.Count, 0)
Else
Set rg = rg.Offset(1, 0)
End If
Loop Until rg.Value = ""
End Sub
How can I make it so I can run this macro on selected ranges
Sub test2()
Dim rg As Range
Set rg = Application.InputBox("Select a certain row starts from column C", Type:=8)
Rows(rg.Row).Copy
Rows(rg.Row + 1 & ":" & rg.Row + rg.Columns.Count - 1).Insert Shift:=xlDown
rg.Resize(rg.Columns.Count, 1).Value = Application.Transpose(rg)
rg.Offset(0, 1).Resize(rg.Columns.Count, rg.Columns.Count - 1).ClearContents
End Sub
For sub test1
The code assumed that the delivery number will start in cell C2 which defined as variable rg then do a loop
If the the cell to the right of the rg is not empty,
then it define a range from the rg to the last column of the rg row which has value as cc variable.
Then it copy insert as many as the columns are there inside cc.
Then it transpose the cc value from column to row.
Then it delete the uneeded value.
If the the cell to the right of the rg is empty,
then it doesn't do a process, it just reset the rg to the cell below.
For sub test2
It ask a user to select a range, starts from column C to whatever last column (with value) within the same row. Then do the similar process like in test1.

Related

Excel Macro to Insert Row, with formatting, below header of named range

I would like the user to be able to click the green button on the right of each named range to insert a new data entry row below the named range header. The code I have hard codes the insert row number for the first named range. I need a way to have the code to be smart enough to know that the first row below the header of the second, third, & forth named range will changed.
Another big part is that the inserted row needs to have the same formatting (dropdowns, formulas, color, etc.) as the rows below.
First named range button code:
Sub BidSheetAddRow_Materials()
' BidSheetAddRow_Materials Macro
Rows("19:19").Select
Selection.Copy
Rows("19:19").Select
Selection.Insert Shift:=xlDown
Range("A19").Select
Application.CutCopyMode = False
Selection.ClearContents
Range("C19").Select
Selection.ClearContents
Range("K19").Select
Selection.ClearContents
End Sub
Based on the screenshot all table headers are in colA, the first input row is 3 cells below the header, and the first input cell on each table row is a merged cell.
So this works for me:
Sub AddMaterial()
AddRow "MATERIALS"
End Sub
Sub AddRate()
AddRow "RATE"
End Sub
Sub AddRow(TableHeader As String)
Dim f As Range, ws As Worksheet, c As Range
Set ws = ThisWorkbook.Worksheets("Input") 'or whatever
Set f = ws.Columns("A").Find(what:=TableHeader, lookat:=xlWhole) 'find the header
If Not f Is Nothing Then
Set c = f.Offset(3) 'step down to first input row below header
Do While c.Offset(1).MergeArea.Cells.Count > 1 'keep looping while `c` is merged
Set c = c.Offset(1)
Loop
c.Offset(1).EntireRow.Insert shift:=xlDown 'insert
c.EntireRow.Copy c.Offset(1) 'copy
c.Offset(1).EntireRow.ClearContents 'clear new row
Else
MsgBox "Table header '" & TableHeader & "' not found!"
End If
End Sub
Before/after:

Do while loop on visible cells only after autofilter?

I am stuck on a code where I apply a filter and then have to copy paste data from filtered rows to another sheet. But for some reason the code is not doing anything at all. I have applied an if condition but that is not working, it would be better if the condition was visible cells condition. Basically I want to apply filter>> then I want to copy cell in column 2 to another worksheet and perform calculation>> then copy calculated value in cell in column 7
Sub DOCFairshare()
Set ws = ThisWorkbook.Sheets("Final Orders") 'Setting worksheet in variable
ws.Activate
ws.AutoFilterMode = False 'Removing all filters
ActiveSheet.Range("$A$2:$EL$1561").AutoFilter Field:=50, Criteria1:= _
"DOC Planning Required" 'DOC Filter applied
i = 1
Do Until IsEmpty(Cells(i, 2))
If Cells(i, 50) = "DOC Planning Required" Then
Cells(i, 7).Copy
Worksheets("DOC Fairshare").Range("A3").PasteSpecial Paste:=xlPasteValues
Sheets("DOC Fairshare").Calculate
Worksheets("DOC Fairshare").Range("D11:U11").Copy
Worksheets("Final Orders").Cells(i, 7).PasteSpecial Paste:=xlPasteValues
Debug.Print Cells(i, 2)
End If
' Debug.Print Cells(i, 2)
i = i + 1
Loop
End Sub
I recommend to look at and use SpecialCells method in VBA help. I think it is very usefull.
In your case using like this example.
Range("A1:A10").SpecialCells(xlCellTypeVisible).Copy Range("C1")
It copies only visible cells to C1 from range A1-A10. I think more elegant then make loop and check if cell is visible and then copy which I used to do.
You do not say anything... I asume that my understanding should be correct. The code also assumes that on the second row there are not headers. If they exist, the line Set rngDocPl = ws.Range("AX2:AX1561")... should be adapted to Set rngDocPl = ws.Range("AX3:AX1561")....
Please, try the next code. It will stop after each iteration and shows in Immediate Window (Ctrl + G being in VBE) the value in G:G before calculations and after. Is it what you need? I cannot imagine what formulas you have in Worksheets("DOC Fairshare") and I cannot test anything:
Sub DOCFairshare()
Dim ws As Worksheet, wsDoc As Worksheet, rngDocPl As Range, cel As Range
Set ws = ThisWorkbook.Sheets("Final Orders") 'Setting worksheet in variable
Set wsDoc = Worksheets("DOC Fairshare") 'is this sheet in the same workbook?
ws.AutoFilterMode = False 'Removing all filters
ws.Range("$A$2:$EL$1561").AutoFilter field:=50, Criteria1:= _
"DOC Planning Required" 'DOC Filter applied
On Error Resume Next
Set rngDocPl = ws.Range("AX2:AX1561").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rngDocPl Is Nothing Then MsgBox "No any visible cells in AX:AX column": Exit Sub
For Each cel In rngDocPl.cells
With wsDoc
Debug.Print "before, on row " & cel.row, cel.Offset(0, -43).value 'the cell in G:G before calculations
.Range("A3").value = cel.Offset(0, -43).value 'copy the value from G to "A3"
.Calculate
cel.Offset(0, -43).Resize(1, 19).value = .Range("D11:U11").value 'copy back the calculated range
Debug.Print "after, on row " & cel.row, cel.Offset(0, -43).value: Stop 'the cell in G:G after calculations
End With
Next
End Sub

Paste copied cells into offset cell

I am creating a monthly report that copies cell values and pastes them onto specific rows depending on some simple criteria.
Pre Filter
I already have a IF function that =1 if my conditions are met. This is located in column C.
My goal is to copy 5 cells and simply paste-values them in the row that the filter = 1.
The following VBA has been myself playing with the option to filter to only show that specific row that =1, and then selection the 'Criteria 1', to paste in the first visible row below
Post Filter
Sub Macro11()
'
' Macro11 Macro
'
Dim PasteCell As Range
Set PasteCell = Range("F2").Offset(1, 0).Value 'F2 is the header for Criteria 1'
'Copy values from plan
ThisWorkbook.Worksheets("MonthlyDump").Range("N1:R1").Select
Selection.Copy
'Filter to only show the IF function = 1, plus blanks so the headings still show
ActiveSheet.Range("$C$1:$J$64").AutoFilter Field:=1, Criteria1:="1", _
Operator:=xlOr, Criteria2:="="
'Click on header, then pastes into first visible cell on the row below (the filtered row)
PasteCell.PasteSpecial xlPasteValues, xlPasteSpecialOperationNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
'Clear filters
ActiveSheet.Range("$C$1:$J$64").AutoFilter Field:=1
Range("A1").Select
End Sub
It currently fails on the first line 'Dim PasteCell As range', 424 object required.
But I am wondering if this is the best way to even go about this, ideally I would just like to look down column C until C65, and if it sees a 1, then pastes-values 3 cells to the right.
Is anyone able to come it with an elegant solution to this? My second option would be preferred as this seems the quickest way to a solution without requiring manually filtering.
Thanks
No. This doesn't look like the most suitable method. Please try the code below instead.
Sub Macro11()
' 204
Dim Arr As Variant ' temorary array
Dim Rng As Range ' temporary range
Dim Rt As Variant ' Row: target
Arr = ThisWorkbook.Worksheets("MonthlyDump").Range("N1:R1").Value
With ActiveSheet 'better to name the sheet (!)
' start lookin in row 3
Set Rng = .Range(.Cells(3, "C"), .Cells(64, "C"))
' same as above but last row is dynamic:-
' Set Rng = .Range(.Cells(3, "C"), .Cells(.Rows.Count, "C").End(xlUp))
Rt = Application.Match(1, Rng, 0)
If IsError(Rt) Then
MsgBox "No row matches the criteria.", _
vbExclamation, "Failed to post"
Else
.Cells(Rt + Rng.Row - 1, "D").Resize(1, UBound(Arr, 2)).Value = Arr
End If
End With
End Sub
This code uses the MATCH function to find the first 1 in column C. Actually, this looks like not being the optimum, either. It should be possible to look for the conditions that you use to set column C to 1 and 0, perhaps a date. If the code would look for the date, or whatever other criterium, instead of the 1 column C wouldn't be needed.

How do I print/save only the visible columns and rows to an XLSX file in VBA?

I have a macro-enabled spreadsheet that allows me to hide various columns and rows based on certain criteria I select and trigger on the sheet.
First I select the relevant columns by marking that column with a "Y", and hiding the remaining columns with a "N" with the following routine:
Sub Hidecolumn()
Dim p As Range
For Each p In Range("H1:BN1").Cells
If p.Value = "N" Then
p.EntireColumn.Hidden = True
End If
Next p
End Sub
Please note that Columns("A:G") will always be visible. Only Columns("H:BN") can be hidden based on the above. This works perfectly.
Then, I will hide the the various rows that do not have a value in the remaining visible columns for Columns("H:BN"), which is 59 possible columns. If any column within that row has a value, then that row will remain visible. If there are NO values in any of the visible columns for that row, then I hide that row. It is entirely possible that the 59 columns could reduce to 7. I do this with the following routine:
Sub HideRowsSecond()
Module2.Unhiderow
Dim srcRng As Range, ws As Worksheet
Set ws = ActiveSheet
Set srcRng = ws.Rows("5:" & ws.Cells(ws.Rows.Count, 4).End(xlUp).Row)
Dim R As Range, hideRng As Range
For Each R In srcRng
If Application.CountA(R.Columns("H:BN").SpecialCells(xlCellTypeVisible)) = 0 Then
If hideRng Is Nothing Then
Set hideRng = R.EntireRow
Else
Set hideRng = Application.Union(hideRng, R.EntireRow)
End If
End If
Next R
If Not hideRng Is Nothing Then hideRng.EntireRow.Hidden = True
MsgBox ("Complete")
End Sub
Please note that the starting row is Row("5"), and we use Column("D") as the counting column because it has a value in every cell down to the bottom of the data set. This works perfectly.
Now that I have my desired data set, I need to save this visible data set to a new XLSX file that the user can name themselves and save in the directory of their choice. The target range will begin with cell "C3" and we need to save however many visible columns there are to the right and however many visible rows there are down to the bottom of the data set.
Can someone please help me with this final step?
Here is the solution.
Sub exportToFile()
Dim rng As Range
With ActiveSheet
Set rng = Application.Intersect(.UsedRange, .Cells.Resize(.Rows.Count - 2, .Columns.Count - 2).Offset(2, 2))
End With
rng.Select
rng.SpecialCells(xlCellTypeVisible).copy
Workbooks.Add
Selection.PasteSpecial Paste:=xlPasteColumnWidths, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
ActiveSheet.Paste
Range("A" & Row & ":N" & Row).EntireRow.AutoFit
ActiveSheet.Range("A1").Select
Application.Dialogs(xlDialogSaveAs).Show ("c:\")
End Sub

Convert numbers stored as text to numbers?

How can I convert numbers stored as text to numbers?
I have tried setting:
ActiveSheet.Range("H154").NumberFormat = "General"
But it doesn't work!
The only things I've found that work are using "Text to columns" or clicking the cell to edit it and then clicking Enter.
But I would really like to find a way to turn number cells in a sheet stored as text into numbers using VBA.
A general technique is to Copy PasteSpecial, Multiply by 1
In code, something like this:
Sub ConvertToNumber()
Dim rng As Range
Dim cl As Range
Dim rConst As Range
' pick an unused cell
Set rConst = Cells(1, 4)
rConst = 1
Set rng = Cells.SpecialCells(xlCellTypeConstants)
rng.NumberFormat = "General"
rConst.Copy
rng.PasteSpecial xlPasteValues, xlPasteSpecialOperationMultiply
rConst.Clear
End Sub
Just use CDbl():
ActiveSheet.Range("H154") = CDbl(ActiveSheet.Range("H154"))
I'm not a coding expert and the "Number Stored as Text" error plagued me for a long time.
I finally found this:
Delimited Text-to-Columns in a Macro
Which got me to this:
Sub ConvertTextToNumber()
Sheets("Worksheet_Name").Select
Range("A1").Select
Selection.TextToColumns _
Destination:=Range("A:A"), _
DataType:=xlDelimited
End Sub
I use this in a macro to copy & reorder columns in a new sheet:
Sub ColumnReorder()
'**********************************************************
'Paste this macro into the Workbook of each new "Employee_List_Weekly_Update"
'Functionality:
'1. Column order in the "Employee_List_Weekly_Update" worksheet changes fairly often.
' The macro will find each column by header name,
' select that column and copy it to the new sheet.
'2. The macro also converts "Employee ID#" to a number,
' removing the "Number saved as Text" error.
'**********************************************************
'Create new sheet
Sheets.Add.Name = "Roster_Columns_Reordered"
'Repeat for each column or range
'Find Column in "Employee_List_Weekly_Update" - Copy it - Paste it in "Roster_Columns_Reordered" - Employee ID#
Dim a As Integer
Sheets("Employee_List_Weekly_Update").Select
Set rngData = Range("A1").CurrentRegion
a = Application.WorksheetFunction.Match("Employee ID#", Range("A1:BB1"), 0)
Columns(a).Select
Selection.Copy
Sheets("Roster_Columns_Reordered").Select
Range("A1").Select
ActiveSheet.Paste
'Use TextToColumns to convert "Number Stored as Text "
Selection.TextToColumns _
Destination:=Range("A:A"), _
DataType:=xlDelimited
'Find Column in "Employee_List_Weekly_Update" - Copy it - Paste it in "Roster_Columns_Reordered" - Name
Dim b As Integer
Sheets("Employee_List_Weekly_Update").Select
Set rngData = Range("A1").CurrentRegion
b = Application.WorksheetFunction.Match("Name", Range("A1:BB1"), 0)
Columns(b).Select
Selection.Copy
Sheets("Roster_Columns_Reordered").Select
Range("B1").Select
ActiveSheet.Paste
'Go to "Roster_Columns_Reordered" - Add AutoFilter - Freeze Top Row
Rows("1:1").Select
Selection.AutoFilter
With ActiveWindow
.SplitColumn = 2
.SplitRow = 1
End With
Rows("2:2").Select
ActiveWindow.FreezePanes = True
Range("A1").Select
End Sub
if you want to convert a selection (even with text in it!), you can use the code by firefiend (http://www.ozgrid.com/forum/showthread.php?t=64027&p=331498#post331498)
I think the magic is in .Value = .Value
vba
Sub macro()
Range("F:F").Select 'specify the range which suits your purpose
With Selection
.NumberFormat = "General"
.Value = .Value
End With
End Sub

Resources