5

Copying the values of a filtered range to an array seems to work without a problem: the array then contains values from both filtered and unfiltered cells. However, when I copy the array's contents back to the filtered range, the results are incomprehensible to me.

Here's my code:

Sub test()
    Dim rangecopy() As Variant

    rangecopy() = Range(Cells(2, 1), Cells(14, 3)).Value
    For c = LBound(rangecopy, 1) To UBound(rangecopy, 1)
        rangecopy(c, 1) = c
        rangecopy(c, 2) = c * 10
        rangecopy(c, 3) = c * 100
    Next
    Range(Cells(2, 1), Cells(14, 3)).Value = rangecopy()
End Sub

It is supposed to give the following result. Here, the range was unfiltered when the macro copied the array to it.

Imgur

If the range is filtered by column D ("NO" is filtered out), the result looks like this:

Imgur

First, the filtered cells aren't updated. Then, most cells from column B get values from the array's first column (4, 5, 6), while a few others get values from the array's second column correctly (10). The last two rows are filled with #N/A error. Is this supposed to work that way? I'm using Office 2010.

Community
  • 1
  • 1
Cutter
  • 1,673
  • 7
  • 27
  • 43
  • fyi, I get the same results. Don't know why as yet. – iND Apr 15 '12 at 04:48
  • What version of Excel are you using? – Cutter Apr 15 '12 at 04:52
  • I get the same results with the same code with Excel 2003. I also get the same result if I replace the last statement with `Set RngUnion = Application.Union(Range("A2:C2"), Range("A4:C6"), Range("A8:C8"), Range("A10:C14"))` `RngUnion = rangecopy`. So the cause is not the AutoFilter but the Union. – Tony Dallimore Apr 15 '12 at 17:14
  • With `Set RngUnion = Application.Union(Range("A2:C2"), Range("A3:C6"), Range("A7:C8"), Range("A9:C14"))` (that is, with no gaps in the union), I get the correct result. With `Set RngUnion = Application.Union(Range("A2:C2"), Range("A4:C6"), Range("A8:C8"), Range("A10:C14"), Range("A16:C18"))` (that is, with the union made up to the same size as the array), the first four sub-range are as before but the last sub-range is set to the first three rows of the array. – Tony Dallimore Apr 15 '12 at 17:30

1 Answers1

5

I really hope someone with knowledge of the internal workings of VBA can provide more insight on your question. I can share the following:

First, it is working as intended. However, I don't know why this is the design, nor what exactly is happening in the assignment process.

There are many cases that create a similar issue. For instance, if you have the filter on (some rows are hidden) and try to fill (drag) a formula down, you will see similar results, in that hidden rows aren't populated, but they do affect the (relative) references in the formula. On the other hand, if you manually copy and paste into a filtered range, the data is pasted into the hidden rows (as you intend).

It seems that any range referenced that is part of the Autofilter range is actually non-contiguous*. Using Range.Address does not always reveal this, nor does looping through Range.Areas. If we modify your example, we can see where the "real" error is:

Dim r1 as range
Dim r2 as range

Set r1 = Sheet1.Range("A1:B5") 'some numbers in a range
Set r2 = Sheet2.Range("A2:B6") 'same-size range underneath a filtered header

r1.Copy Destination:=r2

It works when all the rows are visible. When the filter on Sheet2 creates hidden rows, the result is "Run-time error '1004': ...the Copy area and the paste area are not the same size and shape." On the other hand, using the "manual" / clipboard method works for hidden rows:

r1.Copy    
r2.PasteSpecial (xlPasteValues)

Since assigning an array to a range bypasses the clipboard (as in the 1st block), we ought to receive an error (instead you just end up with erroneous results).

The only solutions I'm aware of are to either loop through the range and assign a value to each cell:

For i = 1 to LastRow
  For j = 1 to LastCol
    Sheet1.Cells(i,j).Value = myArr(i,j)
  Next
Next

OR (better) remove the Autofilter, assign the array to the range, then reapply the filter.

*technically it's contiguous, so it may be better to say that the range is composed of several ranges/areas, although using .Address doesn't indicate this and there is only one area when you try to loop through Range.Areas

Zairja
  • 1,441
  • 12
  • 31
  • Thanks Zairja. Is it possible to save the Autofilter settings in order to reapply them later? Or better, is it possible to disable Autofilter without losing its settings, then re-enable it later? – Cutter Apr 27 '12 at 16:48
  • 1
    I believe [this](http://stackoverflow.com/questions/9489126/in-excel-vba-how-do-i-save-restore-a-user-defined-filter) should work for you. A few more things to add. Be careful when copying from a filtered range. A direct copy (r1.copy dest:= r2) only copies visible rows. A direct copy from a filtered range (with hidden rows) to another filtered range (with hidden rows), throws error 1004: "That command cannot be used on multiple selections." So, it appears Autofilter (when it creates hidden rows) breaks up contiguous ranges into multiple selections. – Zairja Apr 27 '12 at 16:56