VBA - How to access and retrieve the PageSetup.FitToPagesWide variable? - excel

My Page Breaks can almost be seen on the end of Column H and Row 33.
The end of Column H is 245 pixels (34.14cm)
The end of Row 33 is 20 pixel (15cm)
I need some guidance on how can I accesss these figures rather than have fixed variables. These figures will change so I need to somehow intelligently retrieve these PageSetup.FitToPagesWide to get the correct figure.
It is somewhat saying, finding the width of the page and finding the height of the page.
If a small snippet of the length of a cell is Cells(c.row, 2) = Len(c)
Then what would width of the PageSetup.FitToPagesWide be?

Related

Centering shape in Cell increases height from top as row increases

I have a macro that is taking information from another sheet and reformating. Every 5th row down there should be an image and all other information is entered correctly in the following rows. As the row number increases it is increasing distance from the top of the cell to the image. For example, if the first shape is 5 pixels from the top of the designated cell, the next will be 6 and so forth. If it was a simple trend I could add a variable to accommodate this, but all images are slightly different in size thus why I am trying to do this dynamically.
So far, as mentioned above, I have tried doing this statically by using a variable to account for the increase, but to no success. Below is the code I am currently using that does not seem to be working.
If CopyPicFromCell(shtSource.Range("A1").Offset(i - 1, 2)) Then
'copied the picture, so paste to shtDest
shtDest.Paste
With shtDest.Shapes(shtDest.Shapes.Count)
.Top = shtDest.Range("A1").Offset(x - 3, 0).Top + ((shtDest.Range("A1").Offset(x - 3, 0).Height - .Height) / 2)
.Left = shtDest.Range("A1").Offset(x - 3, 0).Left + (shtDest.Range("A1").Offset(x - 3, 0).Width - .Width) / 2
End With
End If
This always centers the image left to right but does not seem to be doing the trick vertically. Please inform if there is any other information needed. Thanks!

Formatting programatically added controls to fit in cells

I'm creating a program in Visual Basic through Microsoft Excel, where I can input all the information from an application into excel. Each row is unique and features a dropdown control that is created and placed programatically (see code below).
The problem I have come across is correctly placing the control in each cell within one of the columns.
I have read through documents on the web and seen examples on other questions but none of them seem to work for my situation. I keep having to guess-n-check the x and y values for the AddFormControl function to place the controls in a somewhat correct way.
Code:
For y = 1 To UBound(array) + 1
Set lb = Sheet1.Shapes.AddFormControl(xlDropDown, 274, 32 + (19 * y), 77, 19)
With lb
.ControlFormat.DropDownLines = 3
.ControlFormat.AddItem "Select a value", 1
.ControlFormat.AddItem "Auto", 2
.ControlFormat.AddItem "Default", 3
.ControlFormat.Value = 1
End With
Next y
I have counted the width's and height's of all the rows and columns so that I can place the controls in the correct spot but they are offset, some more than others.
The 274 x-value above is what lines up to the spot I would like it to for the x-axis on the excel sheet but is not how wide the columns prior to the spot add up to. Same goes for the y-value 77.
What would be the most efficient solution to this issue?
You can actually use Range.Top and Range.Left to place your From Controls exactly where you want them and they will be perfectly lined up with the cell in question. This is a great method for adding them in large quantities dynamically.

Zoom couple of columns to fit page with VBA

