Excel vba-- macro to add a new comment and set focus to that comment - vba

I'd like to imitate the behavior of the default insert comment button with a macro. I want to store all of my macros in the Personal workbook, not the active workbook.
I'd like it to simply create a comment and then set the focus to that empty comment.
Below is what I have so far, using Terry's suggestion to make the comment .Visible and then .Shape.Select it:
Sub addNewComment()
Dim authorName As String
Dim authorNameLength As Integer
authorName = Application.UserName
authorNameLength = Len(authorName)
ActiveCell.AddComment _
authorName & ":" _
& Chr(10)
With ActiveCell.Comment
With .Shape
.AutoShapeType = msoShapeFoldedCorner
.Fill.ForeColor.RGB = RGB(215, 224, 239)
With .TextFrame
.AutoSize = True
.Characters.Font.Size = 11
.Characters.Font.Name = "Calibri"
.Characters(1, (authorNameLength + 1)).Font.Bold = True
.Characters((authorNameLength + 2), 1).Font.Bold = False
End With
End With
.Visible = True
.Shape.Select True
End With
End Sub
I'm not sure how to get the comment to go back to not being visible. Do I store the reference to the cell I just added the comment to, and then refer to that cell with the Worksheet_SelectionChange event? Or do I make that event just hide all comments on the sheet? Is it possible to use Worksheet_SelectionChange at all with the Personal workbook?
Also, my comment box does not resize as I type and add line breaks. It does resize after I exit, but actually too large by about four lines. Not sure why that is happening.
I'm sure there is a cleaner way to organize my With blocks as well.
I tried using the following to hide the comment again after selecting another cell:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Target.Comment.Visible = False
End Sub
I received the following error:
error 91: Object variable or With block variable not set

You can select the comment once you make it visible using the following:
With range("a1")
.Comment.Visible = True
.Comment.Shape.Select True
End With
But I think you'll need to have another macro to hide the comment again once you deselect, as otherwise it will stay visible. You could try doing this on the SelectionChange event of the worksheet:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Target.Comment.Visible = False
End Sub

Related

Trigger macro with change in different worksheet

Apologies any incorrect terms, this is the first time I am trying to code a macro. I currently have the following code running:
Private Sub Worksheet_Deactivate()
'Alpha Show / Hide
If Sheets("Project_selection").Range("D4") = Range("C2") Then
Sheet3.EnableCalculation = True
ElseIf Sheets("Project_selection").Range("D4") = "All" Then
Sheet3.EnableCalculation = True
Else
Sheet3.EnableCalculation = False
End If
End Sub
which has been cobbled together from other codes and google. It works, but only when I move out of the sheet, which I think is being driven by the first line.
I would actually like it to activate when the Cell D4 in the 'Project_selection' sheet (a separate sheet to the one the code is on) gets changed - does anyone know how I would do that? I have seen references to worksheet_change, but I do not understand how one defines the target/range to get the appropriate reference.
Hope that makes sense and thanks in advance!
If you were to place the following code under the sheet (Project_selection), it would fire that event every time a change has happened in Cell D4:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet: Set ws = Sheets("Project_selection")
If Target.Address = "$D$4" Then
If ws.Range("D4") = ws.Range("C2") Then
Sheet3.EnableCalculation = True
ElseIf ws.Range("D4") = "All" Then
Sheet3.EnableCalculation = True
Else
Sheet3.EnableCalculation = False
End If
End If
End Sub

Sub or function not defined: Buttons()

