Getting a copied formula from the clipboard in Excel VBA - vba

I’ve a routine that effectively pastes a link to a cell or cells that a user has copied to the clipboard, putting a space after the "=" (a personal preference, for readability) and changing the anchoring to row-only before pasting. If the link is to another sheet, the font is changed to blue. The code is as follows:
Sub QuickLink2()
' Copies a link,putting a space after the "=" and changing the
' anchoring to row-only. If the link is to another sheet, the
' font is changed to blue.
Dim r As Long, c As Long
Dim FormulaArr() As Variant
Dim Destination As Range
Application.ScreenUpdating = False
' Paste link
On Error Resume Next
ActiveSheet.Paste Link:=True
If Err.Number = 1004 Then GoTo NoSelection '1004 is a paste failure
On Error GoTo 0
' Transfer pasted link to array
If Selection.Cells.Count = 1 Then
ReDim FormulaArr(1 To 1, 1 To 1)
FormulaArr(1, 1) = Selection.Formula
Else
FormulaArr = Selection.Formula
End If
' Adjust formula spaces and anchoring
For r = 1 To UBound(FormulaArr, 1)
For c = 1 To UBound(FormulaArr, 2)
FormulaArr(r, c) = Replace(FormulaArr(r, c), "=", "= ")
FormulaArr(r, c) = Application.ConvertFormula _
(FormulaArr(r, c), xlA1, xlA1, xlAbsRowRelColumn)
Next c
Next r
Set Destination = Selection
Destination.Formula = FormulaArr
' Change font to blue if link is to another sheet
If Destination(1).Formula Like "*!*" Then _
Destination.Font.Color = RGB(0, 0, 255)
Exit Sub
NoSelection:
Application.CutCopyMode = False
End Sub
The idea here is to speed up the code by assigning the pasted link to a variant array, doing the necessary work on the array, and then assigning the array to a range. What I really want to do, however, is to access the copied cell formulas directly from the clipboard, and assign to the variant array without the intermediate ActiveSheet.Paste Link:=True step.
The following code would allow me to get the copied cell value, but of course I'm looking for the copied formulas.
Dim DataObj As New MSForms.DataObject
Dim S As String
DataObj.GetFromClipboard
S = DataObj.GetText

To get formula's:
Private Sub PutCellFormulaInClipBoard(ByVal Cell As Range)
Dim oDataObject As Object
Set oDataObject = _
GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
With oDataObject
.Clear
.SetText Cell.Cells(1).Formula
.PutInClipboard
End With
End Sub
Ref

Related

Excel macro - check for a string in a cell, run a routine, move to the next row, do it again

