2016-09-01 5 views
5

Мне нужно восстановить свойство фона (Range.Interior.Color) из нескольких тысяч ячеек. Прохождение через каждую ячейку индивидуально очень медленно из-за ограничений COM-Interop.Извлечь свойства нескольких ячеек из Excel за один раз?

Можно ли получить свойство клеток, которые не .Text, .Value или .Value2 из Range, содержащих более одной ячейки в одном вызове?

+0

Я просто попробовал это с 30k-ячейками в VBA, и это заняло меньше секунды. Является ли ваш COM-выход из процесса случайным? Я не могу думать о других свойствах Range, которые, к сожалению, возвращают массив. Я кратко подумал об использовании Excel4, но вам все равно нужно вызвать код vba для вызова функции (скажем) GET.CELL, и они не возвращают массивы в любом случае, так что это не слишком многообещающе. – GodLovesATrier

ответ

0

Вы можете сделать это, но это не тривиально, поэтому я не уверен, стоит ли пытаться подражать подобному поведению.

В принципе вам нужно создать макрос в Excel, который выполняет эту работу за вас, а затем просто вернуть результаты после завершения макроса. Это в основном эмулирует поведение Value. Я не совсем уверен, почему MS решила не реализовывать все свойства в Range, чтобы вести себя одинаково, странно.

Для этого вам необходимо обратиться к библиотеке COM Microsoft Visual Basic for Application Extensibility 5.3. Это дает вам необходимые инструменты для динамической сборки и добавления макросов в книгу Excel.

Первый шаг - создать метод, который добавит модуль и требуемый макрос в рабочую книгу, с которой вы взаимодействуете (вы также можете открыть пустую книгу и создать там макрос, если вы используете эту функцию на разных листах открываются и закрываются).

В следующем примере добавлен макрос, который возвращает массив со всеми цветами ячеек в указанном диапазоне. Это написано в VBA, у меня нет VS со мной, но версия C# должна быть довольно простой для порта.

Sub AddCode() 
    Dim wb As Workbook 
    Dim xPro As VBIDE.VBProject 
    Dim xCom As VBIDE.VBComponent 
    Dim xMod As VBIDE.CodeModule 

    Set wb = ActiveWorkbook 

    With wb 
     Set xPro = .VBProject 
     Set xCom = xPro.VBComponents.Add(vbext_ct_StdModule) 
     Set xMod = xCom.CodeModule 

     With xMod 
      .AddFromString "Public Function GetInteriorColors(r As Range) As Variant" & vbCrLf & _ 
          " Dim colors() As Long" & vbCrLf & _ 
          " Dim row As Integer" & vbCrLf & _ 
          " Dim column As Integer" & vbCrLf & _ 
          " ReDim colors(r.Rows.Count - 1, r.Columns.Count - 1)" & vbCrLf & _ 
          " For row = 1 To r.Rows.Count" & vbCrLf & _ 
          "  For column = 1 To r.Columns.Count" & vbCrLf & _ 
          "   colors(row - 1, column - 1) = r.Cells(row, column).Interior.Color" & vbCrLf & _ 
          "  Next" & vbCrLf & _ 
          " Next" & vbCrLf & _ 
          " GetInteriorColors = colors" & vbCrLf & _ 
          "End Function" 
     End With 
    End With 
End Sub 

Что-то примечание; Иногда я получаю странную ошибку при выполнении AddFromString. Макрокод добавляется правильно в модуль, но иногда я получаю сообщение об ошибке; проглатывание это не кажется вредным, но если у вас есть такая же проблема, я бы заглянул в нее.

Теперь, когда у вас есть макрос, возвращая результат легко (опять же, написано в VBA):

Public Function GetColors(r As Range) As Long() 
    //Note the absolute path to the macro; this is probably needed if the macro is in a different workbook. 
    GetColors = Application.Run(ActiveWorkbook.Name & "!GetInteriorColors", r) 
End Function 
+0

Я думаю, что вашему решению также необходимо многократное путешествие.Но вместо приложения OP и Excel между VBE и Excel. В цветах строк (строка - 1, столбец - 1) = r.Cells (строка, столбец). Interior.Color вы получаете доступ к ячейкам индивидуально. – z32a7ul

0

Я хотел бы попробовать следующее (написано в VBA, но может быть преобразован в C#):

Public Sub GetColors() 
    Dim ewsTarget As Worksheet: Set ewsTarget = ActiveWorkbook.Worksheets(1) 
    ewsTarget.Copy , ewsTarget.Parent.Worksheets(ewsTarget.Parent.Worksheets.Count) 
    Dim ewsCopy As Worksheet: Set ewsCopy = ewsTarget.Parent.Worksheets(ewsTarget.Parent.Worksheets.Count) 
    ewsCopy.UsedRange.ClearContents 
    ewsCopy.UsedRange.Columns.EntireColumn.ColumnWidth = 0.5 
    ewsCopy.UsedRange.Rows.EntireRow.RowHeight = 5# 
    ewsCopy.UsedRange.CopyPicture xlScreen, xlBitmap 
    ewsCopy.Delete 
End Sub 

Этот код помещает растровое изображение в буфер обмена. Это растровое изображение создается из копии рабочего листа, однако содержимое ячеек удаляется, поэтому вы увидите только фон ячейки, более того, строки и столбцы имеют одинаковую высоту и длину. Ваша программа C# может затем получить это растровое изображение и получить отдельные пиксели, учитывая, что местоположение ячеек можно легко вычислить, так как строки и столбцы имеют одинаковую высоту и ширину.

Я знаю, что это только обходной путь, но я не думаю, что было лучшее решение. Это единственный способ (нельзя писать, читать только) и трудно распространяться на другие свойства (возможно, на границы и шрифты).