Extracting hyperlink from a range and writing them on another range - vba

I'm new to this site, but I have already found some nice advice on how to solve problems in VBA. Now I'm here to ask help on a sub that gives me problems with Hyperlinks.
In particular, my problem is similar to the one described in this topic:
Excel VBA Get hyperlink address of specific cell
I have a worksheet full of hyperlink, but I need to extract only the addresses present in the "H" column, starting from "H6" and writing them into the "N" column, starting from "N6".
I put down this code:
Sub EstraiIndirizzoPut()
Dim IndirizzoInternet As Hyperlink
Dim ISINs As String
i = 6
For Each IndirizzoInternet In Sheets("XXX").Range("H" & i).Hyperlinks
IndirizzoInternet.Range.Offset(0, 6).Value = IndirizzoInternet.Address
ISINs = Mid(IndirizzoInternet.Address, 78, 12)
Range("N" & i).Value = ISINs
i = i + 1
Next
End Sub
It works fine only for the first "H6" cell, but at the "Next" point, when it should read the "H7" cell, it goes instead to "End Sub", terminating the routine, altough the "H7" cell, as well many others down the column, are filled with hyperlinks (it gives me "Nothing" value).
Could you please suggest me where I get this wrong? Many thanks.

Your loop isnt set up correctly. Try it like this instead:
For i = 6 to 100
Set IndirizzoInternet = Sheets("XXX").Range("H" & i).Hyperlinks
IndirizzoInternet.Range.Offset(0, 6).Value = IndirizzoInternet.Address
ISINs = Mid(IndirizzoInternet.Address, 78, 12)
Range("N" & i).Value = ISINs
Next
How do you know when to stop the loop? Is it a preset number of rows? If it not, you will want to have something determine the last row to process and replace the 100 with that variable.

Related

Range.SpecialCells: What does xlCellTypeBlanks actually represent?

