Print dynamic Excel page with changing content - vba

I have an Excel workbook with two worksheets.
On the first one I have a "form" and on the second all the data in a table.
What I have done is that on the first sheet I change an ID number, then a function looks at the second sheet to find the ID number and then changes the data on the cells I've specified to those on the second page.
It is for paying company employees and there are currently 50 unique ID's.
Is there a way to print the first sheet with different content in one click if possible? Does anyone know a way to do this?

You can use something like this For each myCell in worksheets(2).range("A1:A50")...next myCell to loop through all the stored cells with different ID's and put each of the value to the cell on the sheet 1.
This is what I mean
Sub PrintAll_IDs()
For Each myCell In Worksheets(2).Range("A1:A50") 'range with stored ID's
Worksheets(1).Range("A1") = myCell.value ' "A1" is the cell with ID that you change manually now
Worksheets(1).PrintOut ' I'm not sure how to print using VBA, just showing the workflow
Next myCell
End Sub

Related

Meaning of .Cells(.Rows.Count,"A").End(xlUp).row

I was just wondering if you could help me better understand what .Cells(.Rows.Count,"A").End(xlUp).row does. I understand the portion before the .End part.
It is used to find the how many rows contain data in a worksheet that contains data in the column "A". The full usage is
lastRowIndex = ws.Cells(ws.Rows.Count, "A").End(xlUp).row
Where ws is a Worksheet object. In the questions example it was implied that the statement was inside a With block
With ws
lastRowIndex = .Cells(.Rows.Count, "A").End(xlUp).row
End With
ws.Rows.Count returns the total count of rows in the worksheet (1048576 in Excel 2010).
.Cells(.Rows.Count, "A") returns the bottom most cell in column "A" in the worksheet
Then there is the End method. The documentation is ambiguous as to what it does.
Returns a Range object that represents the cell at the end of the region that contains the source range
Particularly it doesn't define what a "region" is. My understanding is a region is a contiguous range of non-empty cells. So the expected usage is to start from a cell in a region and find the last cell in that region in that direction from the original cell. However there are multiple exceptions for when you don't use it like that:
If the range is multiple cells, it will use the region of rng.cells(1,1).
If the range isn't in a region, or the range is already at the end of the region, then it will travel along the direction until it enters a region and return the first encountered cell in that region.
If it encounters the edge of the worksheet it will return the cell on the edge of that worksheet.
So Range.End is not a trivial function.
.row returns the row index of that cell.
[A1].End(xlUp)
[A1].End(xlDown)
[A1].End(xlToLeft)
[A1].End(xlToRight)
is the VBA equivalent of being in Cell A1 and pressing Ctrl + Any arrow key. It will continue to travel in that direction until it hits the last cell of data, or if you use this command to move from a cell that is the last cell of data it will travel until it hits the next cell containing data.
If you wanted to find that last "used" cell in Column A, you could go to A65536 (for example, in an XL93-97 workbook) and press Ctrl + Up to "snap" to the last used cell. Or in VBA you would write:
Range("A65536").End(xlUp) which again can be re-written as Range("A" & Rows.Count).End(xlUp) for compatibility reasons across workbooks with different numbers of rows.
The first part:
.Cells(.Rows.Count,"A")
Sends you to the bottom row of column A, which you knew already.
The End function starts at a cell and then, depending on the direction you tell it, goes that direction until it reaches the edge of a group of cells that have text. Meaning, if you have text in cells C4:E4 and you type:
Sheet1.Cells(4,"C").End(xlToRight).Select
The program will select E4, the rightmost cell with text in it.
In your case, the code is spitting out the row of the very last cell with text in it in column A. Does that help?
.Cells(.Rows.Count,"A").End(xlUp).row
I think the first dot in the parenthesis should not be there, I mean, you should write it in this way:
.Cells(Rows.Count,"A").End(xlUp).row
Before the Cells, you can write your worksheet name, for example:
Worksheets("sheet1").Cells(Rows.Count, 2).End(xlUp).row
The worksheet name is not necessary when you operate on the same worksheet.

Keeping column formulas in a table header

