Fast way to copy formatting in excel

I have two bits of code. First a standard copy to copy from cell A to cell B

Sheets(sheet_).Cells(x, 1).Copy Destination:=Sheets("Output").Cells(startrow, 2) 

I can do almost the same thing using

 Sheets("Output").Cells(startrow, 2) = Sheets(sheet_).Cells(x, 1) 

Now this second method is much faster, avoiding copying to the clipboard and pasting again. However, it does not copy formatting, as the first method does. The second version copies almost 500 lines almost instantly, while the first method adds about 5 seconds to the time. And the final version can exceed 5,000 cells.

Thus, my question can be changed to the second line to enable cell formatting (mostly font color), while maintaining speed.

Ideally, I would like to be able to copy cell values ​​to an array / list along with font formatting so that I can perform further sorting and operations on them before β€œpasting” them back into the worksheet.

So my ideal solution would be something like

 for x = 0 to 5000 array(x) = Sheets(sheet_).Cells(x, 1) 'including formatting next for x = 0 to 5000 Sheets("Output").Cells(x, 1) next 

Is it possible to use RTF strings in VBA or is it possible only in vb.net, etc.

Answer *

To see how my origianl method and the new method compare, here are the results, either before or after

New code = 65 ms

 Sheets("Output").Cells(startrow, 2) = Sheets(sheet_).Cells(x, 1) Sheets("Output").Range("B" & startrow).Font.ColorIndex = Sheets(sheet_).Range("A" & x).Font.ColorIndex 'copy font colour as well 

Old code = 1296 ms

 'Sheets("Output").Cells(startrow, 2).Value = Sheets(sheet_).Cells(x, 1) 'Sheets(sheet_).Cells(x, 1).Copy 'Sheets("Output").Cells(startrow, 2).PasteSpecial (xlPasteFormats) 'Application.CutCopyMode = False 
+8
vba format copy rtf
source share
5 answers

For me you cannot. But if this suits your needs, you can have speed and formatting by copying the entire range at once, instead of looping:

 range("B2:B5002").Copy Destination:=Sheets("Output").Cells(startrow, 2) 

And by the way, you can create a custom range string, for example Range("B2:B4, B6, B11:B18")


edit : if your source is sparse, can you just format the destination right after copying is complete?

+4
source share

You could just use Range("x1").value(11) something like below:

 Sheets("Output").Range("$A$1:$A$500").value(11) = Sheets(sheet_).Range("$A$1:$A$500").value(11) 

range has a default property. Value "Value" plus may have 3 optional arguments 10,11,12. 11 is what you need to change both the value and the formats. It does not use the clipboard, so it is faster. - Durgesh

+12
source share

Remember that when you write:

 MyArray = Range("A1:A5000") 

you really write

 MyArray = Range("A1:A5000").Value 

You can also use the names:

 MyArray = Names("MyWSTable").RefersToRange.Value 

But Value is not the only property of Range. I used:

 MyArray = Range("A1:A5000").NumberFormat 

I doubt that

 MyArray = Range("A1:A5000").Font 

but i would expect

 MyArray = Range("A1:A5000").Font.Bold 

for work.

I do not know which formats you want to copy, so you have to try.

However, I must add that when you copy and paste a large range, it is not much slower than doing it through an array, as we all thought.

Publish Editing Information

Having laid out the above, I tried my own advice. My experiments with copying Font.Color and Font.Bold to an array failed.

Of the following statements, the second will fail with a type mismatch:

  ValueArray = .Range("A1:T5000").Value ColourArray = .Range("A1:T5000").Font.Color 

ValueArray must be of type variant. I tried both options and for a long time for ColourArray without success.

I populated a ColourArray with values ​​and tried the following statement:

  .Range("A1:T5000").Font.Color = ColourArray 

The entire range will be colored according to the first ColourArray element, and then Excel will loop, consuming about 45% of the CPU time, until I have completed it using the task manager.

There is a time limit associated with switching between sheets, but recent questions about the duration of the macro have forced everyone to reconsider our belief that working with arrays is much faster.

I built an experiment that generally reflects your requirement. I filled out a Time1 worksheet with 5,000 rows of 20 cells that were selectively formatted as: bold, italics, underline, bottom, border, red, green, blue, brown, yellow, and gray - 80%.

With version 1, I copied every 7th cell from the "Time1" sheet to the "Time2" worksheet using a copy.

With version 2, I copied every 7 cells from the "Time1" worksheet to the "Time2" worksheet, copying the value and color through the array.

With version 3, I copied every 7th cell from the "Time1" sheet to the "Time2" worksheet, copying the formula and color through the array.

Version 1 took an average of 12.43 seconds, and the second version took an average of 1.47 seconds, while version 3 took an average of 1.83 seconds. Version 1 copied the formulas and all the formatting, copied the values ​​and color of version 2, and version 3 copied the formulas and color. With versions 1 and 2, you can add bold and italics, say, and still have some time in your hand. However, I'm not sure what to worry about, given that copying 21,300 values ​​takes only 12 seconds.