I am not a Dev, but given I do use Excel, I have been tasked to create a looping macro that will check for a string ('Resource') in a cell and if it finds that string, then run a Copy and Paste code and then move to the next row. This starts at row 5 and runs continuously until row 199, but does not work on every row, hence the validation for the string Resource.
I have managed to create the macro for the Copy and Paste but it also has issues as I created it using the macro recorder and it only works on the row I actually did the recording on.
I am at a complete loss, can anyone help?
this is what I have so far
A New Resource name is added manually to the spreadsheet
the user clicks cell (C6) to focus the curser
the user clicks a macro button called 'Forecast for Future Project 1' to start the macro
On the button click the Macro will:
Interogate if cell to the left of current cell (B6) = 'Resource'
IF Yes, THEN
Sub CP()
DO
Range("C6").Select
Selection.Copy
Application.Goto Reference:="ProjAdd"
ActiveSheet.Paste
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = _
"=SUMIF('Current Project Utilisation'!R2C1:R62C1,RC1,'Current Project Utilisation'!R2C:R62C)+SUMIF('Future Project 1'!R2C1:R62C1,RC1,'Future Project 1'!R2C:R62C)"
Range("ProjAdd").Select
Selection.Copy
Range("C6").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Selection.Copy
Range(Selection, Selection.End(xlToRight)).Select
ActiveSheet.Paste
Range("B6").Select
Loop Until ActiveCell.Address(0,0) = "$B$199"
End Sub
Move to cell under original active cell (C7) and Repeat the Macro until cell C199 is reached
If (B6) does not = 'Resource' then move to go to the cell under (C7) aand Repeat the Macro until cell C199 is reached
Refresh Worksheet to update data
Would something like this work for you?
Sub CopyPasteResource()
Dim CopyRange As Range
Dim Cell As Range
Set CopyRange = Workbooks("YourWorkBookName").Sheets("Sheet1").Range("C6:C199")
For Each Cell In CopyRange
If InStr(1, Cell.Offset(0, -1).Text, "Resource") Then
Cell.Copy
'paste where you wish
End If
Next Cell
End Sub
EDIT: Or do you want to loop through B6:B199 and then C6:199? I'm not entirely clear on the aim.
Ah the old macro recorder, generating 90% extra code since 1997. I couldn't exactly figure out from your question what exactly is being copied and to where but this code will loop through rows 5 to 199, check if the value in column B = "Resource" and then set the corresponding value in column C, you should be able to modify for your needs but I think you definitely want a structure more like this than what the recorder generated for you..
public sub cp()
Dim ws as Worksheet
Set ws = Worksheets("Current Project Utilisation")
Dim i as int
for iI = 5 to 199
if(ws.cells(i, 2).value = "Resource") then
ws.cells(i, 3).value = "what you're copying"
end if
next I
end sub
Assuming your cell range doesn't change you can do this for the looping part
Sub ResourceCheck()
Dim WS As Worksheet
Set WS = ActiveSheet
Dim Resources() As Long, r As Long
ReDim Resources(5 To 199)
For r = 5 To 199
If UCase(WS.Cells(r, 2).Value) = "RESOURCE" Then
WS.Cells(r, 3).Value = "x"
'Do copy paste part
End If
Next r
Application.Calculate
End Sub
Can you add a sample of your data? It's a bit hard to see what you're referencing to and how the data relates to each other.
Also, where is the "Projadd" cell reference? And what does it do?
Sub CP()
' I like to know what worksheet I'm on
Dim ws as Worksheet
' if it's a dedicated worksheet use this
' Set ws = ThisWorkbook.Worksheets("Sheet1")
' Otherwise following your current code
Set ws = ActiveSheet
' I also like to grab all my data at once
Dim Data as Variant
Data = ws.Range("B6:B199")
' No need to focus the cursor
For row = 5 to 199
' No need to select any range
' Is this case-sensitive???
If Data(row-4, 1) = "Resource" Then
' Copy C6??? Paste 'ProjAdd'
ws.Cells(row, 3).Copy Range("ProjAdd")
Application.CutCopyMode = False
End If
Next
End Sub

Using a VBA copy sheet macro and formula r1c1

Maybe I've been staring at this for too long, but I have a macro that copies worksheets in Excel that works. What I'm also trying to do is include this into the loop (just the R1C1 formula from this recorded macro):
Sub Macro4()
'
' Macro4 Macro
'
' Keyboard Shortcut: Ctrl+a
'
Sheets("<Null>").Select
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 94.5, 42.75).Select
Sheets("<Null>").Copy After:=Sheets(3)
ActiveCell.FormulaR1C1 = "='Dividing Walls Only'!RC[-2]"
Range("C4").Select
Sheets("<Null> (2)").Select
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 95.25, 42.75).Select
Sheets("<Null> (2)").Copy After:=Sheets(4)
Range("C3").Select
ActiveCell.FormulaR1C1 = "='Dividing Walls Only'!R[1]C[-2]"
Range("C4").Select
Sheets("<Null> (3)").Select
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 95.25, 42.75).Select
Sheets("<Null> (3)").Copy After:=Sheets(5)
Range("C3").Select
ActiveCell.FormulaR1C1 = "='Dividing Walls Only'!R[2]C[-2]"
Range("C4").Select
End Sub
Obviously, this would be silly to repeat 180 times. This is the Copy Sheet macro that I have already:
Sub CopySheet()
Call OptimizeCode_Begin
Dim x As Integer
x = InputBox("Enter number of times to copy active sheet")
For numtimes = 1 To x
'Loop by using x as the index number to make x number copies
ActiveWorkbook.ActiveSheet.Copy _
After:=ActiveWorkbook.Sheets(3)
'Put copies in front of Sheet3
'Might need to move the new sheets
Next
Call OptimizeCode_End
End Sub
What I would like to do is either incorporate a nested loop or something to automatically advance the R1C1 formula for each sheet that would keep me from having to type in the cell I'm trying to reference after all the sheets have copied. Any help would be appreciated.
Thanks!
Justin
From what I could understand from your post, the code below will run according to the number of times selected by the user in InputBox, and copy a Sheet at after the last on.
For each created Sheet it will add a Formula to Cell C4, I'm just not sure the logics in advancing the Formula for each sheet.
Sub CopySheets()
Dim x As Long
Dim numtimes As Long
Dim newSht As Worksheet
x = Application.InputBox("Enter number of times to copy active sheet", Default:=1, Type:=1)
' optimize run time
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Application.DisplayAlerts = False
' create the Buttons on the original sheet
' (will be copied inside the loop for all other sheets)
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 94.5, 42.75).Select
For numtimes = 1 To x
'Loop by using x as the index number to make x number copies
ActiveWorkbook.ActiveSheet.Copy _
After:=ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.Count)
Set newSht = ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.Count)
' give the new Sheet Name the reference of num of times
newSht.Name = "<NULL " & numtimes & ">"
' advance the row number in the formula
newSht.Range("C3").FormulaR1C1 = "='Dividing Walls Only'!R[" & numtimes & "1]C[-2]"
Next numtimes
' Resume Settings
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
may be this is what you are after:
Option Explicit
Sub CopySheet()
Dim numtimes As Long, x As Long, rowIndex As Long
Call OptimizeCode_Begin
rowIndex = 4 '<-- this is the row index that will be used in the formula that'll be written in the first new sheet
numtimes = Application.InputBox("Enter number of times to copy active sheet", Default:=1, Type:=1)
For x = 1 To numtimes
'Loop by using x as the index number to make x number copies
ActiveWorkbook.ActiveSheet.Copy _
After:=ActiveWorkbook.Sheets(3)
Range("C3").Formula = "='Dividing Walls Only'!A" & rowIndex '<--| write formula in the new sheet cell "C3" referencing "Dividing Walls Only" worksheet column "A" cell in current 'rowIndex'
rowIndex = rowIndex + 1 '<--| update row index for subsequent new sheet formula
Next
Call OptimizeCode_End
End Sub
you see I used Excel (i.e. Application) InputBox() method instead of VBA InputBox() one since the former lets you specify the return data type also (Type:=1 for numeric input), thus forcing the user input to the wanted one.

