Using VBA in Word 2010 to change column widths in a table - vba

I am just getting started with VBA, using my old copy of Word 2010. I want to resize two of the columns in a three column table and format the text that is in the columns. This code was generated by Word's macro recorder:
Selection.ConvertToTable Separator:=wdSeparateByCommas, NumColumns:=3, _
NumRows:=14, AutoFitBehavior:=wdAutoFitContent
With Selection.Tables(1)
.Style = "Table Grid"
.ApplyStyleHeadingRows = True
.ApplyStyleLastRow = False
.ApplyStyleFirstColumn = True
.ApplyStyleLastColumn = False
End With
Selection.Cells.VerticalAlignment = wdCellAlignVerticalCenter
Now I want the first two columns to be 1.3 inches wide and the third column to be 5.1 inches wide. Then I want to change the formatting of the text in the third column to increase the font size. The macro recorder doesn't seem to record when I resize columns. Any suggestions as to how to edit this code?

Controlling tables is rather complicated. Word does much of it the way it sees fit, but if you use VBA you must take control. It really isn't desirable that you should make your life even more miserable by invoking the Selection object. Try and make your object the table itself, for example, like this.
Dim Tbl As Table
Set Tbl = ActiveDocument.Tables.Add(Range:=Selection.Range, _
NumRows:=14, _
NumColumns:=3, _
DefaultTableBehavior:=wdWord8TableBehavior, _
AutoFitBehavior:=wdAutoFitFixed)
' wdWord8TableBehavior = doesn't change table size to match content
The table will be inserted following the selection. You may want to collapse your selection range before running this code. But now that you have a table object you can format it to your heart's content without selecting anything. There are dozens of properties. One of them which you will want to determine is Tbl.PreferredWidth = InchesToPoints(7.7) Word will not set this property automatically to the total width of your columns.
Your table has 3 columns which you can address as Tbl.Columns(1) to 3. You can set the width for each column, like Tbl.Columns(3).Width = InchesToPoints(5.1)
Similarly, you can address each row as Tbl.Rows(1) and up. Individual cells are addressed by Row and Column numbers, for example, Tbl.Cell(1, 3) which is the 3rd cell in the first row. Avoid merging cells because that will prevent VBA from being able to count them off.
The text in a cell is contained in its range, for example, Tbl.Cell(1, 3).Range.Text. You can both read and write this property. Bear in mind that Word keeps a paragraph-end mark at the end of each cell's range. When you write to a cell Word will add it for you, even if you thought of adding it yourself. But when you read a cell's text you need to remove the last character, for example,
Dim Rng As Range
Set Rng = Tbl.Cell(1, 3).Range
With Rng
.End = .End - 1
Fun = .Text
End With
Here Fun is the variable (As String) that contains the actual text part of the cell's contents.
You can address the paragraphs within a cell as part of the range, for example, Tbl.Cell(1, 3).Range.Paragraphs(1). I always avoid having more than 1 paragraph into any cell. However, you could address more paragraphs as (2) etc. You can apply all formatting available for paragraphs to each paragraph in each cell, and all the text in each paragraph can be subjected to all the formatting Word has available for text.

Related

Set Range to be specific lines within a cell, Microsoft VBA

I am working with a word template document that extensively uses tables to separate values. This has been great for most of my VBA code, as it makes it very simple to set values where I need to make edits. However, there is one place, in the header, where a single cell contains three lines of text. I need to set the value of JUST the first two lines in this cell.
QUESTION:
How do you set the value of JUST the first two lines of text in a given cell? Or, more broadly, can you set Range to be one line within a cell?
What I have right now, which sets Range as the whole cell and sets the value as "NEW TEXT":
ActiveDocument.PageSetup.DifferentFirstPageHeaderFooter = True
With ActiveDocument.Sections(1).Headers(wdHeaderFooterFirstPage)
.Range.Tables(1).Cell(1, 2).Range.Text = "NEW TEXT"
End With
Thank you for any expertise you can lend me.
To replace (eg) the second line:
With ActiveDocument.Sections(1).Headers(wdHeaderFooterFirstPage)
.Range.Tables(1).Cell(1, 2).Range.Paragraphs(2).Range.Text = "NEW TEXT" & vbLf
End With

(Excel VBA) - Draw from cell text in a macro

