Say if you add a comment to a cell which includes the word "today", then we would like a VBA code to be tiggered to replace the "today" with today's date in that comment. But the problem here is that I could not find an event (or any other way) to know when a comment has been added, or to which cell (range object). Any ideas?
My current stupid solution is to add VBA code inside Worksheet_SelectionChange event (it's a shame that I could not get the old location before the selection change), and then do a For Each loop, check each comment in the sheet, then execute that replacement.
a workaround could be using a "helper" cell to store the address of the last selected cell so that once the user is done with the comment and selects another cell the event handler would check the "last" cell only
something like what follows (I used cell "A1" as "helper")
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim cmt As Comment
With Target
If .Address <> Cells(1, 1) Then
Set cmt = Range(Cells(1, 1)).Comment
If Not cmt Is Nothing Then
With cmt
.Text (Replace(.Text, "today", Date))
End With
End If
End If
End With
Cells(1, 1) = Target.Address
End Sub
The address of the previously last clicked cell can conveniently be stored in a variable. The code below should be installed in the code sheet of the tab on which the action is expected. It will take note of the ActiveCell when the worksheet is activated and tracks every click thereafter. If there was a comment in the cell last clicked it will replace the word "today" with the current computer date.
Dim PrevCell As Range
Private Sub Worksheet_Activate()
Set PrevCell = ActiveCell ' last previously selected cell
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim Cmt As Comment
On Error GoTo NoPrevCell
' an error would occur if your code crashes for some
' unrelated reason and the sheet isn't activated
' before resuming work on the same sheet.
Set Cmt = PrevCell.Comment
If Not Cmt Is Nothing Then
With Cmt
.Text Replace(.Text, "today", Format(Date, "dd-mm-yy"), _
1, -1, vbTextCompare)
End With
End If
Set PrevCell = ActiveCell
Exit Sub
NoPrevCell:
Worksheet_Activate
Resume 0
End Sub
I think it's important for this application to have the Replace function recognize both lower and upper case "Today". This is achieved by specifying case-insensitivity in the function itself. I also prefer to have the date format set right here in the function rather than relying on a Short Date format determined in the setup for Windows.
Related
I have a spreadsheet where users paste numbers, due to the length of these numbers (and the fact that we don't need to spreadsheet to carry out any computations with them) we want them to be formatted as text, otherwise they appear in scientific format i.e. 1.12345E+13 instead of 12345678912345
It is not possible to adjust/modify the data source the numbers are being copied from.
I'm using Private Sub Workbook_SheetChange do detect if a cell in the relevant range has been changed, and I then format the range to text with
ThisWorkbook.Sheets("Sheet1").Columns("B").NumberFormat = "#"
Unfortunately on Excel 2007 whether you do this manually in Excel or via a marco, the number still appears as 1.12345E+13 unless you click into the cell and press enter.
I can get round this by applying:
With rng
.Value = Evaluate("IF(ISTEXT(" & .Address & "),TRIM(" & .Address & "),REPT(" & .Address & ",1))")
End With
but when I do this I end up with an infinite loop, as the Private Sub Workbook_SheetChange detects the cell has been changed and goes round the loop again.
If I could somehow work out whether the cell has been changed manually by the user or by the macro, this would be easily fixed. The macro is in ThisWorkbook. I've tried using Application.Activesheet instead of ThisWorkbook.Sheets but it didn't make any difference.
If alternatively there's an easier/better way to fix numbers being displayed as 1.12345E+13 even after I've re-formatted the cell I'd love to know about it.
Thank you.
but when I do this I end up with an infinite loop, as the Private Sub Workbook_SheetChange detects the cell has been changed and goes round the loop again.
That's because you need to disable application events from automatically firing.
Private Sub Workbook_SheetChange(ByVal Sh As Worksheet, ByVal Target As Range)
Application.EnableEvents = False '// Stop events automatically firing
With rng
.Value = Evaluate("IF(ISTEXT(" & .Address & "),TRIM(" & .Address & "),REPT(" & .Address & ",1))")
End With
Application.EnableEvents = True '// Re-enable events for next time
End Sub
Because you've disabled the events, it can't trigger itself again when you change the value of the cell. Once the code has completed you can re-enable the events to ensure that it fires the next time it is required.
For what it's worth, don't beat yourself up about it - this is an extremely common pitfall when people start working with event procedures in excel-vba.
Here's a full example, including handling Target ranges of >1 cell:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim c As Range
Application.EnableEvents = False
On Error GoTo haveError
For Each c In Application.Intersect(Target, Sh.UsedRange).Cells
With c
If IsNumeric(.Value) Then
.NumberFormat = "#"
.Value = CStr(.Value)
End If
End With
Next c
haveError:
'Make sure to re-enable events!
Application.EnableEvents = True
End Sub
I have a spreadsheet that is going to be used in a survey.
How can I make the cells only to return "x" regardless of what the survey taker type in.
For instance, if I write a "w" in the cell, it should turn into an "x".
I have come to a point where I think there is an option when I protect the workbook or sheet. Because I can tell from another spreadsheet (which has this function) that it only works if the workbook is protected.
I tried to google it, but it seems as if I don't know the right keywords to find the answer.
Also, I have found a set of Vba code that I fiddle with, but I'm not sure this is correct. I don't want to attach the code as I don't want to confuse any response here.
Thank you for any help provided.
Put this code in the worksheet module and test it out, when you change a cell in column A (1) it will activate,
Where is the worksheet Module?
Copy and paste the code ,
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub
If Intersect(Target, Range("A1,B1,C1,A4,B4,C4")) Is Nothing Then Exit Sub
Application.EnableEvents = 0
If Target <> "" Then Target = "X"
Application.EnableEvents = 1
End Sub
This should work for you (just change the range to the one you need) :
Option Explicit
Sub worksheet_Change(ByVal Target As Range)
On Error GoTo errorbutler 'error handler - important to have this in case your macro does a booboo
Application.Calculation = xlCalculationManual 'turn off automatic calculation to speed up the macro
Application.EnableEvents = False 'turn off events in order to prevent an endless loop
Dim LR, cell As Range 'Declare your variables
Set LR = Range("A1:b3") ' Select range of cells this macro would apply to
For Each cell In Target 'Loops through the cells that user have changed
If Union(cell, LR).Address = LR.Address Then 'Checks if the changed cell is part of your range
cell.Value="x" 'changes the value of that cell to x
End if
Next cell
Errorexit: 'part of error handling procedure
Application.EnableEvents = True 'Turn autocalc back on
Application.Calculation = xlCalculationAutomatic 'turn events back on
Exit Sub
Errorbutler: 'error handling procedure
Debug.Print Err.Number & vbNewLine & Err.Description
Resume Errorexit
End Sub
Oh yes, and this code should be put into the worksheet module - the same way as Dave has shown you
I have a macro that selects things in a sheet.
Prior to running the main section of the macro I want to save the active selection, so that I can set the same selection at the end of the macro.
I've tried the solution below, but it doesn't work. I'd appreciate suggestions.
Dim rng As Range
'Beginning of macro
rng = Range(ActiveSheet.Selection) 'Object doesn't support this property or method
'Main section
'End of macro
rng.Select
Solution offered by tmoore82 is the best way to go (+1)
For completeness you can also save the Address as a string:
Dim selectionAddress as String
selectionAddress = Selection.Address 'e.g. A1 is "$A$1"
'Your macro
Range(selectionAddress).Select 'At end of macro select cell A1
Instead of rng = Range(ActiveSheet.Selection), it should be Set rng = Selection.
I've looked at the documentation for table cell objects and selection objects in VBA, and I didn't see any way to delete cell contents in Word while retaining the cell itself. It looks like doing so is easy in Excel, and next to impossible in Word.
Some cells I need to do this for will contain text, others will contain text form fields. Any ideas?
This works:
ActiveDocument.Tables(1).Cell(1, 2).Select
Selection.Delete
This deletes the cell contents but leaves the empty cell behind.
I understand your dismay, because oddly, the above does not do the same as
ActiveDocument.Tables(1).Cell(1, 2).Delete
which deletes the entire cell!
The former is the equivalent of selecting a cell and pressing the Delete key (which clears the contents but leaves the cell in place). The latter is the equivalent of right-clicking a cell and choosing "Delete cells..." (which deletes the cell).
I cobbled this together from various parts of the interwebs... including Fumei from VBA Express. It's working well. Select any cells in your table and run the macro deleteTableCells
Sub deleteTableCells()
Dim selectedRange As Range
On Error GoTo Errorhandler
Set selectedRange = SelectionInfo
selectedRange.Delete
Errorhandler:
Exit Sub
End Sub
Function SelectionInfo() As Range
'
Dim iSelectionRowEnd As Integer
Dim iSelectionRowStart As Integer
Dim iSelectionColumnEnd As Integer
Dim iSelectionColumnStart As Integer
Dim lngStart As Long
Dim lngEnd As Long
' Check if Selection IS in a table
' if not, exit Sub after message
If Selection.Information(wdWithInTable) = False Then
Err.Raise (2022)
Else
lngStart = Selection.Range.Start
lngEnd = Selection.Range.End
' get the numbers for the END of the selection range
iSelectionRowEnd = Selection.Information(wdEndOfRangeRowNumber)
iSelectionColumnEnd = Selection.Information(wdEndOfRangeColumnNumber)
' collapse the selection range
Selection.Collapse Direction:=wdCollapseStart
' get the numbers for the END of the selection range
' now of course the START of the previous selection
iSelectionRowStart = Selection.Information(wdEndOfRangeRowNumber)
iSelectionColumnStart = Selection.Information(wdEndOfRangeColumnNumber)
' RESELECT the same range
Selection.MoveEnd Unit:=wdCharacter, Count:=lngEnd - lngStart
' set the range of cells for consumption
With ActiveDocument
Set SelectionInfo = .Range(Start:=.Tables(1).cell(iSelectionRowStart, iSelectionColumnStart).Range.Start, _
End:=.Tables(1).cell(iSelectionRowEnd, iSelectionColumnEnd).Range.End)
End With
End If
End Function
Sorry for digging up such an old question, but hopefully someone will find this useful. If you prefer to avoid the Select method, the following is what you're looking for:
ActiveDocument.Tables(1).Cell(1, 1).Range.Text = ""
It deletes images and content controls as well.
Private Sub cbClearTable_Click()
'mouse cursor must be in the table for clearing
Dim cell_BhBp As Cell
For Each cell_BhBp In Selection.Tables(1).Range.Cells
cell_BhBp.Range = ""
Next
End Sub
The code above clears the contents in all cells in the current table /the table, where the mouse cursor is/
One other way to clear all table cells of first table in document is
ActiveDocument.Tables(1).Range.Delete
Or for current table /where the cursor is in/
Selection.Tables(1).Range.Delete
Private Sub CommandButton40_Click()
Application.Activate
SendKeys ("{DEL}")
End Sub
The code above clears contents of all selected cells. In this case, the selected cells may not be adjacent. This code is fired when button of user form is clicked.
I have two columns but the codition I would like is to be evaluated from one cell to another.
The first column has cells which have a drop down validation with names, and the second will activate only if a certain name from the adjacent cell is selected.
so far i only found this code but it does not seem to work:
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("A1").Value = "Car" Then
Range("B1").Locked = False
Else
Range("B1").Locked = True
End If
End Sub
I would need this code go from (for example) A1:A10 and B1:B10.
I hope I am making sense. If there is a way to do it without VBA, that would be great.
Thanks for the help.
The Target parameter tells you the range that is being changed.
You need to do something like the following:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Intersect(Range("A1:A10"), Target)
If rng Is Nothing Then
' Not updating the range we care about
Exit Sub
Else
rng.Offset(0, 1).Locked = ... whatever ...
End If
End Sub
Note that your target range can be more than one cell (e.g. when using copy/paste), so you need to handle and test this case.
Calling Intersect returns you the intersection of the target range and the range you are interested in testing (A1:A10 in this sample).
You can then access the corresponding adjacent cell(s) using .Offset(0,1)
That code snippet works perfectly for me.
Did you place that code in the proper WorkSheet object? It won't work if you just put it into a VBA module. When you are in the Visual Basic Editor, look for a directory on the left side of the screen labeled "Microsoft Excel Objects". In that directory should be a WorkSheet object for every sheet in your file. Double-click on one of these to edit the code for that WorkSheet. This is where your code snippet should go.
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("A1").Value = "Car" Then
Range("B1").Locked = False
Me.Unprotect ("password")
Else
Range("B1").Locked = True
Me.Protect ("password")
End If
End Sub
Use Me.Protect so the .Locked method does something. You should probably unlock every other cell though.