I had this sub in another spreadsheet where I could click a button to collapse and expand certain columns. I copied it into a new spreadsheet to use to collapse some rows but now I get the error "Sub or function not defined". It highlights Buttons
Sub HideDetails()
Range("3:8").Select
If Selection.EntireColumn.Hidden Then
Selection.EntireColumn.Hidden = False
Buttons("btnToggleDetails").Caption = "-"
Else
Selection.EntireColumn.Hidden = True
Buttons("btnToggleDetails").Caption = "+"
Range("A1").Select
Application.CutCopyMode = False
End If
Range("A1").Select
Application.CutCopyMode = False
End Sub
There are no other scripts in this workbook. This one was originally in Module1 but I tried moving it to a new module.
Edit: I changed the button name in the code but not the screenshot. Both references are to btnToggleDetails now but it still throws the same error.
It's telling you that the identifier Buttons() can't be found in the current scope. If Buttons() is something that you've declared somewhere else, you either need to make it public or you need to fully qualify the object that contains the Buttons() object, for example:
Sheet1.Buttons("btnToggleDetails").Caption = "+"
Had to add my answer as was sure I could shorten the lines of code:
If you consider that Selection.EntireColumn.Hidden returns TRUE/FALSE or 0/-1.
CHR(45) is a minus sign.
CHR(43) is a plus sign.
ABS turns -1 into 1.
So:
If TRUE (0) then 45-(0*2) = 45
If FALSE (-1) then 45-(1*2) = 43
This will swap the columns from hidden to visible and vice-versa and display the correct button caption in the immediate window:
Sub HideShowColumns()
Selection.EntireColumn.Hidden = Not (Selection.EntireColumn.Hidden)
Debug.Print Chr(45 - (Abs(CLng(Selection.EntireColumn.Hidden)) * 2))
End Sub
This should work in your procedure:
Sub HideDetails()
Dim rng As Range
Set rng = ActiveSheet.Range("3:8")
rng.EntireColumn.Hidden = Not (rng.EntireColumn.Hidden)
Buttons("btnToggleDetails").Caption = Chr(45 - (Abs(CLng(rng.EntireColumn.Hidden)) * 2))
End Sub

VBA Grey Checkboxes

