Excel-VBA macro to transform cell content into a comment of another cell - vba

I have a seemingly simple goal to turn the content of column B into comments of column A.
I have tried using the following code from #Dy.Lee mentioned here, but unfortunately it gives me a Run-time error '1004' Application-defined or object-defined error...
Sub Komentari()
Dim rngDB As Range, rngComent As Range
Dim rng As Range
Dim cm As Comment, i As Integer
Set rngComent = Range("A1:A50")
Set rngDB = Range("B1:B50")
For Each rng In rngComent
i = i + 1
If Not rng.Comment Is Nothing Then
rng.Comment.Delete
End If
Set cm = rng.AddComment
With cm
.Visible = False
.Text Text:=rngDB(i).value
End With
Next rng
End Sub
Can somebody, please, spot the mistake or suggest a better solution for this?

I'd go this way (explanations in comments):
Public Sub Komentari()
Dim rng As Range
With Range("A1:A50") ' reference comments range
.ClearComments ' clear its comments
For Each rng In .Offset(, 1).SpecialCells(xlCellTypeConstants) ' loop through refrenced range adjacent not empty cells
With rng.Offset(, -1).AddComment ' add comment to current rng corresponding comment range cell
.Visible = False
.Text rng.Value2
End With
Next
End With
End Sub

Sub Komentari()
Dim rngDB As Range, rngComent As Range
Dim rng As Range
Dim cm As Comment, i As Integer
Set rngComent = Range("A1:A50")
For Each rng In rngComent
i = i + 1
If Not rng.Range("B1").Comment Is Nothing Then
rng.Range("B1").Comment.Delete
End If
rng.Range("B1").AddComment (rng.Text)
Next rng
End Sub

Something like the following where you can use Offset to get the adjacent range, you drop the = when adding the text value to the comment, test that there is actually a value present first as well, and ensure you state the sheet to avoid implicit Activesheet reference.
Option Explicit
Public Sub Komentari()
Dim rngComent As Range
Dim rng As Range, cm As Comment
With ThisWorkbook.Worksheets("Sheet1")
Set rngComent = .Range("A1:A50")
For Each rng In rngComent
If Not rng.Comment Is Nothing Then
rng.Comment.Delete
End If
Set cm = rng.AddComment
With cm
.Visible = False
If rng.Offset(, 1) <> vbNullString Then .Text rng.Offset(0, 1).Value
End With
Next
End With
End Sub
Rather than add blank comments you could also flip this round to:
Option Explicit
Public Sub Komentari()
Dim rngComent As Range
Dim rng As Range, cm As Comment
With ThisWorkbook.Worksheets("Sheet1")
Set rngComent = .Range("A1:A50")
For Each rng In rngComent
If Not rng.Comment Is Nothing Then
rng.Comment.Delete
End If
If rng.Offset(, 1) <> vbNullString Then
Set cm = rng.AddComment
With cm
.Visible = False
.Text rng.Offset(0, 1).Value
End With
End If
Next
End With
End Sub

Related

Finding values from another worksheet (a loop in a loop)