** Code for version 1 **

I do not think this code contains anything that needs explanation. Reply with a comment if I am wrong and I will correct it.

 Sub SelectionCopyAndPaste() Dim ColDestCrnt As Integer Dim ColSrcCrnt As Integer Dim NumSelect As Long Dim RowDestCrnt As Integer Dim RowSrcCrnt As Integer Dim StartTime As Single Application.ScreenUpdating = False Application.Calculation = xlCalculationManual NumSelect = 1 ColDestCrnt = 1 RowDestCrnt = 1 With Sheets("Time2") .Range("A1:T715").EntireRow.Delete End With StartTime = Timer Do While True ColSrcCrnt = (NumSelect Mod 20) + 1 RowSrcCrnt = (NumSelect - ColSrcCrnt) / 20 + 1 If RowSrcCrnt > 5000 Then Exit Do End If Sheets("Time1").Cells(RowSrcCrnt, ColSrcCrnt).Copy _ Destination:=Sheets("Time2").Cells(RowDestCrnt, ColDestCrnt) If ColDestCrnt = 20 Then ColDestCrnt = 1 RowDestCrnt = RowDestCrnt + 1 Else ColDestCrnt = ColDestCrnt + 1 End If NumSelect = NumSelect + 7 Loop Debug.Print Timer - StartTime ' Average 12.43 secs Application.Calculation = xlCalculationAutomatic End Sub 

** Code for versions 2 and 3 **

A user type definition must be placed before any subroutine in the module. The code works through the source sheet, copying the values ​​or formulas and colors into the next element of the array. After the selection is completed, it copies the collected information to the destination sheet. This avoids switching between sheets more than necessary.

 Type ValueDtl Value As String Colour As Long End Type Sub SelectionViaArray() Dim ColDestCrnt As Integer Dim ColSrcCrnt As Integer Dim InxVLCrnt As Integer Dim InxVLCrntMax As Integer Dim NumSelect As Long Dim RowDestCrnt As Integer Dim RowSrcCrnt As Integer Dim StartTime As Single Dim ValueList() As ValueDtl Application.ScreenUpdating = False Application.Calculation = xlCalculationManual ' I have sized the array to more than I expect to require because ReDim ' Preserve is expensive. However, I will resize if I fill the array. ' For my experiment I know exactly how many elements I need but that ' might not be true for you. ReDim ValueList(1 To 25000) NumSelect = 1 ColDestCrnt = 1 RowDestCrnt = 1 InxVLCrntMax = 0 ' Last used element in ValueList. With Sheets("Time2") .Range("A1:T715").EntireRow.Delete End With StartTime = Timer With Sheets("Time1") Do While True ColSrcCrnt = (NumSelect Mod 20) + 1 RowSrcCrnt = (NumSelect - ColSrcCrnt) / 20 + 1 If RowSrcCrnt > 5000 Then Exit Do End If InxVLCrntMax = InxVLCrntMax + 1 If InxVLCrntMax > UBound(ValueList) Then ' Resize array if it has been filled ReDim Preserve ValueList(1 To UBound(ValueList) + 1000) End If With .Cells(RowSrcCrnt, ColSrcCrnt) ValueList(InxVLCrntMax).Value = .Value ' Version 2 ValueList(InxVLCrntMax).Value = .Formula ' Version 3 ValueList(InxVLCrntMax).Colour = .Font.Color End With NumSelect = NumSelect + 7 Loop End With With Sheets("Time2") For InxVLCrnt = 1 To InxVLCrntMax With .Cells(RowDestCrnt, ColDestCrnt) .Value = ValueList(InxVLCrnt).Value ' Version 2 .Formula = ValueList(InxVLCrnt).Value ' Version 3 .Font.Color = ValueList(InxVLCrnt).Colour End With If ColDestCrnt = 20 Then ColDestCrnt = 1 RowDestCrnt = RowDestCrnt + 1 Else ColDestCrnt = ColDestCrnt + 1 End If Next End With Debug.Print Timer - StartTime ' Version 2 average 1.47 secs ' Version 3 average 1.83 secs Application.Calculation = xlCalculationAutomatic End Sub 
+3
source share

Just use the NumberFormat property after the Value property: In this example, Ranges are defined using variables called ColLetter and SheetRow, and this comes from a for-next loop that uses the integer i, but they can usually be defined ranges, of course.

TransferSheet.Range (ColLetter and SheetRow) .Value = Range (ColLetter and i) .Value TransferSheet.Range (ColLetter and SheetRow) .NumberFormat = Range (ColLetter and i) .NumberFormat

0
source share

whether:

 Set Sheets("Output").Range("$A$1:$A$500") = Sheets(sheet_).Range("$A$1:$A$500") 

... work? (I don’t have Excel in front of me, so I can’t check.)

-2
source share

All Articles