I would like to grey out my checkboxes in Excel VBA. When using Checkbox.Enabled = False, the checkbox is not editable, but it is also not grey. How do I get the greyed out effect?
Using form controls with Excel 2010. Inserted via developer tab directly into excel worksheet. Not used in a VBA userform.
Thanks!
Whenever anyone says "it is impossible", it hits my stubborn streak. So may I present to you: "The Impossible".
"Visible" and enabled checkbox:
"Disabled" checkbox (you can tweak the degree of visibility by changing values in the code for both color and transparency of the cover shape):
Basic idea: you place a semi transparent shape over the checkbox, and assign a dummy macro to it. Now you can't change the value of the checkbox. The "toggle" button is there to change the state - either place the shapes, or remove them. It uses a global variable to track the current state.
Finally - note that you can't use For Each when you delete (or add) shapes as you should not modify the collection you are iterating over. I circumvented that with a simple "count shapes, then iterate backwards by numerical index".
Is it a hack? You bet! Does it do what you asked? Yes!
Dim checkBoxesVisible As Boolean
Option Explicit
Sub toggleIt()
' macro assigned to "Toggle visibility" button
checkBoxesVisible = Not checkBoxesVisible
toggleCheckboxes checkBoxesVisible
End Sub
Sub grayOut(cb)
' put a "cover" shape over a checkbox
' change the color and transparency to adjust the appearance
Dim cover As Shape
Set cover = ActiveSheet.Shapes.AddShape(msoShapeRectangle, cb.Left, cb.Top, cb.Width, cb.Height)
With cover
.Line.Visible = msoFalse
With .Fill
.Visible = msoTrue
.ForeColor.RGB = RGB(255, 255, 255)
.Transparency = 0.4
.Solid
End With
End With
cover.Name = "cover"
cover.OnAction = "doNothing"
End Sub
Sub doNothing()
' dummy macro to assign to cover shapes
End Sub
Sub unGray(cb)
' find the cover shape for the checkbox passed as the argument
' and delete it
' "correct shape" has the name "cover" and is properly aligned with top left
Dim sh As Shape
For Each sh In ActiveSheet.Shapes
If sh.Name = "cover" And sh.Left = cb.Left And sh.Top = cb.Top Then
sh.Delete
Exit For
End If
Next sh
End Sub
Sub toggleCheckboxes(onOff)
Dim s As Shape
Dim n As Integer, ii As Integer
n = ActiveSheet.Shapes.Count
' loop backwards over shapes: if you do a "For Each" you get in trouble
' when you delete things!
For ii = n To 1 Step -1
Set s = ActiveSheet.Shapes(ii)
If s.Type = msoFormControl Then
If s.FormControlType = xlCheckBox Then
If onOff Then
unGray s
Else
grayOut s
End If
End If
End If
Next ii
End Sub
A slight hack - but the following does work. I created a simple userform with two controls - a regular checkbox (CheckBox1), and a button I called "DisableButton" with the following code:
Private Sub DisableButton_Click()
CheckBox1.Enabled = Not (CheckBox1.Enabled)
If CheckBox1.Enabled Then
CheckBox1.ForeColor = RGB(0, 0, 0)
Else
CheckBox1.ForeColor = RGB(128, 128, 128)
End If
End Sub
When I clicked the button, the checkbox was grayed out and unavailable. Clicking it again "brought it back to life". I think this is the effect you were looking for. If it's not - that's what comments are for.
Here is what it looks like:
I am afraid it is impossible what you are trying to do within a worksheet. You can refer to the Floris' answer if you are using an UserForm.
For more details on the properties of (Form/worksheet) check boxes see MSDN
Maybe this is what you want.
Private Sub CheckBox1_Click()
If CheckBox1.Value = True Then
CheckBox2.Value = False
CheckBox2.Enabled = False
CheckBox2.ForeColor = rgbBlue
Else
CheckBox2.Visible = True
CheckBox2.ForeColor = rgbAntiqueWhite
CheckBox2.Enabled = True
End If
Code tels that when checkbox1 is checked, checkbox2 is disabled; unchecked and the forecollor changes. the colors can be what you want.
Did this with the checkboxes directly in the excel worksheet.
Based on Floris' idea.
The code assumes all the controls are on ActiveSheet and they are called CheckBox1 and CheckBox2, if not, change it accordingly.
You can call this when you click on CheckBox1 or you can call it from another sub, with an optional ticked status (True/False) to check or uncheck CheckBox1.
Draw an object on top of CheckBox2 and name it "mask" (you can name it anything else but then you have to change the code accordingly)
Give mask the same fill color as your background color and opacity of around 50%.
Public Sub CheckBox1_Click(Optional ticked As Variant)
Application.ScreenUpdating = False
ActiveSheet.Unprotect
If Not IsMissing(ticked) Then
If ticked = True Then ActiveSheet.Shapes("CheckBox1").OLEFormat.Object.Value = 1 Else ActiveSheet.Shapes("CheckBox1").OLEFormat.Object.Value = -4146
End If
If ActiveSheet.Shapes("CheckBox1").OLEFormat.Object.Value > 0 Then
ActiveSheet.Shapes("mask").OLEFormat.Object.ShapeRange.ZOrder msoSendToBack
Else
ActiveSheet.Shapes("mask").OLEFormat.Object.ShapeRange.ZOrder msoBringToFront
End If
ActiveSheet.Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
Application.ScreenUpdating = True
End Sub
Now each time you tick CheckBox1, mask comes to front to hide CheckBox2 and when you untick it, mask goes to back to unhide it. Since it is opaque, it gives you the greyed out effect and you don't even have to worry about enable/disable either.
Worksheet should be protected so that user can't accidentally move or edit mask, but should be unprotected for SendToBack/BringToFront to work, so the code does that. Please check the protection settings at the Application.Protect part.

Need case-sensitive formatting (Excel)

