忍者ブログ

www.oka-da.com blog

新潟県妙高市在住。

Apache NPOIでExcelセルに斜め罫線を引く方法

Excel帳票のオートメーションを高速化するために、VB.netとApache NPOI 2.5.1でプログラムを書いてみました。
 セルに斜め罫線を引く方法をネットで調べましたが、VB.netの文法に対応していない複雑な方法によるサンプルプログラムが公開されているだけで、「実現は不可能」という結論でした。
 ところが、私が挑戦してみたところ、簡単にできました。

<ソースリスト>
'Excel97Book (.xls) 斜め罫線を引く サンプル
'Copyright (c) 2020 oka-da.com

'NPOI Ver.2.5.1.0 を使用
Imports NPOI.HSSF.UserModel
Imports NPOI.SS.UserModel
Imports NPOI.SS.Util
'(HSSF EXCEL97用   HSSFCell
'(XSSF EXCEL2007用 XSSFCell


Class MainWindow
    Private Property XlsFileName As String = "C:\Users\o\Desktop\x1.xls"
    Private Property NPOI_book As IWorkbook = Nothing

    Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Open_xlsFile(NPOI_book, XlsFileName)
        Dim sheet_A1 As ISheet = NPOI_book.GetSheet("Sheet1")

        'Excel VBA rangeのシミュレート
        Dim rg As New POI_Range(NPOI_book, sheet_A1)
        'セル座標をA1に設定する
        rg.SetAddr("A1")
        rg.draw斜罫線(BorderDiagonal.Both, BorderStyle.Thick, IndexedColors.Tan)

        '再計算を行う ■これをPOI側でやっておかないと非常に危険、Excel側でどんなに再計算を行っても計算されないことがある
        NPOI_book.GetCreationHelper().CreateFormulaEvaluator().EvaluateAll()

        SaveBook(XlsFileName)
        'Excelを閉じる
        NPOI_book.Close()
        NPOI_book = Nothing

        'Excelファイルを閲覧
        Dim p As System.Diagnostics.Process = System.Diagnostics.Process.Start(XlsFileName)
        Me.Close()

    End Sub


    'ExcelファイルをNPOIで開く
    Private Sub Open_xlsFile(ByRef book As IWorkbook, ByVal fs As String)
        Try
            book = WorkbookFactory.Create(fs)
        Catch ex As Exception

        End Try

    End Sub


    'Excelファイルを保存する
    Private Sub SaveBook(ByVal filePath As String)
        Using fs As New System.IO.FileStream(filePath, IO.FileMode.Create)
            NPOI_book.Write(fs)
        End Using
    End Sub


    Public Class NpoiLib
        Public Shared Property xls列名 As String() = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV", "AW"}
    End Class


    'POIでrangeをシミュレートする
    Public Class POI_Range
        Private Property _x0 As Integer 'セルを示す座標X (列)
        Private Property _y0 As Integer 'セルを示す座標Y (行)
        Private Property NPOI_book As IWorkbook
        Private Property _sheet As ISheet

        Public Sub New(ByRef b As IWorkbook, ByRef s As ISheet)
            NPOI_book = b
            _sheet = s
        End Sub

        '罫線を引く
        Public Sub draw斜罫線(ByVal 斜め形状 As NPOI.SS.UserModel.BorderDiagonal,
                        ByVal thickness As NPOI.SS.UserModel.BorderStyle,
                        ByVal color1 As NPOI.SS.UserModel.IndexedColors)

            Dim style = NPOI_book.CreateCellStyle()
            style.BorderDiagonalLineStyle = thickness
            style.BorderDiagonal = 斜め形状
            style.BorderDiagonalColor = color1.Index

            setStyle(style)

        End Sub

        'セル書式を設定する
        Public Sub setStyle(ByVal style As ICellStyle)
            Dim row = If(_sheet.GetRow(_y0 - 1), _sheet.CreateRow(_y0 - 1))
            Dim cell1 = If(row.GetCell(_x0 - 1), row.CreateCell(_x0 - 1))

            cell1.CellStyle = style

        End Sub

        '定義されたセル名,またはR1C1形式によりrangeの指す座標を設定する
        Public Sub SetAddr(ByVal cs As String)
            Dim add1 As CellRangeAddress
            Dim ina1 As IName = NPOI_book.GetName(cs)

            If ina1 IsNot Nothing Then
                add1 = CellRangeAddress.ValueOf(ina1.RefersToFormula())
                _x0 = add1.FirstColumn + 1
                _y0 = add1.FirstRow + 1

                Exit Sub
            End If


            Dim s As String = cs
            Dim c As Char

            Dim i, j As Integer

            For i = 0 To cs.Length - 1
                c = cs.Substring(i, 1)
                j = Convert.ToInt32(c) 'ASCIIコード
                If j < &H41 Then
                    'csから数字以降の文字列をIntegerに変換、_y0に代入
                    If Integer.TryParse(cs.Substring(i), _y0) Then
                    Else
                        _y0 = 0
                    End If

                    Exit For
                End If
            Next

            'CSのアルファベット部分を配列要素内からクエリー
            If i > 0 Then
                s = cs.Substring(0, i)
                For i = 0 To NpoiLib.xls列名.Length - 1

                    If s = NpoiLib.xls列名(i) Then
                        _x0 = i + 1
                        GoTo L100
                    End If
                Next

            End If
            _x0 = 0
L100:
            If _x0 < 1 Or _y0 < 1 Then Exit Sub
        End Sub

    End Class


End Class


<実行結果>


 従来、Excelオートメーションで行っていた処理を全てNPOIに置換しようと試みましたが、不可能なこともあります。
(1)行削除が用意されておらず、行をシフトする方法で代用すると書式が崩れる。
(2)セルの書式設定は一言で表現すると、かなり複雑。特定の行・列全体を書式も含めてコピー・貼り付けするのは避けた方が無難。
サンプルプログラムでは、Excel VBAのrangeクラスをシミュレートしています。まだ開発途中なので一部を公開しただけですが、Offsetによる参照の移動を実装すると、プログラムが楽になります。

オカダ・システムエンジニアリング研究所


拍手[0回]

PR

コメント