How To Paste My Formula In A Cell With Specific Text Instead Of A Column?

I pretty much have an already working macro for me but for the future it may cause problems because the macro i have finds the column i gave it and then starts to input the formula there. Now my data may change in the future and in that column i might have something new so the macro would obviously run the formulas to the wrong column. Changing it manually is possible but hectic and a lot of work. Is there any possible way i can select a cell with a specific text in it instead of the column? since the text will never change this will me much easier for me to work with. Because doing this the formulas will always be posted in the correct column.
EDIT! I added the whole code to the post so you can see it more clearly and understand what i mean more clearly.
Sub HW_Copy_RawData_Formulas()
Dim intChoice As Integer
Dim strPath As String
Dim I As Integer
Dim filePath As String
Dim SourceWb As Workbook
Dim TargetWb As Workbook
Dim Lastrow As Long
Dim Nrow As Long
Set TargetWb = ActiveWorkbook
' Delete Rows
On Error Resume Next
TargetWb.Worksheets("Raw Data").Activate
Range("A2:AL2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Delete Shift:=xlUp
Range("A2:AL2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Delete Shift:=xlUp
'Copy Formulas
Range("AF2").Formula = "=IF([#ServDt]<DATE(2013,1,1), DATE(YEAR([#ServDt]),12,31),EOMONTH([#ServDt],0))"
Range("AG2").Formula = "=IF([#Amount]>1,[#Quantity],0)"
Range("AH2").Formula = "=IF([#Amount]<>0,[#Amount]-[#Adj]-[#[Adjustment ]],0)"
Range("AI2").Formula = "=IF(AND([#Department]=""HH"",[#Pay]=0),[#Amount]/2,0)"
Range("AJ2").Formula = "=IF([#Amount]<>0,[#Bal]-[#[Adjustment ]],[#Bal]+[#Adj])"
Range("AK2").Formula = "=VLOOKUP([Department],Service[#All],2,FALSE)"
Range("AL2").Formula = "=VLOOKUP([#Entity],Site,3,FALSE)"
MSG1 = MsgBox("Add Raw Data", vbYesNo)
If MSG1 = vbYes Then
'only allow the user to select one file
Application.FileDialog(msoFileDialogOpen).AllowMultiSelect = False
'make the file dialog visible to the user
intChoice = Application.FileDialog(msoFileDialogOpen).Show
'determine what choice the user made
If intChoice <> 0 Then
'get the file path selected by the user
strPath = Application.FileDialog( _
msoFileDialogOpen).SelectedItems(1)
Else: GoTo endmsg
End If
'Setting source of data
Set SourceWb = Workbooks.Open(strPath)
Lastrow = SourceWb.Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row
SourceWb.Worksheets(1).Range("A2:BJ" & Lastrow).SpecialCells(xlCellTypeVisible).Select
Selection.Copy Destination:=TargetWb.Sheets("Raw Data").Range("A2")
' Close the source workbook without saving changes.
SourceWb.Close savechanges:=False
Else
endmsg:
MsgBox "Complete"
End If
Range("AF2:AL2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Range("AF2").PasteSpecial xlPasteValues
End Sub
The following code snippet might be of use to you. It acquires the range of the cell given a specific value. It can also be used to search a specific row with .Rows() instead.
Dim *YOURCELL* As Range
Set *YOURCELL*= .Columns(1).Find(What:= *WHATYOUWANTTOFIND*, LookAt:=xlWhole, MatchCase:=False, searchformat:=False)
If, however, you do not know where the last used cell is located, then consider reading this other post.
EDIT:
The while loop runs as long as the currently selected cell is not empty. In this loop, it selects the next cell to the right and increments a count. After the loop has finished, the currently selected cell is the first empty cell in the second row. Count has found the column number of it by incrementing alongside the loop, so it can then be used as needed. I used cells instead of range afterwards because it can use the column number.
Range("A2").Select
Dim count As Integer
count = 1
'skip all used cells in the row
Do While Not (ActiveCell.value = None)
ActiveCell.Offset(0, 1).Range("A1").Select
count = count + 1
Loop
Cells(count, 2).Formula = your_formula
Cells(count + 1, 2).Formula = your_formula ' next cell to the right
Cells(count + 2, 2).Formula = your_formula ' next cell to the right

Copy/Paste Specific Columns from a Worksheet to another

I want to copy some columns with headers from a worksheet to another one. I've created an array that looks for the different headers needed so I can copy and paste the entire column into the new tab. I know I have an error somewhere because I'm getting a type mismatch error and possibly other types as well. Can someone take a look and see what I'm missing/have wrong?
Dim rngCell As Range
Dim strHeader() As String
Dim intColumnsMax As Integer
Sheets.Add.Name = "Material Master"
Sheets.Add.Name = "BOM"
intColumnsMax = Sheets("HW Zpure Template").UsedRange.Columns.Count
ReDim strHeader(1 To intColumnsMax)
strHeader(1) = "MATERIAL"
strHeader(2) = "MATERIAL TYPE"
strHeader(3) = "MATERIAL DESCRIPTION"
For Each rngCell In Rows(4)
For i = 1 To intColumnsMax
If strHeader(i) = rngCell.Value Then
rngCell.EntireColumn.Copy
Sheets("Material Master").Select
ActiveSheet.Paste Destination:=Worksheets("Material Master").Cells(1, i)
Sheets("HW Zpure Template").Select
End If
Next i
Next
I prefer to use Application.Match to locate a specific column header label rather than cycling through them trying to find a match. To that end, I've heavily modified your code.
Dim c As Long, v As Long, vHDRs As Variant
Dim s As Long, vNWSs As Variant, wsMM As Worksheet
vHDRs = Array("MATERIAL", "MATERIAL TYPE", "MATERIAL DESCRIPTION")
vNWSs = Array("Material Master", "BOM")
For v = LBound(vNWSs) To UBound(vNWSs)
For s = 1 To Sheets.Count
If Sheets(s).Name = vNWSs(v) Then
Application.DisplayAlerts = False
Sheets(s).Delete
Application.DisplayAlerts = True
Exit For
End If
Next s
Sheets.Add after:=Sheets(Sheets.Count)
Sheets(Sheets.Count).Name = vNWSs(v)
Next v
Set wsMM = Sheets("Material Master")
With Sheets("HW Zpure Template")
For v = LBound(vHDRs) To UBound(vHDRs)
If CBool(Application.CountIf(.Rows(4), vHDRs(v))) Then
c = Application.Match(vHDRs(v), .Rows(4), 0)
Intersect(.UsedRange, .Columns(c)).Copy _
Destination:=wsMM.Cells(1, Application.CountA(wsMM.Rows(1)) + 1)
End If
Next v
End With
Set wsMM = Nothing
Correct me if I'm wrong, but your code seemed to be looking for the column labels in row 4. That is what I'm using above but if that assumption is incorrect then the fix should be fairly self-evident. I've also stacked the copied columns into the first available column to the right. Your code may have been putting them in the original position.
When you run the above, please note that it will remove worksheets named Material Master or BOM without asking in favor of inserting its own worksheets of those names. Given that, it's probably best to run on a copy of your original.
Using the Find() method is a very efficient way of finding the data you want. Below are a few suggestions to optimize your existing code.
Dim rngCell As Range
Dim strHeader() As String
Dim intColumnsMax As Integer
Dim i As Integer
Sheets.Add.Name = "Material Master"
Sheets.Add.Name = "BOM"
'Quick way to load a string array
'This example splits a comma delimited string.
'If your headers contain commas, replace the commas in the next line of code
'with a character that does not exist in the headers.
strHeader = Split("MATERIAL,MATERIAL TYPE,MATERIAL DESCRIPTION", ",")
'Only loop through the headers needed
For i = LBound(strHeader) To UBound(strHeader)
Set rngCell = Sheets("HW Zpure Template").UsedRange.Find(What:=strheader(i), LookAt:=xlWhole)
If Not rngCell Is Nothing Then
'Taking the intersection of the used range and the entire desired column avoids
'copying a lot of unnecessary cells.
Set rngCell = Intersect(Sheets("HW Zpure Template").UsedRange, rngCell.EntireColumn)
'This method is more memory consuming, but necessary if you need to copy all formatting
rngCell.Copy Destination:=Worksheets("Material Master").Range(rngCell.Address)
'This method is the most efficient if you only need to copy the values
Worksheets("Material Master").Range(rngCell.Address).Value = rngCell.Value
End If
Next i

Excel VBA Get hyperlink address of specific cell

How do I code Excel VBA to retrieve the url/address of a hyperlink in a specific cell?
I am working on sheet2 of my workbook and it contains about 300 rows. Each rows have a unique hyperlink at column "AD". What I'm trying to go for is to loop on each blank cells in column "J" and change it's value from blank to the hyperlink URL of it's column "AD" cell. I am currently using this code:
do while....
NextToFill = Sheet2.Range("J1").End(xlDown).Offset(1).Address
On Error Resume Next
GetAddress = Sheet2.Range("AD" & Sheet2.Range(NextToFill).Row).Hyperlinks(1).Address
On Error GoTo 0
loop
Problem with the above code is it always get the address of the first hyperlink because the code is .Hyperlinks(1).Address. Is there anyway to get the hyperlink address by range address like maybe sheet1.range("AD32").Hyperlinks.Address?
This should work:
Dim r As Long, h As Hyperlink
For r = 1 To Range("AD1").End(xlDown).Row
For Each h In ActiveSheet.Hyperlinks
If Cells(r, "AD").Address = h.Range.Address Then
Cells(r, "J") = h.Address
End If
Next h
Next r
It's a bit confusing because Range.Address is totally different than Hyperlink.Address (which is your URL), declaring your types will help a lot. This is another case where putting "Option Explicit" at the top of modules would help.
Not sure why we make a big deal, the code is very simple
Sub ExtractURL()
Dim GetURL As String
For i = 3 To 500
If IsEmpty(Cells(i, 1)) = False Then
Sheets("Sheet2").Range("D" & i).Value =
Sheets("Sheet2").Range("A" & i).Hyperlinks(1).Address
End If
Next i
End Sub
My understanding from the comments is that you already have set the column J to a string of the URL. If so this simple script should do the job (It will hyperlink the cell to the address specified inside the cell, You can change the cell text if you wish by changing the textToDisplay option). If i misunderstood this and the string is in column AD simply work out the column number for AD and replace the following line:
fileLink = Cells(i, the number of column AD)
The script:
Sub AddHyperlink()
Dim fileLink As String
Application.ScreenUpdating = False
With ActiveSheet
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 4 To lastrow
fileLink = Cells(i, 10)
.Hyperlinks.Add Anchor:=Cells(i, 10), _
Address:=fileLink, _
TextToDisplay:=fileLink
Next i
End With
Application.ScreenUpdating = True
End Sub
Try to run for each loop as below:
do while....
NextToFill = Sheet2.Range("J1").End(xlDown).Offset(1).Address
On Error Resume Next
**for each** lnk in Sheet2.Range("AD" & Sheet2.Range(NextToFill).Row).Hyperlinks
GetAddress=lnk.Address
next
On Error GoTo 0
loop
This IMO should be a function to return a string like so.
Public Sub TestHyperLink()
Dim CellRng As Range
Set CellRng = Range("B3")
Dim HyperLinkURLStr As String
HyperLinkURLStr = HyperLinkURLFromCell(CellRng)
Debug.Print HyperLinkURLStr
End Sub
Public Function HyperLinkURLFromCell(CellRng As Range) As String
HyperLinkURLFromCell = CStr(CellRng.Hyperlinks(1).Address)
End Function