I'm trying to build a small macro that allows the user to format multiple different documents at once.
I would like for the user to be able to enter into a particular cell within the document containing the macro a particular piece of text.
I then want for this piece of text to be able to be drawn upon in the macro while affecting a different document.
For instance, a code to add another column might say
Worksheets(1).Range("A1").EntireColumn.Insert
Instead of specifying the column (A), I would like it to draw on a value in the host document. For instance, the user types "G" into the particular cell, and then clicks a button to run the macro, and the macro will dynamically know to affect column G in all excel documents it targets based off of the value in the host document.
I hope this makes sense.
Any suggestions for the sort of functions I should be looking at to make this work?
"Any suggestions on the sort of functions I should be looking at?"
Here's a few...
To get the value which is entered...
If the cell will always be in the same address, say A1:
' Define a string variable and set it equal to value in A1
Dim cellText as String
cellText = ThisWorkbook.ActiveSheet.Range("A1").Value
or instead of using Range you can also use Cells which takes a row and column number.
cellText = ThisWorkbook.ActiveSheet.Cells(1, 1).Value
If the cell changes then you may need to look into the Find function to look for a label/heading next to the input cell. Then you can use it (easily with Cells) to reference the input...
Once you have this variable, you can do what you like with it.
To put this value into cell B3 in another (open) workbook named "MyWorkbook", and a sheet named "MySheet" you can do:
Application.Workbooks("MyWorkbook").Sheets("MySheet").Range("B3").Value = cellText
To insert a column at cellText, do
Application.Workbooks("MyWorkbook").Sheets("MySheet").Range(cellText & "1").EntireColumn.Insert
Notably here, the & concatonates the strings together, so if
cellText="B"
then
cellText & "1" = "B1"
Further to your comment about moving values between sheets, see my first example here, but always refer to the same workbook. If you find yourself repeatedly typing something like
ThisWorkbook.Sheets("MySheet").<other stuff>
then you can use the With shorthand.
With ThisWorkbook.Sheets("MySheet")
' Starting anything with a dot "." now assumes the with statement first
.Range("A1").Value = .Range("A2").Value
.Range("B1").Value = .Range("B2").Value
End With
Important to note is that this code has no data validation to check the cell's value before using it! Simply trying to insert a column based on a value which could be anything is sure to make the macro crash within its first real world use!

VBA result inconsist between computers - setting .value to formula

I created and ran a VBA sub on my main computer, but it is not working as expected on my laptop (both use Excel 2013).
Set headerColumn_total = ActiveWorkbook.Worksheets("Raw Consensus").ListObjects("TblConsensusTotals").ListColumns.Add
colPosition = ActiveWorkbook.Worksheets("Raw Consensus").ListObjects("TblConsensusTotals").ListColumns.Count
colPosition = colPosition - 1
CellValue = "=SUM(TblConsensus[" & colPosition & "])"
headerColumn_total.Range.Cells(1, 1).Value = CellValue
headerColumn_total.Range.Cells(1, 1).Interior.ColorIndex = 6
Explanation of code:
As you can see, the first line adds a column to the end of a 2 row table, TblConsensusTotals. Then, the number of columns is counted (subtract 1) and assigned to colPosition. CellValue is the exact text I want as the cell value, and the interior color as well of course.
On my main computer (Expected):
The topmost cell (1st row of table) in the column receives the formula, and the 2nd row cell, (.Range.Cells(2,1)), is empty. Color set is the same.
On my laptop (Observed):
Both the 1st and 2nd row cells of the column receive the formula, as if it was given to the whole range and not just Cells(1,1). However, the Color still only sets on the 1st cell, as expected.
I'm sure there is a .001 version difference in the VBA or something, but I have no clue how to look. Is there a different way I can write this to have it work on both computers?
Thanks!
EDIT:
Forgot to mention. Interestingly, if you set .Value = anything but a formula (say, .Value = 2) instead of having CellValue there defined as a string containing a formula, it works as expected on both the main and laptop computer, setting the Value of only the single cell. A string does the same. But once you make it a formula ('=' sign) in the string, it "breaks" and sets the value to both cells on the laptop.
Go to Excel Options ([Alt]+F, T) then Proofing and click AutoCorrect options. Go to the AutoFormat As You Type tab of the AutoCorrect dialog and check/uncheck the option for Automatically as you work ► Fill formulas in tables to create calculated columns.
                                                  Culprit

Excel VBA how to select all cells in a dynamic range were the colour index is not 0

I have a SAP Report embedded in a worksheet, it is refreshed via a macro using variables defined in another worksheet. That all works fine, but i am having trouble selecting the data the report generates.
The headings of the report are in and always will fall in this range ("A17:K17"), but the results rows will vary making the total range I want to capture anywhere from ("A17:K18") to (A17:K1000").
The solutions I've already tried didn't work i think because there is almost no consistency in the result data, it's a mixture of text and numbers with empty cells all over the place, in both the rows and columns. Including the occasional completely empty row. This means the methods I have tried before reach a point where it thinks it's reached the end of the populated rows - but it hasn't.
The only factor that remains the same throughout the report is that the cells in the range I want to capture are all filled with a color as default and anything outside the range is unfilled.
To me the simplest solution would be to use VBA to select all the cells beneath and including the headers on ("A17:K17") where the color index is not 0 (blank?) regardless of their contents as I don't mind capturing empty cells. Except I don't know how to do this.
At this point I'd just like to select this range I haven't decided if I'm going to copy it into a new workbook or into an email yet, but that I can do. I've just hit a dead end selecting it.
Quite unsure exactly what it is you require but here's a solution. It's worth noting that both the ColorIndex and Color properties are not necessarily zero with no fill, so if you just change blankCell to a cell with the fill which you define to be blank you'll be good to go.
Sub test()
Set blankCell = Range("A1") ' change this to a cell that you define to be blank
blankIndex = blankCell.Interior.Color
Set cellsDesired = Range("A17:K17")
For Each cell In Range("A17:K1000")
If cell.Interior.Color <> blankIndex Then
Set cellsDesired = Application.Union(cellsDesired, Range(cell.Address))
End If
Next cell
cellsDesired.Select
End Sub

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)