空腹おやじのログと備忘録

VBA(主にExcel)でいろいろな実験的な事とか、Linuxのコマンドとか設定とかについて忘れないように、あれこれと・・・

DictionaryのItemに格納したExcelのRangeは更新可能か?

以前の記事でDictionaryのItemに配列を格納して、後から更新しようとして出来なかったが、懲りずに今度はRangeを格納して試してみた。

z1000s.hatenablog.com

結論から言うと、今回は更新可能でした!!!

  1. DictionaryのItemを更新することで、ワークシートの値も更新されます。
  2. また、ワークシートの対象Rangeのセルの値を更新すると、DictionaryのItemを参照しても更新された値を取得できます。

サンプルコード

Public Sub dictionaryItemUpdate()

    Const TARGET_SHEET_NAME As String = "Sample"
    Const KEY_ROW           As Long = 2
    Const TARGET_COLUMNS    As Long = 6

    Dim dicRange    As Dictionary
    Dim sLineBefore As String
    Dim sLine       As String
    Dim i           As Long

    Set dicRange = New Dictionary

    With Worksheets(TARGET_SHEET_NAME)
        '現在のセルのデータを取得、表示用に結合
        For i = 1 To TARGET_COLUMNS
            sLineBefore = sLineBefore & vbTab & .Cells(KEY_ROW, i).Value
        Next i

        '現在のセルのデータを表示
        Debug.Print "基準となるセルデータ"
        Debug.Print Mid$(sLineBefore, 2) & vbCrLf

        '対象となるRangeをDictionaryに追加
        dicRange.Add 2, .Range(.Cells(KEY_ROW, 1), .Cells(KEY_ROW, TARGET_COLUMNS))

        'Dictionaryに格納されているRangeから、データを取得、表示用に結合
        For i = 1 To TARGET_COLUMNS
            sLine = sLine & vbTab & dicRange(KEY_ROW).Parent.Cells(KEY_ROW, i).Value
        Next i

        'Dictionaryに格納されているデータを表示
        Debug.Print "Dictionaryに格納されているデータ(更新前)"
        Debug.Print Mid$(sLine, 2) & vbCrLf

        '格納したRange内のセルの値を更新
        Debug.Print "ワークシートの値を更新"
        Debug.Print "C2(更新前):" & .Cells(KEY_ROW, 3).Value
        .Cells(2, 3).Value = "----- " & .Cells(KEY_ROW, 3).Value & " -----"
        Debug.Print "C2(更新後) :" & .Cells(KEY_ROW, 3).Value & vbCrLf

        'DictionaryのItemを更新
        Debug.Print "DictionaryのItemを更新"
        Debug.Print "D2セル相当の値を更新(更新前):" & dicRange(KEY_ROW).Parent.Cells(KEY_ROW, 4).Value
        dicRange(KEY_ROW).Parent.Cells(KEY_ROW, 4).Value = "+++++ " & dicRange(KEY_ROW).Parent.Cells(KEY_ROW, 4).Value & " +++++"
        Debug.Print "D2セル相当の値を更新(更新後):" & dicRange(KEY_ROW).Parent.Cells(KEY_ROW, 4).Value & vbCrLf

        sLine = ""

        'Dictionaryに格納されているRangeから、データを取得、表示用に結合
        For i = 1 To TARGET_COLUMNS
            sLine = sLine & vbTab & dicRange(KEY_ROW).Parent.Cells(KEY_ROW, i).Value
'            sLine = sLine & vbTab & dicRange(KEY_ROW).Cells(1, i).Value
        Next i

        Debug.Print "Dictionaryに格納されているデータ(更新後)"
        Debug.Print "更新前:" & Mid$(sLineBefore, 2)
        Debug.Print "更新後:" & Mid$(sLine, 2)

        sLine = ""

        '最終のセルのデータを取得、表示用に結合
        For i = 1 To TARGET_COLUMNS
            sLine = sLine & vbTab & .Cells(KEY_ROW, i).Value
        Next i

        '最終のセルのデータを表示
        Debug.Print "最終のセルデータ"
        Debug.Print Mid$(sLine, 2) & vbCrLf
    End With

End Sub

サンプルコードを試すときは、参照設定忘れると動きませんので・・・

実行結果

call dictionaryItemUpdate
基準となるセルデータ
1 4820831 1256948 AFPKAUNYAI gKoo0fpjSf ゑたさえんぐそつぢろ


Dictionaryに格納されているデータ(更新前)
1 4820831 1256948 AFPKAUNYAI gKoo0fpjSf ゑたさえんぐそつぢろ


ワークシートの値を変更
C2(更新前):1256948
C2(更新後) :----- 1256948 -----


DictionaryのItemを更新
D2セル相当の値を更新(更新前):AFPKAUNYAI
D2セル相当の値を更新(更新後):+++++ AFPKAUNYAI +++++


Dictionaryに格納されているデータ(更新後)
更新前:1 4820831 1256948 AFPKAUNYAI gKoo0fpjSf ゑたさえんぐそつぢろ
更新後:1 4820831 ----- 1256948 ----- +++++ AFPKAUNYAI +++++ gKoo0fpjSf ゑたさえんぐそつぢろ
最終のセルデータ
1 4820831 ----- 1256948 ----- +++++ AFPKAUNYAI +++++ gKoo0fpjSf ゑたさえんぐそつぢろ

ポイント

ItemとしてRangeを格納したので、dicRange(KEY_ROW)がRangeを返します。
なので、セルの値は、

dicRange(KEY_ROW).Parent.Cells(KEY_ROW, i).Value

のように取得していますが、Parentの有無でCellsのアドレス指定が変わってきます。
今回の例でC2のセルの値を取得したい場合、

Parentがある場合 dicRange(KEY_ROW).Parent.Cells(2, 3).Value
Parentがない場合 dicRange(KEY_ROW).Cells(1, 3).Value

と指定する必要があります。
Parentがある場合には、普段使用している行、列の指定となります。
Rangeに対するParent(親)なので、ParentはWorksheetとなるためです。

? typename(dicRange(KEY_ROW).Parent)
Worksheet

として確かめることが出来ます。

一方Parentがない場合には、
ワークシート全体で見れば2行目のセルですが、Dictionaryに格納したRangeの左上のセル(A2:ワークシート基準でのCells(2,1))を1行目、1列目とみなした相対的な行、列として指定する必要があります。従って、同じ2行目なので行を「1」としなければいけません。

今回のサンプルでは、A列から始まるRangeを使用したのですが、B列以降から始まるRangeを指定した場合には、列であっても同様のことが言えます。

Parentの有無、どちらの方法でも出来ますので、どのような処理を行うかによって使い分ければよいかと思います。

最後に

うまく使えばデータベースのように、キーを指定してレコードを取得するといった使い方とかに応用できそう?
更新もできるし・・・
あとは、背景の塗りつぶしも出来ましたので、一通りのことは出来るのではないかと思われます。