I'm having trouble fitting my columns in Excel on a sheet.
I have a sheet with columns from A to CK (can be different per project).
I don't need to print column A, but column B has to be on all pages and next to column B has to be 3 columns. So that will make column "B,C:E" on first page, next page "B,F:H", and so on... Column B is set as title, so it will be printed on every page.
My problem is to set the scale. What I'm doing:
Take pagesize and translate to points, take off margin left and margin right = my printable area
Get the width of range("B:E") = my range to fit the page
Divide my printable area by my range to fit, multiply that with 100%, and extract 1% to make sure it will fit
The outcome in my situation is 83, but is has to be 77 to fit the page. I'll have to find other numbers I think, but I don't know how and which...
My code:
If ActiveSheet.Name = "Meterkastlijst" Then
Dim lngZoom As Long
Dim lngKolB As Long
Dim lngPagB As Long
lngKolB = ActiveSheet.Range("B:E").Width
If ActiveSheet.PageSetup.PaperSize = xlPaperA4 Then
lngPagB = CLng(Application.CentimetersToPoints(21)) - CLng((ActiveSheet.PageSetup.LeftMargin + ActiveSheet.PageSetup.RightMargin))
ElseIf ActiveSheet.PageSetup.PaperSize = xlPaperA3 Then
lngPagB = CLng(Application.CentimetersToPoints(29.7)) - CLng((ActiveSheet.PageSetup.LeftMargin + ActiveSheet.PageSetup.RightMargin))
End If
If lngPagB <> 0 And lngKolB <> 0 Then
lngZoom = ((lngPagB / lngKolB) * 100) - 1
With ActiveSheet.PageSetup
.Zoom = lngZoom
End With
End If
End If
Different widths:
Column B: 45 (319 pixels) -> in Excel, set with VBA
Column C: 15 (109 pixels) -> in Excel, set with VBA
Column D: 30 (214 pixels) -> in Excel, set with VBA
Column E: 20 (144 pixels) -> in Excel, set with VBA
Column B-E: 589 points -> with VBA
Page: 21 centimeters (595 points)
Margins (left & right): 1.8 centimeters (50.4 points)
Print area: 595 - 101 (100.8) = 494 points
With numbers above it calculates 83%, but then it doesn't fit, when I set it manually to 77% it does fit, but how can I get this number with VBA? I don't understand the column widths, what I see in Excel and how I set it in VBA (45+15+30+20) is different from what VBA tells me it should be (589)...
Column Width Units
Column width is measured in Characters, Points, Centimeters / Inches, Pixels, ...
Column width in Characters
If you set a column width by manual value input or by mouse, you see the "amount of standard font number characters". Please refer to Microsoft support for details.
This value can be read and written in VBA: .Range.ColumnWidth = 10.78.
The maximum value is 255.
Column width in Points
This is an internal value not shown in GUI during manual resize of a column.
It corresponds to 72 points per inch.
In VBA it can only be read: .Range.Width
Column width in Pixels
Excel shows the column width in pixels (in parentheses) during manual resize of a column width in normal view. This value can not be read or written directly in VBA.
Column width in Centimeters or Inches
During manual resize within the page layout view Excel shows column width in centimeters (or inches) instead of pixels.
Only this value depends on print zoom level!
The measurement unit itself can be read in VBA:
Application.MeasurementUnit ' 0 = xlInches, 1 = xlCentimeters, 2 = xlMillimeters
Conversion Formulas
By this you may check or verify all values in your environment:
Dim ScreenResolution As Double
Dim ColumnWidthChars As Double
Dim ColumnWidthPoints As Double
Dim ColumnWidthPixels As Double
Dim ColumnWidthInches As Double
Dim ColumnWidthCentimeters As Double
ScreenResolution = 120 ' normal (96 dpi) or large (120 dpi)
ColumnWidthChars = ActiveSheet.Columns(1).ColumnWidth
ColumnWidthPoints = ActiveSheet.Columns(1).Width
ColumnWidthPixels = (ColumnWidthPoints / 72) * ScreenResolution
ColumnWidthInches = ColumnWidthPoints / 72 * ActiveSheet.PageSetup.Zoom / 100
ColumnWidthCentimeters = ColumnWidthInches * 2.54
Debug.Print ColumnWidthChars, ColumnWidthPoints, ColumnWidthInches, _
ColumnWidthCentimeters, ColumnWidthPixels
ScreenResolution may be retrieved with API function GetDeviceCaps(hDC, 88)
Rounding Effects
Excel stores the character-based .Range.ColumnWidth with decimals for each relevant column in the workbook file. If you set it to 100, it is stored as e. g.
<cols><col min="1" max="1" width="100.77734375" customWidth="1"/></cols>
After reopening this file, the reported .ColumnWidth is 100 without decimals.
If you set a large column width and switch between normal view and page layout view, then you may register difference of about 2% between the measures (.Range.Width and pixels suddenly change) - but all values still correspond to each other according to above formulas.
Display Scaling Dependency
All different column width values are independent of Excel's view zoom level and/or Windows 10 display scaling.
Print Zoom Dependency
Only the inch- and centimeter values change, if you change the print zoom level.
But you get more or less columns i. e. amount of points on your paper.
Excel measures .PageSetup.Leftmargin in points (with a scale of 72 points per inch). This corresponds to .Range.Width which is also measured in points.
Example: If I set both paper margins to 5.5 cm, then the resulting A4 paper width of 10 cm holds e. g. two columns with a total .Width of appr. 283 points which corresponds to 72 points/inch.
If I set the print zoom to 83 percent a .Width of appr. 340 points is maximum, and at a print zoom of 30 % it's almost 943 points.
Print Scaling
The calculation of a print zoom factor is
WorkSheet.PageSetup.Zoom = (PageWidthInPoints / AllColumnsWidthInPoints) * 100
Your calculation seems to be correct, but I would subtract at least 2 % (see rounding effects above).

