VB.NET は今も現役です。業務ツール、社内ダッシュボード、データ入力フォームなど、エンタープライズ向けデスクトップソフトウェアの多くは今も VB.NET で書かれており、その多くが Excel とのデータ連携を必要としています。本記事では実践的なアプローチを 1 か所にまとめます — ワークブックの読み込み、コードからの生成、そして WinForms / WPF フォームへのライブビューアの埋め込みです。

いずれのアプローチも Excel のインストールは不要で、.NET が動作するあらゆるマシンで使用できます。


まとめ: 用途別ライブラリ選定表

やりたいこと推奨ライブラリ
.xlsx からセル値を読み込むClosedXML または ReoGrid
.xlsx レポートを生成する(UI なし)ClosedXML
WinForms フォームに .xlsx を表示するReoGrid(WinForms パッケージ)
WPF ウィンドウに .xlsx を表示するReoGrid(WPF パッケージ)
ユーザーに編集・保存させるReoGrid
レガシー .xls(Excel 97〜2003)NPOI

ClosedXML はサーバーサイドやヘッドレスの処理に最適です。ReoGrid はスプレッドシートをアプリの UI 内に表示する場合に最適です。両者は排他ではありませんが、多くのケースでどちらか一方を選べば事足ります。


VB.NET で Excel データを読み込む

ClosedXML を使う

NuGet でインストール:

PM> Install-Package ClosedXML

最初のワークシートからすべての行を読み込む:

Imports ClosedXML.Excel

Sub ReadWorkbook(filePath As String)
    Using workbook As New XLWorkbook(filePath)
        Dim ws = workbook.Worksheet(1)

        For Each row In ws.RowsUsed()
            For Each cell In row.CellsUsed()
                Console.Write(cell.Value.ToString() & vbTab)
            Next
            Console.WriteLine()
        Next
    End Using
End Sub

RowsUsed() は空行をスキップします。CellsUsed() は各行の空セルをスキップします — 空の範囲を走査せずに済みます。

名前付き範囲を読み込む

ワークブックで名前付き範囲(例: SalesData)を使用している場合、行・列番号をハードコードせずに直接ターゲットにできます。

Imports ClosedXML.Excel

Sub ReadNamedRange(filePath As String, rangeName As String)
    Using workbook As New XLWorkbook(filePath)
        Dim namedRange = workbook.NamedRange(rangeName)
        Dim cells = namedRange.Ranges.CellsUsed()

        For Each cell In cells
            Console.WriteLine($"{cell.Address}: {cell.Value}")
        Next
    End Using
End Sub

ReoGrid を使う(UI なしのパス)

ReoGrid は UI を表示しなくても、ワークブックを開いて読み込めます — インポートパイプラインに使えます。

Imports unvell.ReoGrid
Imports unvell.ReoGrid.IO

Sub ReadWithReoGrid(filePath As String)
    Dim workbook As New ReoGridControl()
    workbook.Load(filePath)

    Dim ws = workbook.CurrentWorksheet

    For r As Integer = 0 To ws.MaxContentRow
        For c As Integer = 0 To ws.MaxContentCol
            Dim cellData = ws.GetCellData(r, c)
            Console.Write(If(cellData IsNot Nothing, cellData.ToString(), "") & vbTab)
        Next
        Console.WriteLine()
    Next
End Sub

データも表示も必要な場合、このアプローチなら ClosedXML は不要です — 1 つのライブラリで IO と UI の両方を処理できます。


VB.NET で Excel ファイルを書き出す

ゼロからワークブックを生成する

Imports ClosedXML.Excel

Sub GenerateReport(outputPath As String, data As DataTable)
    Using workbook As New XLWorkbook()
        Dim ws = workbook.Worksheets.Add("Report")

        ' ヘッダー行
        For c As Integer = 0 To data.Columns.Count - 1
            Dim cell = ws.Cell(1, c + 1)
            cell.Value = data.Columns(c).ColumnName
            cell.Style.Font.Bold = True
            cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#4472C4")
            cell.Style.Font.FontColor = XLColor.White
        Next

        ' データ行
        For r As Integer = 0 To data.Rows.Count - 1
            For c As Integer = 0 To data.Columns.Count - 1
                ws.Cell(r + 2, c + 1).Value = XLCellValue.FromObject(data.Rows(r)(c))
            Next
        Next

        ws.Columns().AdjustToContents()
        workbook.SaveAs(outputPath)
    End Using