I would like to atomatize an excel process using VBA.
The script has to go cell by cell in a selected area on Sheet3. Each cell contains a number or is blank.
The script will go and search for the value of each cell in a specific range on Sheet2. When it finds something the content of the whole row where it was found must go bold.
If it finds nothing it will just procede to the next cell.
After browsing here on stackoverflow and different guides I've managed to put together a script. It has no errors but it doesn't do Anything.
Sub MacroText()
Dim xlRng As Range
Dim rng As Range
Dim xlSht As Worksheet
Dim sht As Worksheet
Dim iLastRow As Integer
Dim iRow As Integer
Dim bFound As Boolean
Dim xCell As Range
Dim xlCell As Range
Dim valueToFind As String
bFound = False
Set sht = ActiveWorkbook.Worksheets("Sheet3")
Set xlSht = ActiveWorkbook.Worksheets("Sheet2")
Set rng = Selection
Set xlRng = ActiveWorkbook.Worksheets("Sheet2").Range("A:A")
iLastRow = xlSht.Range("A1").End(xlDown).Row
Set xlRng = xlSht.Range("A1:A" & iLastRow)
For Each xCell In rng
valueToFind = xCell.Value
For Each xlCell In xlRng
Worksheets("Sheet2").Activate
If xlCell.Value = valueToFind Then
bFound = True
iRow = xlCell.Row
Rows(iRow).Font.Bold = True
End If
If bFound = True Then Exit For
End
Next xlCell
Next xCell
End Sub
I am assuming that it has to be something with positioning within the code but I couldn't find any information for that.
After working on this for 12 hours I would really appreciate your help.
Cheers!
You could use the Find method to achieve this instead of the second loop
Sub MacroText()
Dim xlRng As Range
Dim rng As Range
Dim xlSht As Worksheet
Dim sht As Worksheet
Dim iLastRow As Long
Dim iRow As Long
Dim bFound As Boolean
Dim xCell As Range
Dim xlCell As Range
Dim valueToFind As String
Dim FoundRange As Range
bFound = False
Set sht = ActiveWorkbook.Worksheets("Sheet3")
Set xlSht = ActiveWorkbook.Worksheets("Sheet2")
Set rng = Selection
Set xlRng = ActiveWorkbook.Worksheets("Sheet2").Range("A:A")
iLastRow = xlSht.Range("A1").End(xlDown).Row
Set xlRng = xlSht.Range("A1:A" & iLastRow)
For Each xCell In rng
Set FoundRange = Nothing
Set FoundRange = xlRng.Find(what:=xCell.Value2)
If Not FoundRange Is Nothing Then
FoundRange.EntireRow.Font.Bold = True
End If
Next xCell
End Sub
For Each xlCell In xlRng
Worksheets("Sheet2").Activate
If xlCell.Value = valueToFind Then
xlCell.EntireRow.Font.Bold = True
End If
Next xlCell
I don't know what thing you are not getting, but I assumed that you are not getting desired row as bold. Replace the above code with your's for loop and run.
I didn't tested it, but am uncertain about not working.

Excel VBA Find Method for specific column

VBA find method seems to fail when I am trying to search for the value in specific column.
This code
Sub TargetR()
Dim CLL As Range
Dim TargetRange As Worksheet
Dim R As Range
Set CLL = ThisWorkbook.Worksheets(1).Range("J29")
Set TargetRange = ThisWorkbook.Worksheets(1)
Set R = TargetRange.Cells.Find(CLL.Value)
If Not (R Is Nothing) Then
Debug.Print R.Address
Else: Debug.Print "Empty"
End If
End Sub
works perfectly.
While the search limited by the column with keyword header fails:
Sub Target()
Dim CLL As Range
Dim TargetRange As Worksheet
Dim targetColumn As Range
Dim sColumn As Range
Dim R As Range
Set CLL = ThisWorkbook.Worksheets(1).Range("J29")
Set TargetRange = ThisWorkbook.Worksheets(1)
Set sColumn = TargetRange.Cells.Find("This Column")
Set targetColumn = sColumn.EntireColumn
Set R = targetColumn.Cells.Find(CLL.Value)
If Not (R Is Nothing) Then
Debug.Print R.Address
Else: Debug.Print "Empty"
End If
End Sub
Specifying search direction through xlByColunm does not help
Try the code below (explanation inside the code as comments):
Option Explicit
Sub Target()
Dim CLL As Range
Dim TargetRange As Worksheet
Dim sColumn As Range
Dim R As Range
Set CLL = ThisWorkbook.Worksheets(1).Range("J29")
Set TargetRange = ThisWorkbook.Worksheets(1)
Set sColumn = TargetRange.Cells.Find("This Column")
If Not sColumn Is Nothing Then ' <-- make sure Find was successful
Set R = sColumn.EntireColumn.Find(what:=CLL.Value, LookIn:=xlValues, lookat:=xlWhole)
If Not R Is Nothing Then
Debug.Print R.Address
Else: Debug.Print "Empty"
End If
Else ' Find failed to find "This Column"
MsgBox "Unable to find 'This Column'"
End If
End Sub
As I figured out, Find method do not work correctly if value that needs to be found contained in merged cell, and Find methoud applied only for the leftmost column, that contain part of that merged cell. For my VBA code to work properly, some additional merge check , and subsequent extension of search area was the answer
If sColumn.MergeCells Then
Set sColumn = Column.Resize(,Column.MergeArea.Columns.Count)

Excel VBA - Unique List - Type Mismatch