Adjust right edge position of Word table with merged columns using Excel macro

I am working on a macro to format and extract information from a number of word documents that all have 1 table in it. The problem is that this table has many rows, and each each row has either 1, 2, or 3 columns in it, and I need to ensure that the right edge of the table is always the same width.
The actual behaviour that I want to replicate in VBA code is when you drag the little square thing on the ruler bar in word (that represents the right edge of the table) to adjust the right edge of the table for all rows. However, when I try and record a macro, it generates the following line of code, which does not work for tables with merged columns:
Selection.Tables(1).Columns(1).SetWidth ColumnWidth:=501.3, RulerStyle:= _
wdAdjustNone
Next I tried to explicitly target just the first row using the following code. This worked, but it only resized the width for the first row - all of the other rows were unchanged:
wdApp.Documents(myDoc).Tables(1).Cell(1, 1).SetWidth ColumnWidth:=501.3, RulerStyle:=_
wdAdjustNone
I then played around with the .width property, rather than the .setwdith property, but they seemed to behave the same. I even tried all four .setwidth RulerStyle parameters but they all behaved the same.
Finally, in frustration, I decided to loop through all of the rows in the table, and resize the rightmost column as needed so the entire row is my desired 501.3 pixels. This took some doing, as I could not find a way to tell how many columns are on a given row. I ended up relying on trapped errors to solve the issue - see below. The 4th row will error when there are not enough columns on that row, so I simply reduce the i variable and try again until there is no error:
On Error GoTo BadExit
For row = 1 To wdApp.Documents(myDoc).Tables(1).Rows.count
i = 3
If wdApp.Documents(myDoc).Tables(1).Cell(row, i).PreferredWidth <> 1501.3 Then
If i = 3 Then
wid = 501.3 - wdApp.Documents(myDoc).Tables(1).Cell(row, 2).Width - wdApp.Documents(myDoc).Tables(1).Cell(row, 1).Width
wdApp.Documents(myDoc).Tables(1).Cell(row, i).Width = wid
ElseIf i = 2 Then
wid = 501.3 - wdApp.Documents(myDoc).Tables(1).Cell(row, 1).Width
wdApp.Documents(myDoc).Tables(1).Cell(row, i).Width = wid
Else
wid = 501.3
wdApp.Documents(myDoc).Tables(1).Cell(row, i).Width = wid
End If
End If
GoTo SafeExit
BadExit:
If i = 3 Then
i = 2
Resume
ElseIf i = 2 Then
i = 1
Resume
End If
SafeExit:
Next row
This code runs through with no errors however the final width never seems to be 501.3 - it seems to vary inconsistently (which I confirmed by using msgbox with the .width property), and also, sometimes the right edges are not lined up! The rows with 1 column may stick out a bit further than the rows with 2 columns, etc. And this code is very slow.
Can anybody help me find a simple solution to replicate the behavior of manually using your mouse to move the right square on the ruler to adjust the width of every row in a word table to a specific value? Your help would be greatly appreciated.
Thanks in advance,
-Josh

Generated Data Label values from worksheet sometimes don't show