Sub test(sToken As String)
Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:=sToken
Cells.FormatConditions(Cells.FormatConditions.Count).SetFirstPriority
With Cells.FormatConditions(1).Interior
.Pattern = xlPatternLightVertical
.PatternColorIndex = 4
.ColorIndex = 10
End With
Cells.FormatConditions(1).StopIfTrue = False
End Sub
The problem with the code above is, when I use Call test("a") (for example) I get formatted cells with
"a" and "A", but I want just an "a".
Any suggestions?
PS: not skilled in VBA and English, please don't kill =)
Ok, here the full macro for better understanding problem (with my crappy coding skills =P )
Sub FormatTokens()
Call FormatReset 'Clear formatting
Call SetFormatting("d", xlPatternNone, 1, 44)
Call SetFormatting("h", xlPatternCrissCross, 46, 44)
Call SetFormatting("t", xlPatternLightVertical, 4, 10) ' Here the 1st conflict token
Call SetFormatting("p", xlPatternNone, 1, 10)
Call SetFormatting("T", xlPatternNone, 4, 10) ' And here another
Call SetFormatting("v", xlPatternGray16, 49, 24)
' Blah, blah, blah in the same style...
End Sub
Private Sub SetFormatting(sToken As String, oPat As XlPattern, iPatCol As Integer, iCol As Integer)
Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:=sToken
Cells.FormatConditions(Cells.FormatConditions.Count).SetFirstPriority
With Cells.FormatConditions(1).Interior
.Pattern = oPat
.PatternColorIndex = iPatCol
.ColorIndex = iCol
End With
Cells.FormatConditions(1).StopIfTrue = False
End Sub
Macro do the job, but not with "t" and "T" tokens
Explicitly specify Upper Case, Lower Case formatting.
Add the condition to check,
if UCase(range.value) = UCase(sToken) then
// do formatting
end if
EDIT
This works:
Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, _
Formula1:="=EXACT($B1,""a"")"
But this doesn't:
sToken = "=EXACT($A1, """"" & sToken & """"")"
Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, _
Formula1:=sToken
Use:
Formula1:= "=EXACT(A1;""" & sToken & """)"
Or:
Formula1:="=EXACT(" & Cells(1, 1).Address(False, False, xlA1) & ";""" & sToken & """)"
If you want to apply to a subrange you can simply change that part.
Well, after some deep readings of couple forums I found what I needed.
Here the solution, suitable in many different situations: set custom event handlers =)
Steps for set Worksheet events from VBA:
1. Create class module, which will be Your event handler (named clsWorksheetEventHandler in my case)
2. Code him:
Option Explicit
Public WithEvents WorksheetEvents As Worksheet 'As an object whose events should be handled
Private Sub WorksheetEvents_Change(ByVal Target As Range) 'Event to handle
'Some code You need to handle this event
End Sub
3. In Your working module add subroutines to initialize and terminate handling:
Option Explicit
Dim oWorksheetEventHandler As clsWorksheetEventHandler 'Ref for Your class
Dim colWorksheetEventHandlers As Collection 'For all referrals
Sub WorksheetEventHandlers_initialize()
'Create new Collection to store ours handlers
Set colWorksheetEventHandlers = New Collection
'Loop through worksheets
For Each Worksheet In Worksheets
'Create new instance of the event handler class
Set WorksheetEventHandler = New clsWorksheetEventHandler
'Set it to handle events in worksheet
Set WorksheetEventHandler.WorksheetEvents = Worksheet
'And add it to our collection
colWorksheetEventHandlers.Add WorksheetEventHandler
Next Worksheet
End Sub
Sub WorksheetEwentHandlers_terminate()
'Loop through our collection
For Each WorksheetEventHandler In colWorksheetEventHandlers
'Clear event handler
Set WorksheetEventHandler = Nothing
Next WorksheetEventHandler
'And finally clear memory
Set colWorksheetEventHandlers = Nothing
End Sub
4. ?????????????????????
5. PROFIT!!!!!!
I hope You enjoy =)
PS: Here are some links that have helped me greatly
How to create application-level event handlers in Excel
Controlling multiple textboxes on a userform

Excel VBA: function to turn activecell to bold

I have the following function inside my module.
Function Colorize(myValue)
ActiveCell.Select
Selection.Font.Bold = True
Colorize = myValue
End Function
The cell that will use this function should be turning bold - however, I get no error messages back and sad but true, its not turning bold. What am I missing?
Thanks
A UDF will only return a value it won't allow you to change the properties of a cell/sheet/workbook. Move your code to a Worksheet_Change event or similar to change properties.
Eg
Private Sub worksheet_change(ByVal target As Range)
target.Font.Bold = True
End Sub
I use
chartRange = xlWorkSheet.Rows[1];
chartRange.Font.Bold = true;
to turn the first-row-cells-font into bold. And it works, and I am using also Excel 2007.
You can call in VBA directly
ActiveCell.Font.Bold = True
With this code I create a timestamp in the active cell, with bold font and yellow background
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
ActiveCell.Value = Now()
ActiveCell.Font.Bold = True
ActiveCell.Interior.ColorIndex = 6
End Sub