End Sub

AdjustToContents() はすべての列を自動的に幅調整します — 生成した Excel ファイルの列幅が常に狭すぎるという定番の不満を防ぐ小さな配慮です。

ストリームへの書き出し(ダウンロードやメール添付用)

データベース、API、メールクライアントは多くの場合ファイルパスではなくバイト列を要求します。

Imports ClosedXML.Excel

Function GenerateToStream(data As DataTable) As Byte()
    Using workbook As New XLWorkbook()
        Dim ws = workbook.Worksheets.Add("Data")
        ws.Cell(1, 1).InsertTable(data)

        Using ms As New MemoryStream()
            workbook.SaveAs(ms)
            Return ms.ToArray()
        End Using
    End Using
End Function

InsertTable は ClosedXML のショートカットで、DataTable を 1 行で Excel テーブル(オートフィルター・縞模様の行・縞模様の列付き)として書き込みます。


WinForms に Excel ファイルを表示する(VB.NET)

WinForms フォームで .xlsx を表示・編集するのに適したツールはスプレッドシートコントロールです — DataGridView ではありません。DataGridView はフラットなレコードを扱うコントロールで、実際の .xlsx ファイルが持つ結合セル・数式の再計算・複数シート・セル書式を理解しません。

1. WinForms パッケージのインストール:

PM> Install-Package unvell.ReoGrid.dll

2. フォームに ReoGridControl を追加 してから、開く・保存のボタンを実装する:

Imports unvell.ReoGrid

Public Class MainForm

    Private Sub OpenButton_Click(sender As Object, e As EventArgs) Handles OpenButton.Click
        Using dlg As New OpenFileDialog()
            dlg.Filter = "Excel Workbook|*.xlsx"
            If dlg.ShowDialog() <> DialogResult.OK Then Return
            ReoGridControl1.Load(dlg.FileName)
        End Using
    End Sub

    Private Sub SaveButton_Click(sender As Object, e As EventArgs) Handles SaveButton.Click
        Using dlg As New SaveFileDialog()
            dlg.Filter = "Excel Workbook|*.xlsx"
            If dlg.ShowDialog() <> DialogResult.OK Then Return
            ReoGridControl1.Save(dlg.FileName)
        End Using
    End Sub

End Class

実装はこれだけです。ファイルを読み込めば、ユーザーはフォント・塗りつぶし・罫線がそのまま反映された完全なワークブックを確認し、セルをクリックして入力し、下部のシートタブで切り替え、保存できます。

読み取り専用モード

ファイルをプレビューするだけで編集させたくない場合:

For Each ws In ReoGridControl1.Worksheets
    ws.SetSettings(WorksheetSettings.Edit_Readonly, True)
Next

書式・数式・フォントはそのまま描画しつつ、セルへの入力はできなくなります。

ストリームやバイト配列から読み込む

ファイルがディスク上でなく、データベースの BLOB や HTTP レスポンスから来る場合:

Imports unvell.ReoGrid
Imports unvell.ReoGrid.IO

' バイト配列から(例: DB カラム)
Dim xlsxBytes As Byte() = GetBytesFromDatabase()
Using ms As New MemoryStream(xlsxBytes)
    ReoGridControl1.Load(ms, FileFormat.Excel2007)
End Using

' HTTP レスポンスから
Dim stream As Stream = Await httpClient.GetStreamAsync(reportUrl)
ReoGridControl1.Load(stream, FileFormat.Excel2007)

WPF に Excel ファイルを表示する(VB.NET)

WPF パッケージと WinForms パッケージは同じ API を持っています。変わるのはインストールする NuGet パッケージとコントロールの宣言方法だけです。

1. WPF パッケージのインストール:

PM> Install-Package unvell.ReoGridWPF.dll

2. XAML でコントロールを宣言:

<Window x:Class="ExcelViewerWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:rg="clr-namespace:unvell.ReoGrid;assembly=unvell.ReoGrid"
        Title="Excel Viewer" Height="600" Width="900">

    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Content="Open" Click="OpenButton_Click"/>
            <Button Content="Save" Click="SaveButton_Click"/>
        </ToolBar>
        <rg:ReoGridControl x:Name="Grid"/>
    </DockPanel>