The Range.SpecialCells method can be used to return a Range object meeting certain criteria. The type of criteria is specified using an xlCellType constant.
One of those constants (xlCellTypeBlanks) is described as referring to "Empty cells" with no further elaboration.
Does anyone know what definition of "Empty" this method uses? Does it include cells with no values/formulas but various other features (data validation, normal formatting, conditional formatting, etc)?
That type includes the subset of cells in a range that contain neither constants nor formulas. Say starting with an empty sheet we put something in A1 and A10 and then run:
Sub ExtraSpecial()
Set r = Range("A:A").SpecialCells(xlCellTypeBlanks)
MsgBox r.Count
End Sub
we get:
Formatting and Comments are not included. Also note that all the "empty" cells below A10 are also ignored.
Papalew's response noted that "xlCellTypeBlanks" excludes any cells not within a specific version of the "used range" that's calculated in the same way as the special cell type "xlCellTypeLastCell". Through testing I've discovered that "xlCellTypeLastCell" returns the last cell of the "UsedRange" property as of the last time the property was calculated.
In other words, adding a line that references "UsedRange" will actually change the behavior of the SpecialCells methods. This is such unusual/unexpected behavior that I figured I'd add an answer documenting it.
Sub lastCellExample()
Dim ws As Worksheet
Set ws = Sheets.Add
ws.Range("A1").Value = "x"
ws.Range("A5").Value = "x"
ws.Range("A10").Value = "x"
'Initially the "UsedRange" and calculated used range are identical
Debug.Print ws.UsedRange.Address
'$A$1:$A$10
Debug.Print ws.Range(ws.Range("A1"), _
ws.Cells.SpecialCells(xlCellTypeLastCell)).Address
'$A$1:$A$10
Debug.Print ws.Cells.SpecialCells(xlCellTypeBlanks).Address
'$A$2:$A$4,$A$6:$A$9
'After deleting a value, "UsedRange" is recalculated, but the last cell is not...
ws.Range("A10").Clear
Debug.Print ws.Range(ws.Range("A1"), _
ws.Cells.SpecialCells(xlCellTypeLastCell)).Address
'$A$1:$A$10
Debug.Print ws.Cells.SpecialCells(xlCellTypeBlanks).Address
'$A$2:$A$4,$A$6:$A$10
Debug.Print ws.UsedRange.Address
'$A$1:$A$5
'...until you try again after referencing "UsedRange"
Debug.Print ws.Range(ws.Range("A1"), _
ws.Cells.SpecialCells(xlCellTypeLastCell)).Address
'$A$1:$A$5
Debug.Print ws.Cells.SpecialCells(xlCellTypeBlanks).Address
'$A$2:$A$4
End Sub
The definition does indeed contain the idea of having nothing in the cell, i.e. it excludes any cell that contains either:
a numerical value
a date or time value
a text string (even an empty one)
a formula (even if returning an empty string)
an error
a boolean value
But it also excludes any cell that’s not within the range going from A1 to the last used cell of the sheet (which can be identified programmatically through ws.cells.specialCells(xlCellTypeLastCell), or by using the keyboard Ctrl+End).
So if the sheet contains data down to cell C10 (i.e. Ctrl+End brings the focus to cell C10), then running Range("D:D").specialCells(xlCellTypeBlanks) will fail.
NB The range A1 to LastCellUsed can sometimes be different from the used range. That would happen if some rows at the top and/or some columns at on the left never contained any data.
On the other hand, cells that fit the empty definition above will be properly identified no matter any of the followings:
size or colour of font
background colour or pattern
conditional formatting
borders
comments
or any previous existence of these that would later have been cleared.
A bit beside the main subject, let me ask a tricky question related to how the term BLANK might be defined within Excel:
How can a cell return the same value for CountA and CountBlank?
Well, if a cell contains ' (which will be displayed as a blank cell), both CountA and CountBlank will return the value 1 when applied to that cell. My guess is that technically, it does contain something, though it is displayed as a blank cell. This strange feature has been discussed here.
Sub ZeroLengthString()
Dim i As Long
Dim ws As Worksheet
Set ws = ActiveSheet
ws.Range("A2").Value = ""
ws.Range("A3").Value = Replace("a", "a", "")
ws.Range("A4").Value = """"
ws.Range("A6").Value = "'"
ws.Range("A7").Formula= "=if(1=2/2,"","")"
ws.Range("B1").Value = "CountA"
ws.Range("C1").Value = "CountBlank"
ws.Range("B2:B7").FormulaR1C1 = "=CountA(RC[-1])"
ws.Range("C2:C7").FormulaR1C1 = "=CountBlank(RC[-2])"
For i = 2 To 7
Debug.Print "CountA(A" & i & ") = " & Application.WorksheetFunction.CountA(ws.Range("A" & i))
Debug.Print "CountBlank(A" & i & ") = " & Application.WorksheetFunction.CountBlank(ws.Range("A" & i))
Next i
End Sub
In this example, both lines 6 & 7 will return 1 for both CountA and CountBlank.
So the term Blank doesn’t appear to be defined a unique way within Excel: it varies from tool to tool.

Excel-VBA: creating dynamic hyperlinks to google

I have an excel sheet [Microsoft Office 2010] where the user can select a certain commodity & category, after which a list of suppliers linked to that commodity & category are printed on the sheet. I now want to print out hyperlinks next to this list of suppliers that perform a google search using the supplier as search term. This is the code I got right now: It checks whether the cells in column 6 are empty or not, if not that means a supplier name is printed in the cell. I then want a hyperlink to be printed in the column next to it that links to a google search using the suppliername as search term.
EDIT: code below works. Issue was in the if statement - isEmpty did not work for string value, but vbNullString fixed the issue.
Previous issueL
The printed links lead to the general google home page, with no search terms. I believe the reason why the links are leading to general google pages is because the actual cell values (which are used as search term) are not read properly. The code line "If Not IsEmpty(cellSupplierListed) Then" always runs, even when the cells have no suppliername in there.. I'm not sure why.
Also: Let's say there are 5 suppliers listed and the code reads over 300 rows (hard coded in code above), then still 300 links are printed out, while only 5 should have been printed out. (as only 5 of the 300 rows have values). Those 5 suppliers are printed out by previous code in the same sub and do indeed show up on the excel sheet. It just appears that the code below is not reading blank cells as being blank cells or non-blank cells as non-blank cells.
Dim cellSupplierListed As String
Dim csl As Integer
Dim h As Integer
h = 0
For csl = 1 To 300 'needs to be updated if more than 300 suppliers are listed
cellSupplierListed = Cells(9 + csl, 4).Value
If cellSupplierListed = vbNullString Then
Exit For
Else
h = h + 1
Range("G" & (9 + h)).Hyperlinks.Add Range("G" & (9 + h)), "http://www.google.com/search?q=" & cellSupplierListed, , , "Link"
End If
Next csl
From https://msdn.microsoft.com/en-us/library/office/ff822490.aspx
For Office 2013 and later, you can use the Hyperlinks.Add method
.Add(Anchor, Address, SubAddress, ScreenTip, TextToDisplay)
Example (from the above linked documentation):
With Worksheets(1)
.Hyperlinks.Add Anchor:=.Range("a5"), _
Address:="http://example.microsoft.com", _
ScreenTip:="Microsoft Web Site", _
TextToDisplay:="Microsoft"
End With
The following was tested in Office 2007:
Range("a5").Hyperlinks.Add Range("a5"), "http://www.google.com"
For the OP's actual question:
Change the following line
Cells(7, 9 + h) = Hyperlink("http://www.google.com/search?q=" & cellSupplierListed, "Link")
to
Range("F"&(9+h)).Hyperlinks.Add Range("F"&(9+h)), "http://www.google.com/search?q=" & cellSupplierListed,,,"Link"
You can copy the values and paste them back as html:
[f:f].Copy
Set o = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}") ' New MSForms.DataObject
o.GetFromClipboard: s = o.GetText
Application.CutCopyMode = False
before = "<a href='http://www.google.com/search?q=": after = "'>Link</a><br>"
s = Replace(s, vbNewLine, after & vbNewLine & before)
s = "<html>" & before & s & after
s = Replace(s, before & after, "<br>") ' replace the blank values
o.SetText s: o.PutInClipboard
[g1].PasteSpecial "Text"

.PasteSpecial doesnt work for Range object

Y helo thar,
actually my vba-knowledge is quite good and normally I don't have many difficulties coding , but this is driving me nuts.
Code is pretty easy. I have a worksheet PR_DB where all my projects are stored. For every project there are a number of employees, saved in cells (sel_pr, >10+) (employee name and ID).
I want to delete an employee from the project and tidy up the project database entry. All employees are listed in two listboxes. The ones that are working in the project, and the ones that don't. Via the buttons I can add and remove employees from either listbox.
When I add an employee to a project (top button), my Sub just puts the ID&Name at the end of the row of said project.
When I remove them from a project and therefore from the database, I look for the cell with the employee data. Afterwards I just want to cut all the cells to the right and paste them one cell to the left (via offset) so the deleted name is overwritten.
This is my code:
Sub delMA_from_prBetList()
Dim i, j, k, listRow, lastRowMA_DB, lastRowPR_DB, sel_pr As Integer
Dim wsPR, wsMA_DB, wsPR_DB As Worksheet
Dim foundMA As Boolean
Dim cutRng, pasteRng As Range
Set wsPR = Worksheets("Projekte")
Set wsMA_DB = Worksheets("MA_DB")
Set wsPR_DB = Worksheets("PR_DB")
lastRowPR_DB = wsPR_DB.UsedRange.Rows.Count
'check if any employee was selected
If IsNull(wsPR.prBetListe.Value) = True Then
MsgBox "Please select an employee."
Exit Sub
End If
j = 10
'look for selected project in DB
For i = 2 To lastRowPR_DB
If wsPR_DB.Cells(i, 1) = CInt(wsPR.prListe.Value) Then
'row ID of said project
sel_pr = i
End If
Next
'find employee
Do Until wsPR_DB.Cells(sel_pr, j) = ""
'employees are saved as "ID;NAME"
If wsPR_DB.Cells(sel_pr, j) = wsPR.prBetListe.Value & ";" & wsPR.prBetListe.Column(1, wsPR.prBetListe.ListIndex) Then
'when found, look for last cell with an entry
k = j
Do Until wsPR_DB.Cells(sel_pr, k) = ""
k = k + 1
Loop
'set cutRng so it spans from cell right to the found employee
'to last cell with an employee in that project
Set cutRng = wsPR_DB.Range(wsPR_DB.Cells(sel_pr, j + 1), wsPR_DB.Cells(sel_pr, k))
'set pasteRng like cutRng, just one cell further to the left
'so the deleted employee will be overwritten
Set pasteRng = cutRng.Offset(rowOffset:=0, columnOffset:=-1)
cutRng.Cut
pasteRng.PasteSpecial
Exit Do
End If
j = j + 1
Loop
're-initialize listboxes
Call init_maListe_dyn
Call init_prBetListe_dyn
End Sub
So whats the problem? Everything works just fine, all the cells I want to cut go into clipboard, but they arent pasted into the pasteRng. Error is
Error 1004 "Application-defined or Object-defined error". I tried a thousand things but the solution is probably too easy to find.
Hope you can help me, thanks in advance.
PS: I'm kind of in a hurry, so that text might not be as well formatted as it could be. Please bear with me.
just use directly:
cutRng.Cut pasteRng
this should solve your problem ;)
If you want to offset one column to the left it should be
Set pasteRng = cutRng.Offset(,-1)
Also, you're not defining how you want to paste. The paste settings may not be set to paste values if they were set differently prior to this instance (via code or otherwise). If you want to just cut-paste the values it would be
cutRng.Cut
pasteRng.PasteSpecial xlPasteValues
Let me know if that helps at all and if not I'll look further into it.

excel - find and highlight text from one sheet on second sheet

I am getting killed by excel, I'm not 100% sure it can do exactly what I'm needing. I've tried various functions and can come close, but none are perfect. I've uploaded a spreadsheet as an example. I have a sheet of mailboxes, followed by a cell with the users who have access to the mailbox. The cell has anywhere from 0 to 5 users separated by commas. The second sheet has a list of users. What I need is a way to parse out the first sheet, either highlight on the first sheet, or copy to another sheet; all the mailboxes that all the associated users match in the second cell appear on the second sheet.
The real world sheet I have has over 2500 mailboxes with as many as 205 (as few as 0) associated users, so I desperately need a way to mechanically filter the sheet. I'm trying to filter the mailboxes that all the associated users are present on a second sheet.
I've tried using vlookup, index/match and a few others, and what seems to trip it up is having the comma separation. Using ""& cell_i'm_looking_for &"" returns nothing so I'm guessing I need to try something else. I also have the sheet with all the users in separate cells.
I downloaded your sheet and created a module with the following function inside of it:
Function mymailboxes(who As Range, lookup As Range)
Dim myRow As Range
For Each myRow In lookup
If InStr(myRow.Cells(1, 2).Value, who.Value) > 0 Then
myReturn = myReturn & "," & myRow.Cells(1, 1).Value
End If
Next
'cut off the first ,
myReturn = Right(myReturn, Len(myReturn) - 1)
mymailboxes = myReturn
End Function
Then on Sheet 2, Cell E2 I gave the following formula: =mymailboxes(A2, Sheet1!A1:B50) which gave me the following results: mailbox1,mailbox2,mailbox4,mailbox8,mailbox12,mailbox17,mailbox21,mailbox25,mailbox28,mailbox34,mailbox39,mailbox41,mailbox42,mailbox44,mailbox49,mailbox50
I hope this helps.
To get a list of invalid users the following function will help.
Function get_invalid_users(users As Range, validusers As Range)
Dim myRow As Range
myusers = Split(users, ",")
myReturn = ""
For Each user In myusers
is_valid_user = False
'Guilty until proven innocent
For Each myRow In validusers
If myRow.Cells(1, 1).Value = user Then
is_valid_user = True
'Proven innocent, break out of the loop, no double jeopardy.
Exit For
End If
Next
If is_valid_user = False Then
myReturn = myReturn & "," & user
End If
Next
If Len(myReturn) > 0 Then
myReturn = Right(myReturn, Len(myReturn) - 1)
Else
myReturn = ""
End If
get_invalid_users = myReturn
End Function
Sheet 1, Cell C2 with formula: =get_invalid_users(B2, Sheet2!$A$1:$A$3) returned zx1234

Excel headers/footers won't change via VBA unless blank

Disclaimer: It's been a few years since I worked (a lot) with VBA, so this might be an issue caused by confusing myself with what is essentially a very different language from what I usually deal with.
So; I've got a workbook (Excel 2010) with multiple sheets (20+), most of whom are multi-page. To make things easier when printing everything, I want to add some sheet-specific headers with amongst others the name of the sheet, number of pages and so on.
I've written a tiny function that should (in theory) do this for me by iterating over all the sheets setting the header. However, for some reason it only works if the header is empty; if it already has a value it refuses to overwrite for some unknown reason.
Dim sheetIndex, numsheets As Integer
sheetIndex = 1
numsheets = Sheets.Count
' Loop through each sheet, but don't set any of them to active
While sheetIndex <= numsheets
Dim sheetname, role, labeltext As String
sheetname = Sheets(sheetIndex).name
role = GetRole(mode)
labeltext = "Some text - " & sheetname & " - " & role
With Sheets(sheetIndex).PageSetup
.LeftHeader = labeltext
.CenterHeader = ""
.RightHeader = "Page &[Page] / &[Pages]"
.LeftFooter = "&[Date] - &[Time]"
.CenterFooter = ""
.RightFooter = "Page &P / &N"
End With
sheetIndex = sheetIndex + 1
Wend
I found a solution that seems to work for replacing text. For whatever reason, in the macro, you need to include the header/footer format character codes in order for it to work properly.
This code worked to replace existing header text with new information:
Sub test()
Dim sht As Worksheet
Set sht = Worksheets(1)
sht.PageSetup.LeftHeader = "&L left text"
sht.PageSetup.CenterHeader = "&C center Text"
sht.PageSetup.RightHeader = "&R right text"
End Sub
Without the &L, &C, and &R codes before the text, I could not get it to work.
Some interesting behavior I found is that if you use the following code:
.CenterHeader = "&L some text"
it will actually put the some text in the LeftHeader position. This led me to believe that the formatting codes were very important.
The line Application.PrintCommunication = False (which is added by the macro recorder) before doing PageSetup screws up the formating via VBA.
If your code has got this line in it, try removing it. That solved my problem with setting the header and footer via VBA.
I've read StackOverflow for years and this is the first time I've actually been able to post a solution ... hope it helps someone!! Also, you need to remember, I am a CPA not a programmer ;-)
I am reading some values from the ActiveSheet to populate the header. The application is a tax election that will be sent with a tax return so it must have the taxpayer's name and social security number at the top.
Sub PrintElection()
' Print preview the MTM Election
If Range("Tax_Year").Value = Range("First_MTM_year").Value Then
ActiveSheet.PageSetup.LeftHeader = Format(Worksheets("Election").Range("Taxpayer_Name").Value)
ActiveSheet.PageSetup.RightHeader = Format(Worksheets("Election").Range("Taxpayer_SSN").Value)
ActiveWindow.SelectedSheets.PrintPreview
Else
MsgBox "The 'Effective For Tax Year' date must EQUAL the 'First MTM year' date", vbOKOnly, "Check Years"
Sheets("Roadmap").Select
Range("First_MTM_year").Select
End If
End Sub
It checks to see if the Mark-to-Market election year is the same as the election form then formats the election page.
I split the sheet print setup into 2 loops. First loop with Application.PrintCommunication = False I run the non-header/footer setup. I then set Application.PrintCommunication = True and run the header/footer setup in a second loop. Appears to run faster than in XL2003, and applies the header/footer correctly. Until MS fixes this bug, that works fine for me.