Simulate button press effect - excel

I have macro assigned to a rectangle shape that goes to the next sheet in my workbook.
I'm trying to add a press down and up effect to this rectangle.
When I use this code, the rectangle is only pressed down then then the next sheet is activated, and if I returned back to the previous sheet, the rectangle is released.
Wht I need is that the rectangle is pressed down and then released before going to the next sheet.
Dim MyButton As Shape
Dim oHeight, oWidth, cHeight, cWidth As Double
Dim oTop, oLeft As Long
Public Sub PressButton()
Set MyButton = ActiveSheet.Shapes(Application.Caller)
With MyButton
'Record original button properties.
oHeight = .Height
oWidth = .Width
oTop = .Top
oLeft = .Left
'Button Down (Simulate button click).
.ScaleHeight 0.9, msoFalse
.ScaleWidth 0.9, msoFalse
cHeight = .Height
cWidth = .Width
.Top = oTop + ((oHeight - cHeight) / 2)
.Left = oLeft + ((oWidth - cWidth) / 2)
End With
'Set MyButton variable to Nothing to free memory.
Set MyButton = Nothing
End Sub
Public Sub ReleaseButton()
Set MyButton = ActiveSheet.Shapes(Application.Caller)
With MyButton
'Button Up (Set back to original button properties).
.Height = oHeight
.Width = oWidth
.Top = oTop
.Left = oLeft
End With
'Set MyButton variable to Nothing to free memory.
Set MyButton = Nothing
End Sub
Public Sub NextPage()
PressButton
Application.Wait (Now + TimeSerial(0, 0, 1))
ReleaseButton
Dim ws As Worksheet
Set ws = ActiveSheet
Do While Not ws.Next.Visible = xlSheetVisible
Set ws = ws.Next
Loop
With ws.Next
.Activate
.Range("A1").Select
End With
End Sub