Is it possible to have the formulas that I need applied on columns be saved or applied to a column header or some kind of metadata so that as and when I add new rows to my Excel table the Formulas get applied to the columns?
Scenarion:
I am creating a template Table, which will have no rows at first.
On a separate sheet (or same sheet for that matter) once the user selects the number of rows to be generated in the table, I dynamically add rows to the table using VBA.
The idea is I may not have any rows in the table at beginning OR user may have deleted rows manually.
When I programmatically add new rows, I want the Formulas applied on the cells as well. Most of the formulas I am using are either of the three types:
Structured table reference, Excel functions like SUM, AVERAGE etc and custom function names.
Updated:
Here is what I have tried:
1> tried applying the formula to the header itself.
Result: The header it self changes with #REF! error. I think the behavior is correct. So it's a no-go option.
2> Tried creating one row and apply the formula to the row. That works, but the problem is, I do not want a dummy row to begin with.
3> Using VBA code to add row to the table using
ActiveWorkbook.Worksheets("Sheet3").ListObjects("Table2").ListRows.Add AlwaysInsert:=True
inside a for loop.
The new rows retain the visual style sheets, but does not seem to retain the formulas. Just blank cells.
Could the fomrmulas be in header cell commnets?
And then with VBA add the formula for the current row:
Sub test()
Dim headerCells As Range
Set headerCells = Range("B2:E2")
OnNewRow 3, headerCells
End Sub
Sub OnNewRow(newRow As Integer, headerCells As Range)
Dim headerCell As Range, targetCell As Range, formulaFromComment As String
For Each headerCell In headerCells
formulaFromComment = GetFormulaFromComment(headerCell)
If (formulaFromComment = "") Then _
GoTo NextHeaderCell
Set targetCell = Intersect(headerCells.Worksheet.Rows(newRow), _
headerCell.EntireColumn)
AddFormula newRow, targetCell, formulaFromComment
NextHeaderCell:
Next
End Sub
Sub AddFormula( _
newRow As Integer, _
targetCell As Range, _
formula As String)
formula = Replace(formula, "{ROW}", newRow)
targetCell.formula = formula
End Sub
Function GetFormulaFromComment(headerCells As Range) As String
' TODO
GetFormulaFromComment = "=SUM($C${ROW}:$E${ROW})"
End Function
Just use tables.
If you highlight cells and choose Insert Table from the ribbon, it doesn't just give you formatting and filters. It also, if you build them the right way, stores column formulas once per column instead of once per cell. Also, the formulas are more readable!
For formulas, you can't use cell addresses if you want it to be a single column formula unless they are absolute. (E.g. $A$1, not A1.) Instead, you use [ColumnTitle] for the entire column (where "ColumnTitle" is the actual title of that column) and [#ColumnTitle] for the column value in the same row. So if "Cost" was the title of column B, "RunningTotal" was the title of column C and your formula for C6 was therefore =B6+C5, you'd instead use a formula of =[#Cost]+OFFSET([#RunningTotal],-1,0)] which is longer but much easier to read/maintain/debug, and if you change a column title then the formulas change too! No VBA required. Given this, plus being able change columns for the entire columns at once, plus being able to refer to other columns in other tables without worrying about cell addresses (e.g. MAX(Table1[Cost])), plus being able to style the tables so easily, plus the integration with Power-Query, and VBA support. (See learn.microsoft.com.) Whether VBA or otherwise, add a row to your table and the columns with a single column formulas will automatically carry over into the new row.
Not sure about Table templates or VBA but perhaps there is another option by using =ARRAYFORMULA()
For example, say you had a header row and 3 columns and wanted your last column to be the product of the first two. In cell C2 you could enter the following:
=ARRAYFORMULA(A2:A*B2:B)
This has three benefits:
Skips the first row completely
Effectively applies the formula to every row which is useful if you later decide to insert a row (your question)
Only one location to modify the formula for every single row
Although, it may not be immediately obvious where how/where the cells are being calculated. (hint: ctrl+~ may help)

Excel VBA - Selecting Items in a list and pasting into specific cell then selecting the next item

I have a list of about 400 company codes in Column S that I would like Excel to select the first company code in Cell S2, paste it into a cell (E3) where I will then run an already created macro to find the company's parent.
Then I'd like Excel to go back to my list of company codes and pick the next one in the list (Cell S3) and paste it into the same cell and run the macro to look up parent company again. I'd like it to stop once there are no longer any cells with company codes left.
I know this is probably a very simple piece of code but I've had no luck so far and can't find any questions similar.
Here's the code I was trying but with no success. I had found another piece of code on here that was to find a blank cell so I thought I'd blank out the first cell in S1 and have it then move one cell down and grab the first code, cut and paste it, then keep repeating.
Sub TEST1()
For Each Cell In Range("Entities").Cells.SpecialCells(xlCellTypeConstants)
Sheets("Input").Range("E3") = Cell.Value
Calculate
Next Cell
End Sub
Loops are very common in any kind of programming language. In VBA, there are several options to choose from. If you are sure the list holds no empty cells, I would probably use a While loop, so something like:
Option Explicit
Sub jzz()
Dim i As Long
i = 1 'first row of data
Do While Cells(i, 1) <> vbNullString
'do something with this cell
i = i + 1 'increment variable i
Loop
End Sub
This loop will run until it finds an empty cell in the first column (A).

Excel VBA code (assigned to a button) to hide/unhide rows based on cell values across multiple sheets

this is my first post here and additionally I also have completely no knowledge on VBA whatsoever... so please excuse my ignorance ;-)
I'm working on a price list which has a quantity column. The same files has multiple worksheets with multiple currencies. What I need to achieve is to create two buttons on each sheet to hide / unhide all rows where the quantity cell equals zero.
So for example you want to select certain items from the list, so you enter the quantity into appropriate cells (quantity column) and press the button to hide all other rows for which the quantity equals zero.
Now, I found the code for this somewhere already, but it only works on a first sheet and when I copy the sheet (to create another currency) with the buttons and press the button it will still apply the changes (hide / unhide rows) to the first sheet. This code is below:
Public Sub HideRows()
Dim cell As Range
For Each cell In Range("BOQ")
cell.EntireRow.Hidden = (cell.Value = 0 And cell.Value <> "")
Next cell
End Sub
and to unhide:
Public Sub UnhideRows()
Dim cell As Range
For Each cell In Range("BOQ")
If (cell.Value = 0 And cell.Value <> "") Then cell.EntireRow.Hidden = False
Next cell
End Sub
I would be extremely grateful if anyone could propose a proper script to do that separately on multiple sheets. Also to avoid the issue when after a print preview the script runs like a 100 times slower.
Thanks in advance.
Range("BOQ") refers to a range on the first sheet.
So no matter which sheet is selected, the macro will affect that range on sheet 1.
To make the code flexible to the sheet you're on, consider changing it to something like:
Activesheet.Range("A2:A10")