I am generating values in Excel 2010, initially putting them into an array, then copying them into a worksheet for use as datalabels for a logarithmix x-axis (actually calling Chart Labeller to do that, but this also happens when i manually apply through Excel). For the most part this works fine without problems. In certain instances, however, some, but not all, of the data labels do not visible show, even though the data in the worksheet is there, manually selecting the data labels shows an invisible label selected.
What I found out, and I think this may be a bug in Excel, when I go to the worksheet, and re-type in the value that is not showing up on the chart, it then shows up on the chart.
Here are my dim's for the array:
Dim chart_labeler_info_x()
Here is how I populate the array:
'Assuming we are going to do the x-axis
ReDim chart_labeler_info_x(1 To x_axis_interval_num, 1 To 3)
For k = 1 To x_axis_interval_num
'Column 1 is the new chart label value, column 2 is the y value of the new series , column 3 is the x value(equivalent to 111...)
'--------------------------------------------------------
chart_labeler_info_x(k, 1) = suf_ize(10 ^ (Log(x_axis.MinimumScale) / Log(10#) + (k - 1)))
chart_labeler_info_x(k, 2) = y_axis.MinimumScale
chart_labeler_info_x(k, 3) = 10 ^ (Log(x_axis.MinimumScale) / Log(10#) + (k - 1))
'--------------------------------------------------------
Next k
Here is how I initialize the range on the worksheet:
Set new_labeler_ws_x_axis = Sheets.Add
new_labeler_ws_x_axis.Name = Chart_for_series & "Eng_Labels_X_Axis"
new_labeler_ws_x_axis.Range("a1:c" & x_axis_interval_num).Value = chart_labeler_info_x
new_labeler_ws_x_axis.Range("a1:c" & x_axis_interval_num).Font.Name = "Arial"
new_labeler_ws_x_axis.Range("a1:c" & x_axis_interval_num).Font.Size = 7
I also create a new series attached to this range:
With ActiveChart.SeriesCollection.NewSeries
.XValues = Sheets(new_labeler_ws_x_axis.Name).Range("C1:C" & x_axis_interval_num)
.Values = Sheets(new_labeler_ws_x_axis.Name).Range("B1:B" & x_axis_interval_num)
.Name = "=""Labeller_x"""
.Border.Color = RGB(0, 0, 0)
.Format.line.Visible = True
End With
The data that is generated in the worksheet looks like this:
1m 100 0.001
10m 100 0.01
100m 100 0.1
1 100 1
10 100 10
100 100 100
1k 100 1000
Column 1 has the values that will be used as the new data labels. Column 2 is the y-value, Column 3 is the actual x-value. (I can attach the worksheet if that helps.)
Here is an image of what I am talking about:
You notice that the 1k data label that should be there, is not visible.
I can make the 1k data label appear one of two ways:
Extend the maximum value for the series, in this case to 10,000 (10k) in which case the 1k label shows.
Manually go to the worksheet, select the cell that has the 1000 value, re-enter the value 1000 and press return, the data label then shows up as 1k.
Some other interesting anomalies, when the maximum value is 100, the data label for 100 disappears also. As the maximum value is increased beyond 1000, there seems to be no problems the data labels all show themselves.
I have tried changing the number format, which general, to number, with two decimal places, no luck. Changing to text and back, no luck.
I think this is a bug, but haven't found in info, can any of the experts out there shine some light on this?
I found a solution, while somewhat of a hack, i think it underlines what the problem may actually be, and maybe someone can suggest a more elegant solution.
I added the last line of this code block:
chart_labeler_info_x(k, 1) = suf_ize(10 ^ (Log(x_axis.MinimumScale) / Log(10#) + (k - 1)))
chart_labeler_info_x(k, 2) = y_axis.MinimumScale
chart_labeler_info_x(k, 3) = 10# ^ (Log(x_axis.MinimumScale) / Log(10#) + (k - 1#))
'This line did the trick
If chart_labeler_info_x(k, 3) >= 1 Then chart_labeler_info_x(k, 3) = Round(chart_labeler_info_x(k, 3), 0)
As i said previously, we had found that manually updating the value in the cell, caused the labels to be visible. We tried applying the Round function to the value in the cell, and that worked, so i put it as a check in the code, for values of 1 and higher.
It appears that, even though the cell value shown is 1000, internally, it must not be. (I checked .value and .value2, they both reported 1000.) The bug i think lies in this happening SOMETIMES. If the maximum value of the series is increased, the 1k label appears, even though it it the same math being used to generate the values.
Maybe someone can explain why this is happening and offer a more elegant solution!
Thanks,
Russ

Resources