Text inside ActiveX textbox 'stretches' with move & size with cells - excel

On my worksheet there are ActiveX textboxes, which I have sized so they fit the cell they are in.
I have set object positioning to move and size with cells.
When I change the size of the cells, the textboxes resize as intended, but the text inside the box 'stretches' instead of remaining the same.

Well you obviously run into one of the numerous ActiveX bugs. I can only recommend to stay far away from ActiveX, as they are well known to cause odd issues and numerous bugs.
As solution I suggest to ask Microsoft to fix the bug and/or in the meanwhile use the following workaround after you resized columns.
There is a workaround, that could fix the odd stretch looking bug:
You just need to resize the TextBox with VBA like TextBox1.Width = TextBox1.Width and everything looks smooth again.
To fix all TextBoxes just loop through all of them and reset their width:
Option Explicit
Public Sub FixOddTextBoxesAfterColumnResize()
Dim obj As OLEObject
For Each obj In ActiveSheet.OLEObjects
If obj.progID = "Forms.TextBox.1" Then
obj.Width = obj.Width
End If
Next obj
End Sub

Related

Scrollbar height in userform frame doesn't update right away. I need to click it to update it

So I have an Excel userform that contains a frame. In this frame, there may be a X amount of dynamically added objects that fill it's content until it overfills. Of course I added a vertical scrollbar to compensate this issue, this is not the problem.
The bottom frame with caption "Données" is the one im talking about:
The problem is much trickier. So as you can see in the previous image, there are a bunch of buttons (actually toggle buttons). When you toggle a set of each of them, they will appear in the frame below. In fact, all the possible combinations of the toggled buttons will be shown.
Example 1:
Example 2:
The toggle buttons click methods are actually programmed in a class-module that calls a sub directly in the userform (named RSAjout2).
Option Explicit
'THIS PIECE OF CODE IS WRITTEN IN A CLASS-MODULE
' Is not useful for this demonstration
Public WithEvents btn As MSForms.ToggleButton
Public frm As UserForm
'''
' Here, each button is an object that calls the refreshData function to make sure it is updated each
' time one of then is pressed
Private Sub btn_Click()
'We call the subprocedure in the userform RSAjout2
RSAjout2.refreshData
End Sub
When this sub (refresh data) is called, there is a part of its code that ajusts the height of the scrollbar.
Public Sub refreshData()
'###Lots of code that I dont have any problem with###
RSAjout2.CadreDonnees.ScrollHeight = totalHeight
'Where totalHeight is just the height of all the frames stacked in the big frame
End Sub
And that works fine too. (So what is the problem then?)
The problem is that when the code actually resizes the height of the scrollbar, it doesn't actually update! I have to either click on the scrollbar itself (and move it) to make it change shape or click another toggleButton (but its state is an old one). Let me show you with pictures.
If I make a combination of tests and medias so that the number of frames overfill the container frame, it should display a scrollbar right (well its height should update)?
Wrong:
Then if I click the scrollbar, and move it just as it already existed, it suddently updates it is now visible.
Why?
This only happends with my toggleButtons. If I, for example, use my "Abondonner" button to change the height of the scrollbar, it works perfectly. Why is that so?
Private Sub Abandonner_Click()
'Code to test if the scrollbar updates normally from a normal click procedure
Me.CadreDonnees.ScrollHeight = 1000
'Spoiler* It works perfectly
End Sub
EDIT: This seemed to do the trick. However, it is clearly not the best way to solve this problem. Well at least it works...
'An aweful, but working solution
CadreDonnees.ScrollTop = 1
CadreDonnees.ScrollTop = 0
CadreDonnees.ScrollHeight = totalHeight
CadreDonnees.ScrollTop = 1
CadreDonnees.ScrollTop = 0
Try invoking the Repaint method of your Frame control =)
CadreDonnees.Repaint

How to position a chart copied from excel into powerpoint using CommandBars?

I am trying to copy some charts (embedded) in different worksheets of an excel workbook using vbscript into different slides of a powerpoint. I would like to keep the link between the excel sheet and the powerpoint while doing so and therefore I used the below piece of code that allows me to keep formatting and link (instead of a simple paste or PasteSpeical. Is there any other way?) :
For i = 1 to TotalNumWorkSheets 'I iterated with indices.
Set pptSlide = pptPres.Slides.Add(i, 11) 'There is one opening slide before this.
set ws = wb.Worksheets(i)
ws.ChartObjects(1).Chart.ChartArea.Copy
pptApp.CommandBars.ExecuteMso("PasteExcelChartSourceFormatting")
pptApp.CommandBars.ReleaseFocus
With pptSlide
.Shapes.Title.TextFrame.TextRange.Text = objCurSheet.Name
'Adding some more textboxes here. Working fine. Position checks out in PPT.
.Shapes(.Shapes.Count).Left = 20 'Doesn't work for all slides.
End With
Next
The code works and copies all the charts, creates titles, adds new text as expected, but I am not able to position the charts on individual slides because after the ExecuteMso command, I don't know how to access the reference to the chart. I read in one of the SO posts that pasting using this method looses the chart selection but you can access the last .Shapes object since pasting always adds the object to the end of the list. Is that always the case? I tried accessing and positioning my chart by accessing the last object but it only works for the first slide of the loop (i.e. the first chart pasted is shifted to Left=20). The rest all charts in other slides are centered. Can someone explain where and how to add the formatting chart options? I ran into an even weirder problem. If I increase the number of worksheets, even the first plot looses it's Left formatting. The above code is the only place where I add formatting so I don't know what is happening. I am sure I am not formatting it correctly.
Many thanks for your suggestions.
Edit: One additional thing which I tested. I am using
WScript.Sleep 500
code before the For loop ends since it gives enough time for earlier operations to finish (at least that's what I understood from many other google searches).
So after some more searching and testing, I found a solution (for my case at least).
Moved the WScript.Sleep 1000 (500 didn't work for me) statement just below CommandBars.ReleaseFocus.
It makes some sense now since it is the Chart copying, pasting and linking from excel that needs time. Especially with source formatting. After that there is only text generation which I believe is not so heavy.
Cleared all the set Object variables when not used. Especially the ones associated with the "With" keyword.
set obj = CreateObject()
With obj
'Do something here.
End With
set obj = Nothing
Not clearing them, apparently, can also prevent you from closing the applications even after you use the .Close and .Quit method. At least that's what I observed. I found PowerPoint.exe running in the task manager when nothing was opened.
After doing the above, I am able to copy with format the charts and also able to set the position of the charts. The weird problem of larger number of worksheets also disappeared. Hopefully, it might help others. If someone thinks the observations are incorrect or troublesome, please correct.

Protecting the Specific Shapes and Images in Excel VBA

I have an Excel Sheet with some macros. Also, I have some navigation shapes and images on my sheet. I want users of this sheet, cannot change this shapes and images positions, can't select them and can't move them.
Is there any way to Protect some specific objects?
Regards.
I believe this is an age old question ever since Shapes were added to MS Excel and the answer to which I myself was looking for many years already.
I just found out 3 days ago, on my own, how to lock MS Excel Freeform shapes like Choropleth Map shapes from being moved around, reformatted or worst, deleted, all WITHOUT needing to lock the WorkSheet, or eventually the WorkBook.
And I think I must share my discovery with the world because everyone wants to lock their Shapes!
Steps: (I work with Excel VBA and with msoFreeform shapes mostly but I think any shape should be working and manually added shapes through Excel UI should work too and in other Office Apps as well)
1.(yourWorkSheet or )ActiveSheet.Shapes.AddChart (through VBA but MAYBE you can add chart through Excel UI and then delete the only chart)
(no need for any other parameter because we just need the chart container, "ChartObject")
2.If you don't have some already, create a shape either through Excel UI or through VBA with AddShape method or BuildFreeform on the Chart directly or on to the worksheet.
3.Copy/Paste the created shape (if created through Excel UI or through VBA on the worksheet) on to the BLANK Chart Container. (NOT drag and drop)
4.Format the ChartContainer Rectangle window as required (try "No Fill & No Outline") through VBA or Excel UI
5.There are 3 options regarding protection of a Chart
(Embedded chart here because I don't work with Chart Sheets, may be this might work with them)
source:[https://peltiertech.com/Excel/ChartsHowTo/ChartProtection.html]
But here, only the relevant 2 will be shown:
5(a)ActiveChart.ProtectFormatting = True
That protection will block any formatting changes on the Shape and the Chart via "Chart Tools Menu" or "Drawing Tools - Format Menu" or moving or resizing with mouse or deleting BUT will show the ChartContainer window upon Selection via Mouse but non-selectable via VBA
eg. yourworksheet.ChartObjects("YourChartName").Chart.ProtectFormatting=True
5(b)ActiveChart.ProtectSelection = True
That will stop the shape or the chart from being selected altogether so this is the end of story
eg. yourworksheet.ChartObjects("YourChartName").Chart.ProtectSelection=True
The best thing about this method is that the shape can still be accessible through VBA like
eg.yourworksheet.ChartObjects("YourChartName").Chart.Shapes("YourShapeName or Index").whatever
except Shape.Select which should be obvious and there is NO need to lock the Worksheet or Workbook at all.
NB:1)The interesting finding here is that the 2 protections do not replace each other (if applied one after another) but more like stacked with each other meaning if both (if you really want) were set True first and then after setting either one False, the other restriction still remains.
2)Even if selection is protected as above, the chart can still be accessible through the Selection Pane, therefore:
Application.CommandBars("Selection and Visibility").Enabled = False
and also blocking the Worksheet Export are advisable but I think these are overkill nonetheless included for completeness' sake.
Discovered and tested on MS Excel 2010 so YMMV.
Nay Lynn's answer caused my Excel to crash. Specifically with the .ProtectSelection property being enabled (using Microsoft365 Excel). . . Excel VBA's Intellisense shows the property, so it is legitimate, but everything would be fine until the chart was selected. Playing around with this idea though, I did find a great work around.
1. Place a Chart on your sheet. Make it span across the area you want to protect
(we will expand the size of this chart later).
a. Leave Fill of Chart at Default until a later step (this helps ensure the
next steps are successful).
b. Delete all the elements present for the chart (Series label, Title, Etc.)
c. Shrink the "Plot Area" box leftover to as negligible as possible.
d. CRITICAL: Right Click the chart and choose "Select Data" - use the
option to remove all the data in the boxes of this dialog, otherwise when
you select the Rectangle added below, it will show the data references as
selections in the Worksheet behind it.
2. Insert a Rectangle into the Chart. It will have the default colors.
3. Tap `Esc` to clear the selection. If you select the shape and try to move it
around, it will take precedence over the Chart itself (this is how we'll trick
Excel later).
a. Confirm this does not allow the shape to be pulled beyond the border of the
Chart.
4. Set Fill of Chart to be "No Fill"
5. Expand the Rectangle's size to match the size of the chart.
6. Set Fill of Rectangle to be a color with 100% Transparency (NOT the same as "No
Fill" - this is critical)
a. You should be able to see all of your shapes, etc, but not touch them if you
click where the rectangle was, and trying to click and drag will also fail since the
Rectangle takes selection precedence and is also bound by the chart.
7. Set the fill of the Rectangle back to a color with 0 transparency (we need to
find the edge of the shape)
8. Select the Rectangle's border and it should then select the Chart instead (you
should see the chart Format option appear).
9. Expand the Chart's border to the ends of the Excel Sheet (ensure the top left
corner sits in the top left corner of the sheet by dragging it). You can also set
the height/width to an absurd number that users will get fed up with trying to find
if you do not wish to expand it across the entire boundaries (or if there's a hard
limitation - I did not try to expand across the entire sheet, but went to "BO400"
with no issue.
a. Expand the Rectangle to fit the Chart once again if it did not expand
automatically (in my case it did, but I cannot guarantee this behavior)
b. If you have any Buttons that DO need to be clicked, place this chart
at the bottom, place all the buttons/shapes etc. that need interaction to
the top, then bring the chart up a level until all the items needing
protection are hidden.
c. Change the Transparency back to 100%
10. After you have the Chart expanded properly, you will need to use a bit of VBA
to ensure the Chart's Formatting is protected as Nay Lynn mentions. Get the
Chart's name by selecting the border, and institute some VBA Code that gets
toggled based on your needs (you might want to include an unprotect sub
as well just in case).
Example:
Sub Protect_Sheet_With_Chart ()
dim sht as Worksheet
dim chrt as Chart
Set sht = ActiveSheet
'You can use a sheet by name for the above as well - make your code robust.
Set chrt = sht.ChartObjects("ChartNameFoundFromStep10").Chart
chrt.ProtectFormatting = True
End Sub
Sub UnProtect_Sheet_With_Chart ()
dim sht as Worksheet
dim chrt as Chart
Set sht = ActiveSheet
'You can use a sheet by name for the above as well - make your code robust.
Set chrt = sht.ChartObjects("ChartNameFoundFromStep10").Chart
chrt.ProtectFormatting = False
End Sub
11. After you protect the Chart, selecting it and deleting will not
actually delete it, NOR the rectangle, so it can't be removed!
12. Protect your code somehow and you'll be set!

Excel VBA "AxisBetweenCategories" property missing in ChartSpace

I vainly try to set the AxisBetweenCategories property to False in a Userform ChartSpace. It is a clusteredBarChart. I did the same manually in a normal chart and it worked. Recording a Marco generated the code with the AxisBetweenCategories property. Why can't I use it in the Userform ChartSpace.
me.ChartSpace1.Charts(0).Axes(0).AxisBetweenCategories = False 'doesn't work
What do I miss?
Thanks
The "Chart" inside a ChartSpace object is of type ChChart and the same properties and methods you use on a Chart (worksheet, or chartsheet) do not translate directly so unfortunately the macro recorder won't be of much help, you will have to turn to good old fashioned debugging, trial & error.
I use the Locals window to examine the objects for a hint at the object model (that's how I observe the type is ChChart, etc.)
You can then look at the available properties and Google usually can point you in the right direction, like this example. Exploring the intellisense, I see that there are really limited options that are not the same as the Chart objects on a worksheet.
After all of that, this is probably not the answer you want to hear, but it can't be done the way you want it to be done.
This seems to verify that observation and suggests that while some things are the same, others can simply not be rendered the same way.
[Chartspace Charts do] not have the refined axis crossing as Excel
does. Features between the two will be similar in certain area and
not in others
And a similar example from Microsoft:
This example sets the category axis to cross the value axis at value zero (0) in the chart workspace.
Sub SetCrossingValue()
Dim chConstants
Dim axValueAxis
Dim axCategoryAxis
Set chtContants = ChartSpace1.Constants
Set axValueAxis = ChartSpace1.Charts(0).Axes(chConstants.chAxisPositionValue)
Set axCategoryAxis = ChartSpace1.Charts(0).Axes(chConstants.chAxisPositionCategory)
axValueAxis.CrossingAxis = axCategoryAxis
axCategoryAxis.CrossesAtValue = 0
End Sub

Name property of dynamically created textboxes

First a little background information:
I am working on a project in Excel. When you open the file, a userform pops up and asks for input in multiple textboxes. It then puts the input in certain cells in the Excel document. Then another userform pops up, and asks for more input, which is inserted in cells as before.
Next, it requests numerical input in textboxes, and this data inserts the respective number of rows under each category in the Excel document.
Now for the problem.
The next thing I want to do is have a userform which dynamically creates a certain number of labels and textboxes based on the numbers inputted by the user in the previous userform. I have already figured out how to bring the variables from one userform to another; now I am having issues with the naming of the dynamically created textboxes. Well, really I know how to name them, but can't figure out how to use the textboxes' .Text attributes as input to cells in the Excel document. I have spent upwards of six hours searching the internet for an explanation of how to do this that I can apply to my project, so now I'm looking for personalized help.
Here's the options I have considered:
Arrays
Controls.Add("txtBoxName" & i) in a For...Next loop
I have actually tried the For...Next loop with Controls.Add,
and had absolutely no luck.
The problem with both of these, while Arrays may be more convenient in the long run, is that (at least as I see it) with both you have to name the textboxes and then call the names, which is a syntax I cannot find anywhere.
Can anyone help? I have no problem posting the relevant code if need be.
Also, if I have overlooked something, feel free to point me in the right direction. I may ask for help understanding it, though! ;)
Thanks,
Dudebird47
Simple example of a button added at run time with code to handle its click event:
Option Explicit
Private WithEvents btn As MSForms.CommandButton
Private Sub btn_Click()
MsgBox "You clicked me"
End Sub
Private Sub UserForm_Initialize()
Set btn = Me.Controls.Add("Forms.CommandButton.1", "some_button", True)
With btn
.Top = 5
.Left = 5
.Width = 75
.Height = 50
.Caption = "click me"
End With
End Sub

Resources