How do I increment cell in Excel VBA script?

I have a data in excel which I want to make a VBA script to copy it into a new worksheet but in a different way.
For example, I have this in sheet1 in A1~A3 cells.
Adam(A1)
Sam(A2)
Smith(A3)
I want to use these cells and create the following in another worksheet using refedit control.
Adam(A1)
Adam(A2)
Adam(A3)
Adam(A4)
Sam(A5)
Sam(A6)
Sam(A7)
Sam(A8)
Smith(A9)
Smith(A10)
Smith(A11)
Smith(A12)
I have refedit control in place in VBA script, but I'm not sure how to increment cell numbers to make it copy and paste into a new worksheet. I would like to use refedit control so that I can assign any cells and make it copy and repeat itself. How do I do this in VBA script?
Check out the Range Rows, Cells, and Address properties. This should help. Your question is too vague for a direct answer.
(This will get you started.)
Range.Row Property
http://msdn.microsoft.com/en-us/library/bb221550(office.12).aspx
Returns the number of the first row of the first area in the range. Read-only Long.
Example
For Each rw In Worksheets("Sheet1").Rows
If rw.Row Mod 2 = 0 Then
rw.RowHeight = 4
End If
Next rw
To increment cells in Excel VBA, you can use the Offset-property of the Range-object, e.g.
ActiveCell.Offset(1, 1).Select
will select the cell one row down and one column to the right of the active cell.
To add to Geoffrey's answer about active cell - it would also require that you activate the sheet you are looking to input your values if it is a different sheet from the one that is currently active. Additionally you would have to activate a cell to use activecell and the activecell offset property.
For example
'Activates the name of the sheet you would like to activate
Sheets("Sheet2").Activate
'Activates cell A1
Range("A1").Activate
'Activates cell one row down, one column right
ActiveCell.Offset(1,1).Select
'if current sheet is not activate you just do Sheets("Sheet2").Range("A1").Activate
The offset property of ActiveCell refers to other cells based off of the current active cell.
For example-
Offset(row,column) -
First Argument -Positive values as the first argument refer you to rows below the current active cell and Negative values refer you to rows above the current active cell
Second Argument-Positive values as the second argument refer you to columns right of the current active cell and Negative values refer you to columns left the current active cell