问题
I wanted to copy some not continous ranges from several workbook/ worksheets to a specific sheet. I am using a userform and RefEdit control on that. But the Excel freezs each time I am calling the form and addressing the ranges! I can't do anything except End Excel! Here is my Code.
Private Sub CommandButton1_Click()
Dim rng As Range
Set rng = Range(Me.RefEdit1.Value)
rng.Copy
ThisWorkbook.Sheets("Transfer").Range("a1").PasteSpecial xlPasteValues
End Sub
Private Sub UserForm_Activate()
For Each wb In Application.Workbooks
ComboBox1.AddItem wb.Name
Next
ComboBox1 = ActiveWorkbook.Name
End Sub
Private Sub Combobox1_Change()
If ComboBox1 <> "" Then Application.Workbooks(ComboBox1.Text).Activate
End Sub
My Form was showed modeless.
https://1drv.ms/u/s!ArGi1KRQ5iItga8CLrZr9JpB67dEUw
So really not sure I can copy with this method or not. As I was not able to test my form. Thanks, M
回答1:
No RefEdit in a modeless Userform
The problem is that you cannot use a modeless userform containing a RefEdit
control. Otherwise Excel loses control over the keyboard focus and can only be terminated via task manager or Ctrl + Alt + Delete. So you'll have to show your Userform modal (e.g. expressly by .Show vbModal
or without this default argument).
Further hints:
Don't use a RefEdit
control within another control, especially not within a Frame
control, this can cause issues.
Check if you get a valid range (see Helper function getRng
below), then you can assign the new values simply by coding ThisWorkbook.Sheets("Transfer").Range("A1") = Range(Me.RefEdit1.Value)
instead of using Copy
and Paste
.
For non contiguos ranges there are number of code examples at SO, but that's not the cause of Excel freezing. In the code example below I assume that you want to write one cell only to worksheet range Target!A1
.
Furthermore I added a boolean variable bReady
in order to lock or unlock the Combobox1_Change()
event and prevent unnecessary activations.
Code example
Option Explicit ' declaration head of UserForm Code module
Dim bReady As Boolean ' boolean flag to show completion of workbook list
Private Sub CommandButton1_Click()
Dim rng As Range
Set rng = getRng(Me.RefEdit1.Value) ' << use helper function getRng
If Not rng Is Nothing Then
'write only first cell back to cell Transfer!A1
ThisWorkbook.Sheets("Transfer").Range("A1").Value = rng.Cells(1).Value
'correct address to one cell only
bReady = False
RefEdit1.Value = rng.Parent.Name & "!" & rng.Cells(1).Address
bReady = True
RefEdit1.ControlTipText = "Value of " & RefEdit1.Value & " = " & Format(rng.Cells(1).Value, "General")
Else ' after manual input of not existing ranges
RefEdit1.Value = "": Me.RefEdit1.ControlTipText = "None": Beep
RefEdit1.SetFocus
End If
End Sub
Private Sub UserForm_Activate()
Dim wb As Workbook
For Each wb In Application.Workbooks
ComboBox1.AddItem wb.Name
Next
ComboBox1 = ActiveWorkbook.Name
bReady = True ' allow workbooks activation in Combobox1_Change event
End Sub
Private Sub Combobox1_Change()
If Not bReady Then Exit Sub ' avoids activation before completion of workbooks list
If ComboBox1 <> "" Then Application.Workbooks(ComboBox1.Text).Activate
End Sub
Helper function getRng()
Function getRng(ByVal sRng As String) As Range
' Purpose: return valid range object or return Nothing
On Error Resume Next
Set getRng = Range(sRng)
If Err.Number <> 0 Then Err.Clear
End Function
Edit: treating non contiguous areas
Pressing the Ctrl
key you are able to select non contiguous ranges, e.g. Sheet1!D12:E15,Sheet1!B7:C10
as completely separate areas (separated by a colon in RefEdit
). Referring to your comment, I added the following example how to write back contiguous and non contiguous areas via a variant datafield array (called v
in the below example code). As far as I understood you alwayas want to start at cell A1 in your target sheet:
Private Sub CommandButton1_Click()
Dim rng As Range, r As Range, v As Variant
Dim i As Long, n As Long
Dim iRowOffset As Long, temp As Long
Dim iColOffset As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Transfer")
Set rng = getRng(Me.RefEdit1.Value) ' << use helper function getRng
If Not rng Is Nothing Then
' a) count (non) contiguous areas obtained via Ctrl-key in RefEdit (e.g. "D13:D15,A1:B2")
n = rng.Areas.Count
' b) calculate necessary row/col offset to start copies at A1 in target sheet
iRowOffset = rng.Areas(1).Row - 1
iColOffset = rng.Areas(1).Column - 1
For i = 1 To n
temp = rng.Areas(i).Row - 1
If temp < iRowOffset And temp > 0 Then iRowOffset = temp
temp = rng.Areas(i).Column - 1
If temp < iColOffset And temp > 0 Then iColOffset = temp
Next i
' c) write values back
For i = 1 To n
With rng.Areas(i).Parent.Name ' sheet
v = rng.Areas(i) ' write values to variant 1-based 2-dim array
ws.Range(rng.Areas(i).Address).Offset(-iRowOffset, -iColOffset) = v
End With
Next i
Else ' after manual input of not existing ranges
RefEdit1.Value = "": Beep
RefEdit1.SetFocus
End If
End Sub
回答2:
Thanks to T.M. for his huge help.
By changing his code I came to this answer. also, copy and paste method was working for me, but that was not a good practice.
Anyway, all the credit goes for T.M.
Private Sub btnCopy_Click()
Dim rng As Range, v As Variant
Dim i As Long, n As Long, colno As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Transfer")
Set rng = getRng(Me.RefEdit1.Value) ' << use helper function getRng
If Not rng Is Nothing Then
ws.UsedRange.Clear
' a) count (non) contiguous areas obtained via Ctrl-key in RefEdit (e.g. "D13:D15,A1:B2")
n = rng.Areas.Count
' c) write values back
For i = 1 To n
v = rng.Areas(i) ' write values to variant 1-based 2-dim array
colno = IIf(ws.Cells(1, 1) = "", 1, ws.Range("xfd1").End(xlToLeft).Column + 1) ' FINDS THE LAST EMPTY COLUMN
ws.Cells(1, colno).Resize(rng.Areas(i).Rows.Count, rng.Areas(i).Columns.Count) = v
Next i
Else ' after manual input of not existing ranges
RefEdit1.Value = "": Beep
RefEdit1.SetFocus
End If
End Sub
来源:https://stackoverflow.com/questions/49543651/vba-using-refedit-for-copying-range-between-workbooks