I am designing an Excel worksheet where the user will click a command button which copies a predetermined range of cells. The user would then paste the contents into a web a
Something like this?
Sheet1.Cells(1, 1).Copy
Sheet1.Cells(1, 2).PasteSpecial xlPasteValues
Or
selection.Copy
Sheet1.Cells(1,2).Activate
Selection.PasteSpecial xlPasteValues
Copy
copies the entire part, but we can control what is pasted.
Same applies to Range
objects as well.
EDIT
AFAIK, there is no straighforward way to copy only the text of a range without assigning it to a VBA object (variable, array, etc.). There is a trick that works for a single cell and for numbers and text only (no formulas):
Sub test()
Cells(1, 1).Select
Application.SendKeys "{F2}"
Application.SendKeys "+^L"
Application.SendKeys "^C"
Cells(1, 3).Select
Application.SendKeys "^V"
End Sub
but most developers avoid SendKeys
because it can be unstable and unpredictable. For example, the code above works only when the macro is executed from excel, not from VBA
. When run from VBA
, SendKeys
opens the object browser, which is what F2 does when pressed at the VBA view :) Also, for a full range, you will have to loop over the cells, copy them one by one and paste them one by one to the application. Now that I think better, I think this is an overkill..
Using arrays is probably better. This one is my favorite reference on how you pass ranges to vba arrays and back: http://www.cpearson.com/excel/ArraysAndRanges.aspx
Personally, I would avoid SendKeys
and use arrays. It should be possible to pass the data from the VBA
array to the application, but hard to say without knowing more about the application..
In Excel, highlight the cell in question. Hit F2. CTRL+Shift+Home. (This highlights the cell’s entire contents.) CTRL+C. Go to destination application. CTRL+V. It looks like a lot of steps, but when you actually do it, it’s much quicker than using the ribbons to accomplish the same.
If you need to copy multiple cells into an application bereft of the Paste Special… facility, then do a regular copy and paste from Excel into Notepad, and then do a copy and paste from Notepad to the destination. Cumbersome, but it works.
Actually, the best way to do this is to copy the cells and paste into a notepad. Notepad won't recognize the cells. You can then copy the text back into whatever cell you want. This works for copying text from multiple cells into a single cell.
If you're dealing with a lot of cells to be copied, the selection.copy method will be extremely slow. (I experienced that when running a macro on 200 000 records).
A 100 times more performant way is to directly assign the value of one cell to another. Example from my code:
With errlogSheet
'Copy all data from the current row
reworkedErrorSheet.Range("A" & reworkedRow).Value = .Range("A" & currentRow).Value
reworkedErrorSheet.Range("B" & reworkedRow).Value = .Range("B" & currentRow).Value
reworkedErrorSheet.Range("C" & reworkedRow).Value = .Range("C" & currentRow).Value
reworkedErrorSheet.Range("D" & reworkedRow).Value = .Range("D" & currentRow).Value
reworkedErrorSheet.Range("E" & reworkedRow).Value = .Range("E" & currentRow).Value
In Excel 2013 you can do this with shortcuts.
Press Ctrl + Alt + V to open the paste special window. Now you can click the values radio button or just press V if your Excel is in English. If you don't use Excel in English you can see which button can be pressed to select the wanted option by looking at the underlining of the single letters.
Finaly press Enter to paste your copied selection.
To accomplish this, I will copy the selected cells to clipboard, save the clipboard to a text variable, and then copy this text back to clipboard.
Copy the following into a new module and then run the last sub:
'Handle 64-bit and 32-bit Office
#If VBA7 Then
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As LongPtr, _
ByVal dwBytes As LongPtr) As LongPtr
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As LongPtr
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As LongPtr
Private Declare PtrSafe Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, _
ByVal lpString2 As Any) As LongPtr
Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As LongPtr, _
ByVal hMem As LongPtr) As LongPtr
#Else
Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, _
ByVal dwBytes As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function EmptyClipboard Lib "user32" () As Long
Private Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, _
ByVal lpString2 As Any) As Long
Private Declare Function SetClipboardData Lib "user32" (ByVal wFormat _
As Long, ByVal hMem As Long) As Long
#End If
Const GHND = &H42
Const CF_TEXT = 1
Const MAXSIZE = 4096
Function ClipBoard_SetData(MyString As String)
'PURPOSE: API function to copy text to clipboard
'SOURCE: www.msdn.microsoft.com/en-us/library/office/ff192913.aspx
'Link: https://www.thespreadsheetguru.com/blog/2015/1/13/how-to-use-vba-code-to-copy-text-to-the-clipboard
#If VBA7 Then
Dim hGlobalMemory As LongPtr, lpGlobalMemory As LongPtr
Dim hClipMemory As LongPtr, x As LongPtr
#Else
Dim hGlobalMemory As Long, lpGlobalMemory As Long
Dim hClipMemory As Long, x As Long
#End If
'Allocate moveable global memory
hGlobalMemory = GlobalAlloc(GHND, Len(MyString) + 1)
'Lock the block to get a far pointer to this memory.
lpGlobalMemory = GlobalLock(hGlobalMemory)
'Copy the string to this global memory.
lpGlobalMemory = lstrcpy(lpGlobalMemory, MyString)
'Unlock the memory.
If GlobalUnlock(hGlobalMemory) <> 0 Then
MsgBox "Could not unlock memory location. Copy aborted."
GoTo OutOfHere2
End If
'Open the Clipboard to copy data to.
If OpenClipboard(0&) = 0 Then
MsgBox "Could not open the Clipboard. Copy aborted."
Exit Function
End If
'Clear the Clipboard.
x = EmptyClipboard()
'Copy the data to the Clipboard.
hClipMemory = SetClipboardData(CF_TEXT, hGlobalMemory)
OutOfHere2:
If CloseClipboard() = 0 Then
MsgBox "Could not close Clipboard."
End If
End Function
Function ClipBoard_GetData() As String
' Return the data in clipboard as text
' Source: https://docs.microsoft.com/en-us/office/vba/access/concepts/windows-api/retrieve-information-from-the-clipboard
#If VBA7 Then
Dim lpGlobalMemory As LongPtr, hClipMemory As LongPtr
Dim lpClipMemory As LongPtr
Dim RetVal As LongPtr
#Else
Dim lpGlobalMemory As Long, hClipMemory As Long
Dim lpClipMemory As Long
Dim RetVal As Long
#End If
Dim MyString As String
If OpenClipboard(0&) = 0 Then
MsgBox "Cannot open Clipboard. Another app. may have it open"
Exit Function
End If
' Obtain the handle to the global memory
' block that is referencing the text.
hClipMemory = GetClipboardData(CF_TEXT)
If IsNull(hClipMemory) Then
MsgBox "Could not allocate memory"
GoTo OutOfHere
End If
' Lock Clipboard memory so we can reference
' the actual data string.
lpClipMemory = GlobalLock(hClipMemory)
If Not IsNull(lpClipMemory) Then
If lpClipMemory <> 0 Then
MyString = Space$(MAXSIZE)
RetVal = lstrcpy(MyString, lpClipMemory)
RetVal = GlobalUnlock(hClipMemory)
' Peel off the null terminating character.
MyString = Mid(MyString, 1, InStr(1, MyString, Chr$(0), 0) - 1)
Else
MsgBox "Clipboard is empty!"
End If
Else
MsgBox "Could not lock memory to copy string from."
End If
OutOfHere:
RetVal = CloseClipboard()
ClipBoard_GetData = MyString
End Function
Sub CopySelectedCellsAsText()
' Copy selected cells to clipboard, save the clipboard to a text variable,
' and then copy this text back to clipboard
If TypeName(Selection) <> "Range" Then Exit Sub
Selection.Copy
Dim strSelection As String
strSelection = ClipBoard_GetData
Application.CutCopyMode = False
ClipBoard_SetData strSelection
End Sub