</Window>

3. VB.NET のコードビハインド:

Imports Microsoft.Win32
Imports unvell.ReoGrid

Class MainWindow

    Private Sub OpenButton_Click(sender As Object, e As RoutedEventArgs)
        Dim dlg As New OpenFileDialog With {.Filter = "Excel Workbook|*.xlsx"}
        If dlg.ShowDialog() <> True Then Return
        Grid.Load(dlg.FileName)
    End Sub

    Private Sub SaveButton_Click(sender As Object, e As RoutedEventArgs)
        Dim dlg As New SaveFileDialog With {.Filter = "Excel Workbook|*.xlsx"}
        If dlg.ShowDialog() <> True Then Return
        Grid.Save(dlg.FileName)
    End Sub

End Class

WPF コントロールも WinForms と同様に、複数ワークシート・数式・書式設定・ウィンドウ枠の固定・条件付き書式をサポートしています。


変更の検知

「未保存の変更があります — 終了してよいですか?」ダイアログには、2 つのイベントでほぼすべてのケースをカバーできます。

' セル値が編集されたとき
AddHandler ReoGridControl1.CurrentWorksheet.CellDataChanged,
    Sub(s, args)
        isDirty = True
        Text = "My App *"   ' タイトルバーのアスタリスク = 一般的な慣習
    End Sub

' 保存後にダーティフラグをリセット
AddHandler ReoGridControl1.WorkbookSaved,
    Sub(s, args)
        isDirty = False
        Text = "My App"
    End Sub

なぜ Office Interop を使わないのか

短くまとめると: Microsoft.Office.Interop.Excel は実際の Excel プロセスを COM 経由で自動操作します。ユーザーのマシンすべてに Office のインストールが必要で、サービス上での使用を Microsoft が明示的に非推奨としており、フォームに埋め込んだコントロールではなく別の Excel ウィンドウが表示され、エラー発生時にプロセスが残留します。自己完結型の VB.NET デスクトップアプリには、ファイル形式を直接読み書きするインプロセスのライブラリが適しています。

Office Interopインプロセスライブラリ
Excel のインストール必須はいいいえ
UI をフォームに埋め込みいいえ(別 Excel ウィンドウ)はい
Office なしのマシンで動作いいえはい
処理速度遅い(COM マーシャリング)速い
エラーハンドリングCOM 例外通常の .NET 例外

まとめ: シンプルなインポートフォーム

よくある VB.NET のシナリオ: .xlsx を選択し、グリッドで確認してから「インポート」をクリックしてデータベースに取り込むフォームです。

Imports unvell.ReoGrid

Public Class ImportForm

    Private Sub BrowseButton_Click(sender As Object, e As EventArgs) Handles BrowseButton.Click
        Using dlg As New OpenFileDialog()
            dlg.Filter = "Excel Workbook|*.xlsx"
            If dlg.ShowDialog() <> DialogResult.OK Then Return

            ReoGridControl1.Load(dlg.FileName)

            ' 編集ロック — これはエディタではなくプレビューです
            For Each ws In ReoGridControl1.Worksheets
                ws.SetSettings(WorksheetSettings.Edit_Readonly, True)
            Next

            ImportButton.Enabled = True
        End Using
    End Sub

    Private Sub ImportButton_Click(sender As Object, e As EventArgs) Handles ImportButton.Click
        Dim ws = ReoGridControl1.CurrentWorksheet

        For r As Integer = 0 To ws.MaxContentRow
            Dim id     = ws.GetCellData(r, 0)?.ToString()
            Dim name   = ws.GetCellData(r, 1)?.ToString()
            Dim amount = ws.GetCellData(r, 2)

            If String.IsNullOrWhiteSpace(id) Then Continue For
            InsertRow(id, name, amount)
        Next

        MessageBox.Show("Import complete.")
    End Sub

    Private Sub InsertRow(id As String, name As String, amount As Object)
        ' ... DB 挿入ロジックをここに
    End Sub

End Class

このフォームによりユーザーは確定前に何がインポートされるかを正確に確認できます — どのシートが読まれたか、どの行がスキップされたか、といった混乱が生じません。


次に読むもの