You're better off using a 'Command Button': shapes and rectangle objects don't really support the event-driven 'On Click' functionality you need here: calling an associated macro is pretty much all that they do.
However, you may well be stuck with that shape as your interface (support for ActiveX command buttons is very poor in 64-bit environments), so here goes...
Background: how to make a button look like a button:
Most shapes have a 'Shadow' property, and an outside shadow cast by a light source from a 45-degree angle (from the top-left corner) gives a 'raised' effect. Conversely, an inside shadow cast from the opposite angle (from a light source off the bottom-right corner) gives a 'sunken' effect.
In practice, an inside shadow for both is good enough: just change the angle.
In VBA, the 'angle' of the light source for your shape's shadow is given as X and Y offsets, and 45 degrees corresponds to 1.14142:
Dim oShape As Excel.Shape
Dim oShadow As Excel.ShadowFormat
Set oShape = ActiveSheet.Shapes(i)
Set oShadow = oShape.Shadow
' Shadow cast by light from above-right at 45 degrees for a 'raised' effect:
oShadow.OffsetX = Sqr(2)
oShadow.OffsetY = Sqr(2)
' Shadow cast by light from above-right at minus 45 degrees for a 'sunken' effect:
oShadow.OffsetX = -Sqr(2)
oShadow.OffsetY = -Sqr(2)
...And that's the code for your click 'up' and click 'down' button state.
I Strongly recommend that you use the built-in dialogs to set the shape's fill colour and the shadow's size, transparency and blur. For your reference, the settings I use for a smart 'semi-flat' light grey button are listed below - but I do not recommend that you set them in VBA code, as these formats will not be applied in the order you expect, and the button will not look like the 'clean' shape you can build using the UI dialogs:
' Light-grey button with a slightly darker 'softened' border
oShape.Fill.ForeColor.RGB = &HD8D8D8
oShape.Line.ForeColor.RGB = &HC0C0C0
oShape.Line.Weight = 2
oShape.Line.Transparency = 0.75
' Use the shape's shadow to give a 'raised button' effect:
oShadow.Style = msoShadowStyleInnerShadow
oShadow.Visible = True
oShadow.Blur = 2
oShadow.Size = 100
oShadow.Transparency = 0.5
' Shadow cast by light from above-right at 45 degrees for a 'raised' effect:
oShadow.OffsetX = Sqr(2)
oShadow.OffsetY = Sqr(2)
You can also use the 3-D effects dialog, but this works by a 'chisel' effect for most shapes (including your rectangle): I haven't found any predefined 'raised' or 'sunken' three-D styles for shapes.
Major Edit:
Guess who's looking at the job of replacing all the Active-X control buttons on all the tactical spreadsheet tools before the 64-Bit Office rollout renders them inoperable?
So your question just became very, very interesting indeed. Here's what I'm doing about that:
Generic 'Button Click' code for using Excel 'Shape' objects to call a VBA Macro instead of ActiveX controls.
This is what I'm using instead of ActiveX buttons: text labels, rectangles and images, placed into the worksheet using the 'Insert' menu on the Ribbon.
These objects are all Excel 'Shapes', they can all be associated with a named macro, and they have a common 'shadow' effect that serves as a 'raised button' 3D effect.
The example below is a function call from an image (it's a 32*32 icon for a database, with a question mark) embedded as a shape on a worksheet. I gave this ersatz control button a meaningful name, and I named the macro [Name]_Click(), because I'm replacing the existing 'Click' event procedures.
So this macro is a public subroutine on a worksheet identified with a code name, - users can 'rename' the sheet, changing the user-readable label, but they won't rename the underlying VBA class module - and it's visible as MySheetCodeName.img_TestDefaultDSN_Click() in the 'assign macro' list when you right-click the shape.
..That's why it's Public (not Private, as the automatically-created event procedure stubs for ActiveX controls will be): private subs aren't visible in the 'Assign Macro' list.
Public Sub img_TestDefaultDSN_Click()
ClickDown Me.Shapes("img_TestDefaultDB")
ShowDBStatus "EOD_Reports_DSN"
ClickUp Me.Shapes("img_TestDefaultDB")
End Sub
This calls a pair of generic 'Click Down' and 'Click Up' subroutines, in a regular code module:
Public Function ClickDown(objShape As Excel.Shape)
On Error Resume Next
'Recast the button shadow from bottom-right to top-left:
With objShape.Shadow
.Visible = msoFalse
.OffsetX = -1.2
.OffsetY = -1.2
.Visible = msoTrue
.Blur = 1
.Size = 99
.Transparency = 0.75
.Style = msoShadowStyleInnerShadow
.Obscured = msoFalse
End With
'Darken the button face slightly:
If objShape.Type = msoPicture Then
With objShape.PictureFormat
.Brightness = .Brightness - 0.01
End With
Else
With objShape.Fill
.Visible = msoTrue
.ForeColor.Brightness = .ForeColor.Brightness - 0.01
End With
End If
End Function
Public Function ClickUp(objShape As Excel.Shape)
On Error Resume Next
'Restore the button face to it's default brightness:
With objShape.Shadow
.Visible = msoFalse
.OffsetX = 1.2
.OffsetY = 1.2
.Visible = msoTrue
.Blur = 1
.Size = 99
.Transparency = 0.75
.Style = msoShadowStyleInnerShadow
.Obscured = msoFalse
End With
'Restore the button shadow to bottom-right:
If objShape.Type = msoPicture Then
With objShape.PictureFormat
.Brightness = .Brightness + 0.01
End With
Else
With objShape.Fill
.Visible = msoTrue
.ForeColor.Brightness = .ForeColor.Brightness + 0.01
End With
End If
End Function
You may well have your own preferences for the appearance of a 'control button', but this works for me.
Note that the 'Click Down' effect is never seen if 'Click Up' follows immediately: nor even if a 'Sleep' or an 'Application Wait' statement separates them - you'll only see it if there's real code with a user-detectable elapsed time or a modal dialog.

Is it possible to use Private subs. You only need to change the way you call the subroutines. To call private sub, even located in another code module, you have to use:
Application.Run "[ModuleName.]MacroName"[, arg1] [,arg2...],
Everything is in details explained here:
https://wellsr.com/vba/2015/excel/3-ways-to-call-a-private-sub-from-another-module/

Related

VBA - Label lose text after paste

So, I'm trying to create "cards" that the user can move around even when the the workbook is locked. Each card will contain info about a certain project.
The way I'm doing it:
Create a few shapes (an rectangle and a few labels and icons)
Group them
Cut the group
Paste as image
The problem is that when I paste as image, all labels loose their text, they change back to "label1".
If I run the code line by line, they don't lose the text.
I've tried already to add "time" between the cut and paste, adding some lines of code, moving the paste line to a separated sub, and even using Application.Wait(), but nothing worked.
I need to have them as an image (or one solid object - just a group doesn't work), because after the macro is finished, the worksheet is locked back again, and there is another macro to allow the user to move shapes even when the workbook is locked.
Here is a sample to show the problem.
Sub MyCode()
Set wkm = Workbooks(ThisWorkbook.Name)
Set wsm = wkm.Worksheets("TestSheet")
'Just two labels as exemple, the original code has more labels, more icons, and the rounded rectangle)
'The values for the constructors in the original code are defined by the user by a forms
Call GenerateLabel("plaseWork", "Name of the project", 14, 30)
Call GenerateLabel("whyCantYouJustWork", "Name of the user", 42, 30)
wsm.Shapes.Range(Array("plaseWork", "whyCantYouJustWork")).Group.Name = "myGroup"
Set freeSlot = wsm.Range("B10") 'Just a random cell, in the original code there is a function to define the position
Application.CutCopyMode = False
wsm.Shapes("myGroup").Cut
With wsm.Pictures.Paste
.left = freeSlot.left
.top = freeSlot.top
End With
Application.CutCopyMode = False
Set card = wsm.Pictures(wsm.Pictures.Count)
card.Name = "card" & projectName
End Sub
Sub GenerateLabel(labelDescription As String, projectName As String, top As Integer, left As Integer)
Set lbLabel = wsm.OLEObjects.Add(ClassType:="Forms.Label.1")
With lbLabel
.Name = labelDescription
.Object.BackStyle = fmBackStyleTransparent
.Width = 160
.top = top
.left = left
End With
With wsm
.OLEObjects(lbLabel.Name).Object.Caption = projectName
.Shapes(lbLabel.Name).Fill.Transparency = 1
End With
End Sub
What about using shapes with no outline or fill, in place of labels?
Sub MyCode()
Dim wsm As Worksheet, arr(0 To 1), grp As Shape
Set wkm = Workbooks(ThisWorkbook.Name)
Set wsm = wkm.Worksheets("TestSheet")
arr(0) = AddLabel(wsm, "Name of the project", 14, 30).Name
arr(1) = AddLabel(wsm, "Name of the user", 42, 30).Name
Set freeSlot = wsm.Range("B10") 'Just a random cell, in the original code there is a function to define the position
wsm.Shapes.Range(arr).Group.Cut
With wsm.Pictures.Paste
.left = freeSlot.left
.top = freeSlot.top
End With
Set card = wsm.Pictures(wsm.Pictures.Count)
card.Name = "card" & projectName
End Sub
'Add a shape to a worksheet, with the text provided.
' Return the added shape
Function AddLabel(ws As Worksheet, projectName As String, top As Integer, left As Integer)
Dim shp
Set shp = ActiveSheet.Shapes.AddShape(msoShapeRectangle, left, top, 160, 30)
With shp
.Fill.Visible = msoFalse
.Line.Visible = msoFalse
With .TextFrame2.TextRange.Characters
.Text = projectName
.Font.Fill.ForeColor.RGB = vbBlack
.Font.Size = 14
End With
End With
Set AddLabel = shp
End Function

Shrink Text in Textbox without Wrap

I have inserted textbox under insert --> shapes----> Textbox. now I want to resize textbox font if text-overflow textbox. I tried the following codes.
With Selection
If .TextFrame.HorizontalOverflow = msoTrue Then
Do
.TextFrame2.TextRange.Font.Size = .TextFrame2.TextRange.Font.Size - 1
Loop Until .TextFrame.HorizontalOverflow = msoFalse
End If
End with
ps: its Barcode font. so if it gets wrap then it's not readable by a barcode reader. so I want to shrink it.
But no success.
Thanks
The code below seems to achieve what you are looking for for standard text. Maybe you can extract the principle and use it with your barcode style.
Option Explicit
Sub AdjustTextInTextBox()
Dim myWs As Worksheet
Set myWs = ThisWorkbook.ActiveSheet
myWs.Shapes.AddShape msoTextBox, 100, 100, 250, 50
Dim myShape As Shape
Set myShape = myWs.Shapes.Item(1)
myShape.TextFrame2.AutoSize = msoAutoSizeShapeToFitText
Dim myHeight As Long
myHeight = myShape.Height
myShape.TextFrame2.TextRange.Text = "Hello world its a really really really nice day"
Do While myShape.Height > myHeight
myShape.TextFrame2.TextRange.Font.Size = myShape.TextFrame2.TextRange.Font.Size - 1
Loop
End Sub

Problem in VBA userforms - resolution totally changed

I built an excel file with several userforms in VBA, which contain buttons (form controls). Usually i work with dock-station to connect my laptop to big screen (im my office). sometimes i open the file with no docking station (only laptop).
I opened today the file, and found out that the resolution is totally changed, the user forms become so big and in all buttons the text inside are wrapped to right.
Please your support to know How can i correct the problem?
thanks
This is an issue caused by the File being open while your native screen resolution changes (and possibly only when this also changes the Aspect Ratio) - the most common reason for that being connecting or disconnecting a Laptop to/from an external screen (in this case, via your Docking Station)
There are 2 forms this issue takes: either the Button stays the same size, and but the contents (text, images, etc) are scaled up/down while anchored in the top-left - which is what has happened here - or the contents stay the same size, but the button itself gets larger/smaller until it either covers the whole sheet or is too small to click.
In my experience, the only way to fix the buttons is adjust them, and force Excel to redraw the shape instead of "remembering" what it should look like. You can do this manually, but I will try to track down some code to "reset" buttons for you. here is some code to do things for you.
(For a UserForm, you may just be able to call Me.Repaint to force a redraw without needing to bother with the resizing - but I haven't tested that as I can never get this issue to happen on-demand >_<)
UserForm Button Fix
Sub FixButtonFormat(ByRef Button As Control)
Dim Top As Double, Left As Double, Width As Double, Height As Double, FontName As String, FontSize As Double
Top = Button.Top
Left = Button.Left
Width = Button.Width
Height = Button.Height
FontName = Button.Object.Font.Name
FontSize = Button.Object.Font.Size
'Scale Button up slightly
Button.Top = Top - 1
Button.Left = Left + 1
Button.Width = Width - 2
Button.Height = Height + 2
Button.Object.Font.Size = FontSize + 1
DoEvents
UserForm1.Repaint
DoEvents
'Reset button to original size
Button.Top = Top
Button.Left = Left
Button.Width = Width
Button.Height = Height
Button.Object.Font.Name = FontName
Button.Object.Font.Size = FontSize
End Sub
Worksheet Button Fix
Sub FixButtonFormat(ByRef Button As Shape)
If Button.Type <> msoFormControl And Button.Type <> msoOLEControlObject Then Exit Sub
Dim Top As Double, Left As Double, Width As Double, Height As Double, FontName As String, FontSize As Double
Dim Screen As Boolean
Screen = Application.ScreenUpdating
Top = Button.Top
Left = Button.Left
Width = Button.Width
Height = Button.Height
If Button.Type = msoFormControl Then 'Form Control
FontName = Button.OLEFormat.Object.Font.Name
FontSize = Button.OLEFormat.Object.Font.Size
ElseIf Button.Type = msoOLEControlObject Then 'ActiveX Control
FontName = Button.DrawingObject.Object.Font.Name
FontSize = Button.DrawingObject.Object.Font.Size
End If
'Scale Button up slightly
Button.Top = Top - 1
Button.Left = Left + 1
Button.Width = Width - 2
Button.Height = Height + 2
If Button.Type = msoFormControl Then 'Form Control
Button.OLEFormat.Object.Font.Size = FontSize + 1
ElseIf Button.Type = msoOLEControlObject Then 'ActiveX Control
Button.DrawingObject.Object.Font.Size = FontSize + 1
End If
If Not Screen Then
Application.ScreenUpdating = True
DoEvents
Application.ScreenUpdating = False
Else
DoEvents
End If
'Reset button to original size
Button.Top = Top
Button.Left = Left
Button.Width = Width
Button.Height = Height
If Button.Type = msoFormControl Then 'Form Control
Button.OLEFormat.Object.Font.Name = FontName
Button.OLEFormat.Object.Font.Size = FontSize
ElseIf Button.Type = msoOLEControlObject Then 'ActiveX Control
Button.DrawingObject.Object.Font.Size = FontSize
Button.DrawingObject.Object.Font.Name = FontName
End If
End Sub
In Office365, I had the same issue (I must have detached my laptop from a docking station while a VBA form was showing on an external monitor) where everything became supersized.
I went into the code and opened up the form object to manually resize everything - when I clicked the bottom right corner of the UserForm rectangle to resize it, all the controls on the form instantly corrected themselves back down to proper size to match to my current screen resolution, and snapped back to the upper left of the form. All I had to do was reduce the UserForm to match the corrected controls.

Dynamically Adding new form control check boxes based on worksheet names (Excel VBA)

I am trying to make a system that adds checkboxes (on the click of a button) if there are any new worksheets added to the workbook. My code is below, It was able to create the checkboxes until I tried changing the location, so I am assuming that the reason it doesn't work is due to that.
Private Sub Update_Click()
Dim cb As CheckBox
Dim Exists As Boolean
'I think these location/ dimension variables are perhaps wrong (I'm not sure what values they take)
Dim TopLocation As Double
Dim LeftLocation As Double
Dim Width As Double
Dim Height As Double
For Each ws In ActiveWorkbook.Worksheets
'This loop is simply to stop it from making duplicate checkboxes
Exists=False
For Each cb In ThisWorkbook.Worksheets("Summary").CheckBoxes
If cb.name = ws.name Or ws.name = "Summary" Or ws.name = "Price List (2)" Then
Exists = True
End If
Next
If Exists = False Then
TopLocation = 0
LeftLocation = 0
Width = 0
Height = 0
'The following loop is an attempt to find the checkbox that is furthest down the page, the problem is that I am not too familiar with the location attribute so am just assuming that it increases as you move down the page
For Each cb In ThisWorkbook.Worksheets("Summary").CheckBoxes
If cb.Top > TopLocation Then
TopLocation = cb.Top
End If
If cb.Left > LeftLocation Then
LeftLocation = cb.Left
End If
If cb.Width > Width Then
Width = cb.Width
End If
If cb.Height > Height Then
Height = cb.Height
End If
Next
'The following is where I believe the problem to be, I thought that I could simply use the variables I had created to place the new one in this location
With ThisWorkbook.Worksheets("Summary").CheckBoxes.Add(LeftLocation, TopLocation + Height, Width, Height)
.name = ws.name
.Caption = ws.name
End With
End If
Next ws
End Sub
I am thinking that perhaps this is a misunderstanding of the checkboxes syntax and was hoping that someone could help me to understand where I have gone wrong. Any help is appreciated, Thanks :)
you have two typos..
in the line
With ThisWorkbook.Worksheets("Summary").CheckBoxes.Add(LocationLeft, LocationTop + Height, Width, Height)
the vars should be LeftLocation (not LocationLeft ) and TopLocation (not LocationTop)
good luck

VBA Runtime/automation error in UserForm

I'm trying to code a dynamic/conditional userform that changes the controls on the userform based on what is entered into certain input fields of the userform. The whole idea of my VBA Excel project is to take 2 data series (say, x and y) and manipulate them according to functionalities queried by the user. The specific part of my userform that causes the problem is that part where the user can choose which type of the series he/she wants to pick for either variable (x or y).
For you to understand my issue as quickly as possible, I uploaded an excel file to the following link:
http://www26.zippyshare.com/v/51719800/file.html
To keep things simple, I isolated the problem causing code, cut out all other macros and limited the userform to the controls that cause the error. To run it, click on the "Initialize" button located in the top left corner of the first tab.
When initialized, the userform contains 6 controls: 2 labels, 2 comboboxes, and 2 frames. The functionality of the uploaded test file is limited: For both series (independent series (or x) and dependent series (y)) when you click on "Replacement" a number of controls should be added to the frame corresponding to the series.
The weird thing is that this works without a problem for the first(/independent) series, but for the second(/dependent) series I get
Run-time-error '-2147417848(80010108): Automation error. The object
invoked has disconnected from its clients.
even though both instances call the very same procedure and in both instances the arguments passed to the function are valid. I tried the code on coworkers' laptops as well, but get the same error.
The code of the procedure that causes the error is given below. The procedure simply adds a control to a certain target frame based on arguments passed to the function. The exact line causing the crash is:
Set daFrame = dest.Controls.Add("Forms.Frame.1", name)
What is also weird is that the error only occurs for the case where we are creating a frame. Adding all the other controls e.g. ListBoxes, Labels, etc. (even for the second series) is not a problem.
Public Sub add_control(dest As MSForms.Frame, ht As Integer, wdt As Integer, tp As Integer, lft As Integer, typ As String, _
name As String, Optional capt As String)
Dim daFrame As MSForms.Frame
Dim daButton As MSForms.OptionButton
Dim daList As MSForms.ListBox
Dim daLabel As MSForms.Label
Dim daBox As MSForms.ComboBox
Select Case typ
Case "Frame"
Set daFrame = dest.Controls.Add("Forms.Frame.1", name)
With daFrame
.Height = ht
.Width = wdt
.Top = tp
.Left = lft
.Caption = capt
End With
Case "ListBox"
Set daList = dest.Controls.Add("Forms.Listbox.1", name)
With daList
.Height = ht
.Width = wdt
.Top = tp
.Left = lft
End With
Case "OptionButton"
Set daButton = dest.Controls.Add("Forms.OptionButton.1", name)
With daButton
.Height = ht
.Width = wdt
.Top = tp
.Left = lft
.Caption = capt
End With
Case "Label"
Set daLabel = dest.Controls.Add("Forms.Label.1", name)
With daLabel
.Height = ht
.Width = wdt
.Top = tp
.Left = lft
.Caption = capt
End With
Case "ComboBox"
Set daBox = dest.Controls.Add("Forms.ComboBox.1", name)
With daBox
.Height = ht
.Width = wdt
.Top = tp
.Left = lft
End With
End Select
End Sub
Please help me guys, I really appreciate any input. Also, if there is a way/programming style to guard against such weird behavior/erros, please let me know. Of course, I searched this forum and google for any hints, but it seems that this problem is rather code specific. In any case, I couldn't find anything helpful on the web.
Cheers
I haven't really figured out what is the real cause, but it is not the code - the real issue must be in the DepFrame. If you simply delete it and copy&rename IndFrame all works fine. But you probably figured out that already - I also wanted to tell you that you have a mistake in Dependent_change sub. You are using value of Independent instead of Dependent in the last ElseIf.
Private Sub Dependent_Change()
If Dependent.Value = "Replacement" Then
DepFrame.Caption = "Replacement"
Call rearrange_frame("Replacement2")
ElseIf Dependent.Value = "Futures" Then
DepFrame.Caption = "Futures"
Call rearrange_frame("Futures2")
here: ElseIf Independent.Value = "Spread" Then
DepFrame.Caption = "Spread"
src = 2
End If
End Sub

Resources