I'm getting a type mismatch error when I try to run this code on opening the workbook, the line highlighted by the debugger is the 2nd to last, I've added a comment to the code so you know where.
The line where there is an error is the same as a line of code further up so I'm unsure why I get a type mismatch error following the second loop.
I have tested the two loops separately in their own modules and it works fine. It's when I combine them into 1 module and try to run on opening the workbook that I get the error.
Private Sub Workbook_Open()
Dim rng As Range
Dim InputRng As Range, OutRng As Range
Set dt = CreateObject("Scripting.Dictionary")
Set InputRng = Worksheets("AA").Range("C2:AF366")
Set OutRng = Worksheets("Unique Lists").Range("A2")
For Each rng In InputRng
If rng.Value <> "" Then
dt(rng.Value) = ""
End If
Next
OutRng.Range("A1").Resize(dt.Count) = Application.WorksheetFunction.Transpose(dt.Keys)
Application.CutCopyMode = False
Set dt = CreateObject("Scripting.Dictionary")
Set InputRng = Worksheets("CT").Range("C2:AF366")
Set OutRng = Worksheets("Unique Lists").Range("B2")
For Each rng In InputRng
If rng.Value <> "" Then
dt(rng.Value) = ""
End If
Next
'ERROR OCCURS ON THE NEXT LINE
OutRng.Range("A1").Resize(dt.Count) = Application.WorksheetFunction.Transpose(dt.Keys)
End Sub
For info: the code is designed to create 2 unique lists from cell ranges on different worksheets upon opening the workbook.
Make sure you have data in your "CT" worksheet. If all of your cells inside Range("C2:AF366") don't have any values, then dt.Count = 0 (since your Dictionary is Empty), and this will result with a run-time error.
You already defined and set OutRng, in Set OutRng = Worksheets("Unique Lists").Range("B2"), so in your error line you can use:
OutRng.Resize(dt.Count) = Application.WorksheetFunction.Transpose(dt.Keys)
That's one way to debug dictionary, when you are not sure what do you have inside- take a look at the last lines:
Option Explicit
Public Sub TestMe()
Dim rng As Range
Dim InputRng As Range, OutRng As Range
Dim dt As Object
Set dt = CreateObject("Scripting.Dictionary")
Set InputRng = Worksheets("AA").Range("C2:AF366")
Set OutRng = Worksheets("Unique Lists").Range("A2")
For Each rng In InputRng
If rng.Value <> "" Then
dt(rng.Value) = ""
End If
Next
OutRng.Range("A1").Resize(dt.Count) = Application.WorksheetFunction.Transpose(dt.keys)
Application.CutCopyMode = False
Set dt = CreateObject("Scripting.Dictionary")
Set InputRng = Worksheets("CT").Range("C2:AF366")
Set OutRng = Worksheets("Unique Lists").Range("B2")
For Each rng In InputRng
If rng.Value <> "" Then
dt(rng.Value) = ""
End If
Next
'ERROR OCCURS ON THE NEXT LINE
OutRng.Range("A1").Resize(dt.Count) = Application.WorksheetFunction.Transpose(dt.keys)
Dim dtKey As Variant
For Each dtKey In dt.keys
Debug.Print dtKey
Next dtKey
End Sub
Indeed, your code works quite ok when dt.Count is not 0.

Complie Error - Else without If VBA

