【UiPath】Excel - 表示されているシート／列／行だけを取得（非表示分は取得しない）

# はじめに

この投稿は、RPAツールのUiPathで「Excelで「表示されている」シート／列／行を取得」する方法についての投稿です。

「Excel操作を.NETのコードでやってみよう」シリーズです。他の投稿はこちら。

https://qiita.com/miya_g/items/02230dc5774818f24e90

https://qiita.com/miya_g/items/1c3bb14583110ccac14b

https://qiita.com/miya_g/items/e36f7b5da5bc994a49d7

https://qiita.com/miya_g/items/e4bf3de19c8b0c4225ca

# Excel操作で困ることがある

UiPathでExcel操作をする際に「どうやって実現すれば良いのだろう」と困ることがあります。
例えば今回の「表示されているシートだけを取得したい」といったケースです。

この手の課題は「一度知ってしまえば、その後は横展開すれば解決できる」ものなので、
自分が知っているナレッジを、整理も含めて投稿していこうと思います。

# 標準では「非表示」も取得してしまう

エクセルには

 - 非表示シート
 - 非表示列
 - 非表示行

がありますが、UiPath標準アクティビティでは「**表示／非表示の関係なく、全て取得**」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/fb6f7cb8-f42a-6190-0c52-b92cffdc603b.png)
そのため「非表示は相手にしたくない」ケースでは困ることがあります。
例えば、以下の業務要件があったとします。

```
- 「2021年売上.xlsx」内には「商品ID」別のシートがある（シート名が商品IDになっている）
- 各シートの商品IDで、当月売上データをWebシステムからスクレイピングし、シートに貼り付ける
- 廃盤の古い商品は「シートが非表示になっている」ので、そのシートは更新しなくて良い
```

この場合、UiPathの標準アクティビティの「**UiPath.Excel.Activities.ExcelGetWorkbookSheets**」でシートの一覧を取得して、ループしながら売上データをWEBから取得すると思いますが、標準アクティビティでは「非表示のシートも取得」してしまいます。
![062613263396366316535302e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/153963d0-4960-d395-9081-8d920a0024a5.png)

オプションで「非表示シートを含めない」があれば良いのですが、残念ながらありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/7415e6d2-9d00-83a0-c9ad-21cb1b3de2bc.png)

標準アクティビティ以外で「非表示を取得しない」方法は、以下があります。

| No | 方法 | デメリット／懸念 |
|:-:|:-|:-|
| 1 | VBAのコードを書いて、Invoke VBAで取得する | VBA実行セキュリティ設定が必要 |
| 2 | .NETのコードを書いて、Invoke Codeで取得する | UiPath標準外なので、現場によっては使用NG  |
| 3 | .NETのコードでInvoke Code | 書くのが難しい？  |

1.2はデメリット／懸念があるので、出来るなら、3の「.NETのコードでInvoke Code」が良いと思います。
以下で紹介します。

# .NETで、表示中のシート名のみを取得する

以下は、Excelブック内のシートで「表示中のシート名」を取得するVB.NETのコードです。
　※ 非表示のシートは取得しません

``` VB
'// 対象エクセルファイルのパスを指定
Dim bookPath As String = "C:\temp\exceltest.xlsx"

'// ファイルを読み込み
Dim sheetNameList As New List(Of String)
Dim ap As New Microsoft.Office.Interop.Excel.Application
Dim wb As Microsoft.Office.Interop.Excel.Workbook
Dim ws As Microsoft.Office.Interop.Excel.Worksheet 
Try
	'// ブックを開く
	wb = ap.Workbooks.Open(IO.path.GetFullPath(bookPath))
	'// シートループ
	For idx As Integer = 1 To wb.Sheets.Count
		ws =  CType(wb.Sheets(idx), Microsoft.Office.Interop.Excel.Worksheet)
		'// 表示状態を確認して、リストに格納
		If ws.Visible = Microsoft.Office.Interop.Excel.XlSheetVisibility.xlSheetVisible Then
			sheetNameList.Add(ws.Name)
		End If
	Next
	wb.Close
Catch ex As Exception
	Console.writeLine(ex.ToString)
	Throw(ex)
Finally
	'// ブックを閉じる
	If Not(wb Is Nothing) Then wb = Nothing
	ap = Nothing
End Try
```

呼び出し可能なxamlファイルにしたものを用意しました。以下からダウンロードできます。
[[github.com] GetVisibleSheetNameList.xaml](https://github.com/miyag/UiPath.ReusableCode/blob/master/ExcelGetVisibleProperty/GetVisibleSheetNameList.xaml)

引数は以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/3ddcf57b-e3b9-2dd6-0797-3af2fdfb70d5.png)

# .NETで、表示中の列名のみを取得する

以下は、Excelシート内で「表示中の列名（列番号）」を取得するVB.NETのコードです。
　※ 非表示の列名／列番号は含まない
　※ 匿名関数「func_ConvColumNumberToName」で、列番号を列名に変換しています