I'm a newbie at vba so please excuse my ignorance. I created a macro that will run through a dropdown list and print for each name in the dropdown - and it works:
Sub PrintAll()
Dim Cell As Range
Dim Rng As Range
Dim Wks As Worksheet
Set Wks = Worksheets("PRINT PAGE")
Set Rng = ThisWorkbook.Names("Brokers").RefersToRange
For Each Cell In Rng
If Cell <> "" Then
Wks.Range("$B$5").Value = Cell.Text
Wks.PrintOut
End If
Next Cell
End Sub
However, the workbook has multiple worksheets to read from and therefore I need the vba to read from multiple ranges so I tried this
Sub PrintAll()
Dim Cell As Range
Dim Cell2 As Range
Dim Rng As Range
Dim Wks As Worksheet
Set Wks = Worksheets("PRINT PAGE")
If "$A$5" = "Company1" Then Rng = ThisWorkbook.Names("1Brokers").RefersToRange
ElseIf "$A$5" = "Company2" Then Rng = ThisWorkbook.Names("2Brokers").RefersToRange
Else: Set Rng = ThisWorkbook.Names("3Brokers").RefersToRange
End If
For Each Cell In Rng
If Cell <> "" Then
Wks.Range("$B$5").Value = Cell.Text
Wks.PrintOut
End If
Next Cell
End Sub
The problem is I keep getting "Compile error Else without If" on the If statement. Is there something wrong with how I'm setting up the If statement or with how I'm using it in the code?
This block compiles for me. Please test it. I have always start set in the after then in if.
Sub PrintAll()
Dim Cell As Range
Dim Cell2 As Range
Dim Rng As Range
Dim Wks As Worksheet
'Set Wks = Worksheets("PRINT PAGE")
If "$A$5" = "Company1" Then
Rng = ThisWorkbook.Names("1Brokers").RefersToRange
ElseIf "$A$5" = "Company2" Then
Rng = ThisWorkbook.Names("2Brokers").RefersToRange
Else
Set Rng = ThisWorkbook.Names("3Brokers").RefersToRange
End If
For Each Cell In Rng
If Cell <> "" Then
Wks.Range("$B$5").Value = Cell.Text
Wks.PrintOut
End If
Next Cell
End Sub
Use
If Range("$A$5").Value = "Company1"..
Similarly for "$A$5".
"$A$5" is just a string and you are comparing a string to a string. What you want is a range object
EDIT
Regarding the Error that you are getting, you have to use Then
The syntax is (Hiten004 post made me realize it)
If <Cond> Then
ElseIF <Cond> Then
End If
Rather than:
If "$A$5" = "Company1" Then Rng = ThisWorkbook.Names("1Brokers").RefersToRange
use:
If "$A$5" = "Company1" Then Set Rng = ThisWorkbook.Names("1Brokers").RefersToRange
There may be other problems in your code.

Is VBA function possible without using the clipboard; make all sheets values only

I have the following function using Excel 2010:
Private Function MakeAllSheetsValuesOnly(targetBookName As String)
If Excel.ActiveWorkbook.Name = Excel.ThisWorkbook.Name Then
Else
Excel.Workbooks(targetBookName).Activate
Dim mySheet
For Each mySheet In Excel.ActiveWorkbook.Sheets
With mySheet
With .Cells
.Copy
.PasteSpecial Excel.xlPasteValues
End With
.Select
.Range("A1").Select
End With
Excel.ActiveWindow.SmallScroll Down:=-200
Excel.Application.CutCopyMode = False
Next mySheet
End If
End Function 'MakeAllSheetsValuesOnly
It works but I'd rather not rely on the clipboard is there an alternative way to make all sheets values only?
Just found an alternative logic I've been using in another program which is relevent to this topic:
Dim rSource As Range
Dim rDest As Range
Set rSource = .Range("C5:BG" & .Range("B4").Value + 4)
Set rDest = mySummaryBook.Sheets("Data_Measures").Cells(Rows.Count, 4).End(xlUp)(2, 1)
With rSource
Set rDest = rDest.Resize(.Rows.Count, .Columns.Count)
End With
rDest.Value = rSource.Value
Set rSource = Nothing
Set rDest = Nothing
Maybe something like this:
With mySheet.UsedRange
.Value = .Value
End With
You don't need a function for this.
Tim has already given you a great method. Here is another way...
Sub Sample()
MakeAllSheetsValuesOnly "Book2"
End Sub
Private Sub MakeAllSheetsValuesOnly(targetBookName As String)
Dim mySheet As Worksheet
Dim formulaCell As Range
Dim aCell As Range
Application.ScreenUpdating = False
For Each mySheet In Workbooks(targetBookName).Sheets
On Error Resume Next
Set formulaCell = mySheet.Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
If Not formulaCell Is Nothing Then
For Each aCell In formulaCell
aCell.Value = aCell.Value
Next
End If
Next mySheet
Application.ScreenUpdating = True
End Sub
Building on Tim's answer above, which seems to be the most efficient way to do it, you could clean up your code to make it a little faster, less resource intensive. See below. Not huge changes, but will help with processing none-the-less. First don't need Function. Sub will do. No need to select and activate so many things:
Private Sub MakeAllSheetsValuesOnly(targetBookName As String)
If ActiveWorkbook.Name <> ThisWorkbook.Name Then
Dim wkb As Workbook
Set wkb = Workbooks(targetBookName)
With wkb
Dim mySheet As Worksheet
For Each mySheet In wkb.Worksheets
mySheet.UsedRange.Value = mySheet.UsedRange.Value
Next mySheet
End With
End If
End Sub 'MakeAllSheetsValuesOnly