```VB
'// -------------------------------------------
'// 関数定義：列番号を列名に変換
'// -------------------------------------------
Dim func_ConvColumNumberToName As Func(Of Integer, String) = _
	Function(columnIndex As Integer) As String
		Dim colLetter As String = String.Empty
		Dim modNum As Integer = 0
		Dim divEnd As Integer = columnIndex
		While divEnd > 0
			modNum = (divEnd - 1) Mod 26 '// 26 = A-Z
			colLetter = Chr(65 + modNum) & colLetter '// char(65) = A
			divEnd = CInt((divEnd - modNum) \ 26)
		End While
		Return colLetter
	End Function
'// -------------------------------------------

'// 対象エクセルファイルのパス・シート名・最大取得列
Dim bookPath As String = "C:\temp\exceltest.xlsx"
Dim sheetName As String = "Sheet1"
Dim columnNumberMax As Integer = 99

Dim columnNameList As New List(Of String)
Dim ap As New Microsoft.Office.Interop.Excel.Application
Dim wb As Microsoft.Office.Interop.Excel.Workbook
Dim ws As Microsoft.Office.Interop.Excel.Worksheet
Try
	'// ブックを開く
	wb = ap.Workbooks.Open(IO.path.GetFullPath(bookPath))
	'// シートループ
	For idx As Integer = 1 To wb.Sheets.Count
		ws =  CType(wb.Sheets(idx), Microsoft.Office.Interop.Excel.Worksheet)
		If ws.Name = sheetName Then
			'// 列ループ
			For idxCol As Integer = 1 To columnNumberMax
				'// 表示状態を確認して、リストに格納
				If Not(CBool(
					ws.Range(ws.Cells(1, idxCol), ws.Cells(1, idxCol)).EntireColumn.Hidden)) Then
					columnNameList.Add(func_ConvColumNumberToName(idxCol))
				End If
			Next
			Exit For
		End If
	Next
	wb.Close
Catch ex As Exception
	Console.writeLine(ex.ToString)
	Throw(ex)
Finally
	'// ブックを閉じる
	If Not(wb Is Nothing) Then wb = Nothing
	ap = Nothing
End Try
```

呼び出し可能なxamlファイルにしたものを用意しました。以下からダウンロードできます。
[[github] GetVisibleRowNumberList.xaml]( https://github.com/miyag/UiPath.ReusableCode/blob/master/ExcelGetVisibleProperty/GetVisibleRowNumberList.xaml)

引数は以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/694d215b-cc70-a5ea-4581-9a640f7e83c0.png)
※ in_columnNumberMax は「取得する列番号の最大値」指定です。


# .NETで、表示中の行番号のみを取得する

以下は、Excelシート内で「表示中の行番号」を取得するVB.NETのコードです。
　※ 非表示の行番号は含まれません

```
'// 対象エクセルファイルのパス・シート名・最大取得行を指定
Dim bookPath As String = "C:\temp\exceltest.xlsx"
Dim sheetName As String = "Sheet1"
Dim rowNumberMax As Integer = 99

'// ファイルを読み込み
Dim rowNumberList As New List(Of String)
Dim ap As New Microsoft.Office.Interop.Excel.Application
Dim wb As Microsoft.Office.Interop.Excel.Workbook
Dim ws As Microsoft.Office.Interop.Excel.Worksheet
Try
	'// ブックを開く
	wb = ap.Workbooks.Open(IO.path.GetFullPath(bookPath))
	'// シートループ
	For idx As Integer = 1 To wb.Sheets.Count
		ws =  CType(wb.Sheets(idx), Microsoft.Office.Interop.Excel.Worksheet)
		If ws.Name = sheetName Then
			'// 行ループ
			For idxRow As Integer = 1 To rowNumberMax
				'// 表示状態を確認して、リストに格納
				If Not(CBool(
					ws.Range(ws.Cells(idxRow,1), ws.Cells(idxRow,1)).EntireRow.Hidden)) Then
					rowNumberList.Add(idxRow.ToString)
				End If
			Next
			Exit For
		End If
	Next
	wb.Close
Catch ex As Exception
	Console.writeLine(ex.ToString)
	Throw(ex)
Finally
	'// ブックを閉じる
	If Not(wb Is Nothing) Then wb = Nothing
	ap = Nothing
End Try
```

呼び出し可能なxamlファイルにしたものを用意しました。以下からダウンロードできます。
[[github] GetVisibleRowNumberList.xaml]
(https://github.com/miyag/UiPath.ReusableCode/blob/master/ExcelGetVisibleProperty/GetVisibleRowNumberList.xaml)

引数は以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/fefd0e3b-1dae-8116-e09f-982cc9d4d64b.png)
※ in_rowNumberMax は「取得する列番号の最大値」指定です。

# おわりに

シート・列・行で「非表示分は取得したくない」という時に、引き出しの一つとして、役に立てばと思います。
最後までお読みいただき、ありがとうございました。