お絵かきロジックアナライザ開発記   その4

シートサイズ設定機能はこのくらいで一段落させ、ロジックシートの見栄えなどに手を加えていきましょう。ワークシートの書式設定等を操作する手順がメインとなるため、Excelのマクロ記録機能である程度自動記録させ、そこに手を加える形でコーディングを進めるのもいい手です。

プロシージャ間での変数の共用

現状ではSetFieldSizeフォームのOKボタンクリックイベントであるOkClick_Clickプロシージャに、罫線でロジックシートを作成するコードが書かれています。今後は他の処理との連携なども考えていかなければならないので、シート作成処理本体は標準モジュールに置き、OKクリックイベントからは標準モジュールにあるロジックシート作成プロシージャを呼出すという構造にしていきます。また変数に保存する値(縦横サイズなど)も、標準モジュール内に置かれる他のプロシージャで利用できるよう、ローカル変数のグローバル化が必要になります。

変数の有効範囲

ここで変数の有効範囲についてまとめておきましょう。変数はDimステートメントなどで宣言された場所により、他のプロシージャでも利用できるものとそうでないものとに分けられます。プロシージャ内で宣言された変数は、そのプロシージャ内のみでしか利用できない、ローカルな変数となります。これに対してモジュールの宣言部(プロシージャの外)で宣言されたものは、同一モジュール内であれば他のプロシージャからも利用できる、グローバルな変数となります。次の例を見て下さい。


Option Explicit
Dim TestVar0 As String                            'モジュール内の全プロシージャで利用可能

Sub Test0()
  TestVar0 = "Test Message 0"
  MsgBox TestVar0
End Sub

Sub Test1()
  Dim TestVar1 As String                          'Test1プロシージャ内でのみ利用可能
  TestVar1 = "Test Message 1"
  MsgBox TestVar0
  MsgBox TestVar1
End Sub

Sub Test2()
  Dim TestVar2 As String                          'Test2プロシージャ内でのみ利用可能
  TestVar2 = "Test Message 2"
  MsgBox TestVar0
  MsgBox TestVar1                                 '←ここでエラー
  MsgBox TestVar2
End Sub

上記コードは標準モジュールの同一モジュール内に作成して下さい。検証用のプログラムなので、新規ファイルを作成してテストするのが良いでしょう。Test0Test1Test2の順に実行すると、Test0ではグローバル変数であるTestVar0への代入と参照を行っており、メッセージ表示されます。Test0ではローカル変数であるTestVar1に対する処理が加わりますが、問題なく実行されます。これらに対しTest2では、プロシージャ内で宣言されていないTestVar1への参照が含まれており(MsgBox TestVar1の部分)、実行エラーが発生します。

次にTest0実行後にVBEのメニューで[実行]→[リセット]をクリックしてからTest1を実行してみて下さい。エラーにはなりませんがメッセージボックスに文字列が表示されません。変数の内容はリセット実行により初期化されてしまうコトが解ります。

ではモジュール内でどのプロシージャからも参照可能なTestVar0はどの程度の範囲まで有効なのかを検証しましょう。標準モジュール内に新たなモジュールを挿入し(先程のモジュール名がModule1ならModule2など)、以下のプロシージャを追加してみます。


Sub Test3()
  TestVar0 = "Test Message 0"                     '←ここでエラー
  MsgBox TestVar0
End Sub

Test3を実行すると変数が未定義でエラーになります。つまり宣言部で定義した変数でも、他のモジュールからは有効にならないということです。マクロの規模が大きくなると、機能や処理内容によって標準モジュール群の中に複数のモジュールが存在するような状況も発生し得ます(ちなみにオリジナルの「お絵かきロジックアナライザ」には複数モジュールが存在します)。このように異なるモジュールに属するプロシージャからも参照したい変数の場合、DimステートメントではなくPublicステートメントで変数定義しなければなりません。Module1の宣言部を以下のように書換えてみて下さい。


Option Explicit
Public TestVar0 As String                         '全モジュールの全プロシージャで利用可能

この状態でTest3を実行するとエラーは発生しません。Publicで宣言された変数は複数のモジュールをまたいでグローバルな変数となったことが分りますます。VBAではプロシージャ内でしか有効でない変数を‘プロシージャレベル変数’と呼び、特定モジュール内で有効な変数を‘モジュールレベル変数’、複数モジュールで有効な変数を‘パブリック変数’と呼びます。

それでは‘パブリック変数’はどこまでグローバルなのでしょうか。試しにThisWorkbookモジュールでSheetBeforeRightClickイベントプロシージャを追加してみましょう。


Private Sub Workbook_SheetBeforeRightClick _
  (ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
  TestVar0 = "Test Message 0"
  MsgBox TestVar0
End Sub

ワークシート上で右クリックするとイベントが発生し、SheetBeforeRightClickイベントプロシージャが実行されますがエラーは発生しません。宣言部にPublicで変数宣言しておけばブックモジュールやシートモジュールからも共用が可能となります。逆に、例え宣言部で定義してもPublic宣言していない変数は、他のモジュールからはグローバル変数扱いされないことが理解できます。

「変数の宣言を強制する」としたワケ

冒頭でVBEの[ツール]→[オプション]で「変数の宣言を強制する」をONにし、モジュール先頭に‘Option Explicit’が付加されるようにした理由がここにあります。このチェックマークがないと、VBAはキーワードに当てはまらない文字列を発見したとき「新しい変数だな」と理解して、勝手に定義してくれます。ある意味便利なのですが、特にタイプミスにより変数名が違ってしまったときなど、意図しない変数が作られる場合があり、実行時にエラーは発生しないものの思い通りの結果が得られません。「プログラム構造は間違いないのにどうして?」デバッグしててもタイプミスであることに気付かず、ドツボにはまってしまうと途中でイヤになってしまうことも多々あります。

「変数の宣言を強制する」がONなら未定義文字列出現時にエラーが発生しますので、このような事態が防げるワケです。

Microsoftが将来やるコトを感じ取る

現在はキーワードとして定義されていない文字列でもVBAの将来的なバージョンアップによりステートメントやプロパティ、メソッドを表すキーワードとして定義されることもあり得ます。作成中のマクロを長く使いたいならこのような将来的なキーワードも、今使っている変数名とカブらないようにしたいものです。Microsoftがいかにも使わなさそうな文字列にしたいところですがこればかりはある意味防ぎようがありません。

このような心配を一番簡単に解決する方法は、変数名を日本語表記してしまうことです。全角文字の変数にしたり、半角文字でもローマ字表記にするなど、日本語を基にした変数定義を行えば、将来的にもまず間違いなくカブることはありません。

また略語表記するのも一つの手です。どうやらVBAキーワードには略語表記が非常に少ないようです。「幅」を示す‘Width’を‘Wd’、「高さ」を示す‘Height’を‘Ht’と略すことで、VBAの“クセ”を逆手に取れるような雰囲気があります。

ロジックシートデザイン

プロシージャ間で変数を共用する方法が解ったところで、実際のロジックシートデザインに着手していきます。ロジックシートを罫線で作成する点は変らないのですが、5マスごとの太線化や場所ごとの色分けなど、視認性の向上を目指した構成に変更していきます。

前述の変数管理については、今のところ各プロシージャを複数モジュールに分けるような複雑な制御を想定していないので、モジュール宣言部でのDimステートメントによるグローバル変数化としておきます。当然ながら標準モジュール群における変数定義がブックモジュールやシートモジュールから参照できないことになるので、それなりの対処が必要になってきます。

新規シート作成時に取得したフィールドサイズ等を分析時などでも利用したいので、関連するプロシージャは変数が共用できる同一モジュール内に収めようというコトでロジックシート作成処理部を標準モジュールに引越すことにしました。またここで作成するコードはかなりボリュームのあるプロシージャになりそうなので、その意味でもフォームのイベントプロシージャ内に押し込んだままというのは、少々難があります。

標準モジュールへの引越し

先ずは標準モジュールに以下のプロシージャを追加して下さい。内容としてはSetFieldSizeフォームのOKクリックイベントにあったシート作成部分を、標準モジュールに移し替えただけです。ただし、今までMeキーワードで代用できたフォームオブジェクトは、キチンとユーザーフォーム名で指定しなければならなくなります。OKクリックイベントでは、作成したシート作成プロシージャを呼出すようにします。


Sub NewLogicSheet()
' 新規ロジックシート作成
  Dim FieldX As Integer                           'フィールドサイズ
  Dim FieldY As Integer
  Dim HintX As Integer                            'ヒントサイズ
  Dim HintY As Integer
  FieldX = SetFieldSize.FieldWidth.Value          'フィールドサイズ取得
  FieldY = SetFieldSize.FieldHeight.Value
  HintX = (FieldX + 1) \ 2                        'ヒントサイズ算出
  HintY = (FieldY + 1) \ 2
  Unload SetFieldSize                             'フォーム消去
  Cells.Delete                                    '編集領域クリア
  With Cells
    .RowHeight = 12                               '行高/列幅調整
    .ColumnWidth = 1.4
  End With
  Range(Cells(HintY + 1, 1), Cells(HintY + FieldY, HintX)) _
    .Borders.LineStyle = xlContinuous             '水平ヒントエリア
  Range(Cells(1, HintX + 1), Cells(HintY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             '垂直ヒントエリア
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             'フィールドエリア
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .BorderAround , xlMedium
End Sub

Private Sub OkClick_Click()
' OKクリックイベント
  With Me
    If FieldWidth.Text = vbNullString Then        '横サイズ欄チェック
      .FieldWidth.SetFocus
      Exit Sub
    End If
    If FieldWidth.Text = vbNullString Then        '縦サイズ欄チェック
      .FieldHeight.SetFocus
      Exit Sub
    End If
  End With
  Dim FieldX As Byte                              'フィールドサイズ
  Dim FieldY As Byte
  Dim HintX As Byte                               'ヒントサイズ
  Dim HintY As Byte
  FieldX = Me.FieldWidth.Value                    'フィールドサイズ取得
  FieldY = Me.FieldHeight.Value
  HintX = (FieldX + 1) \ 2                        'ヒントサイズ算出
  HintY = (FieldY + 1) \ 2
  Unload Me                                       'フォーム消去
  Cells.Delete                                    '編集領域クリア
  With Cells
    .RowHeight = 12                               '行高/列幅調整
    .ColumnWidth = 1.4
  End With
  Range(Cells(HintY + 1, 1), Cells(HintY + FieldY, HintX)) _
    .Borders.LineStyle = xlContinuous             '水平ヒントエリア
  Range(Cells(1, HintX + 1), Cells(HintY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             '垂直ヒントエリア
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             'フィールドエリア
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .BorderAround , xlMedium
  NonoModule.NewLogicSheet                        '新規シート作成
End Sub

プロシージャの引越しということで本当に最低限の変更しかしていないので、ここから少しずつ修正を加えていきます。

5マスごとの太線

記述位置の引越しが終わったら、デザイン面の変更を加えていきます。何も考えずに罫線だけで作った取り敢ずの書式から、より見栄えのいいデザインでロジックシートを作成していきます。縦横のヒント部分と左上の未使用部分を色分けし、シートは5マスごとに太線で区切ります。


Sub NewLogicSheet()
' 新規ロジックシート作成
  Dim FieldX As Integer                           'フィールドサイズ
  Dim FieldY As Integer
  Dim HintX As Integer                            'ヒントサイズ
  Dim HintY As Integer
  Dim StartPos As Integer                         '太線描画ポインタ
  Dim EndPos As Integer
  FieldX = SetFieldSize.FieldWidth.Value          'フィールドサイズ取得
  FieldY = SetFieldSize.FieldHeight.Value
  HintX = (FieldX + 1) \ 2                        'ヒントサイズ算出
  HintY = (FieldY + 1) \ 2
  Unload SetFieldSize                             'フォーム消去
  Cells.Delete                                    '編集領域クリア
  With Cells
    .RowHeight = 12                               '行高/列幅調整
    .ColumnWidth = 1.4
  End With
  Range(Cells(HintY + 1, 1), Cells(HintY + FieldY, HintX)) _
    .Borders.LineStyle = xlContinuous             '水平ヒントエリア
  Range(Cells(1, HintX + 1), Cells(HintY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             '垂直ヒントエリア
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             'フィールドエリア
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .BorderAround , xlMedium
  With Range(Cells(HintY + 1, 1) _
    , Cells(HintY + FieldY, HintX))               '水平ヒントエリア
    .Borders(xlInsideHorizontal).LineStyle = xlContinuous
    .Interior.ColorIndex = 36
    .Interior.Pattern = xlSolid
  End With
  With Range(Cells(1, HintX + 1) _
    , Cells(HintY, HintX + FieldX))               '垂直ヒントエリア
    .Borders(xlInsideVertical).LineStyle = xlContinuous
    .Interior.ColorIndex = 36
    .Interior.Pattern = xlSolid
  End With
  Range(Cells(HintY + 1, HintX + 1), Cells(HintY + FieldY, HintX + FieldX)) _
    .Borders.LineStyle = xlContinuous             'フィールドエリア
  For StartPos = 1 To FieldY Step 5               '横太線描画
    EndPos = StartPos + 4
    If EndPos > FieldY Then EndPos = FieldY
    Range(Cells(StartPos + HintY, 1), _
      Cells(EndPos + HintY, HintX + FieldX)) _
      .BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
  Next StartPos
  For StartPos = 1 To FieldX Step 5               '縦太線描画
    EndPos = StartPos + 4
    If EndPos > FieldX Then EndPos = FieldX
    Range(Cells(1, StartPos + HintX), _
      Cells(HintY + FieldY, EndPos + HintX)) _
      .BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
  Next StartPos
  Range(Cells(1, 1), Cells(HintY, HintX)) _
    .Interior.ColorIndex = 15                     '左上無効領域
End Sub

ロジックシート作成手順としては、水平ヒントエリアは横線のみ、垂直ヒントエリアは縦線のみを引き、イメージフィールドには縦横格子状に線を引きます。ヒントエリアは背景を橙色に塗潰し、フィールド部分と見分けやすいようにもしています。次に横方向で1マス目から5マス分を囲った外側(BorderAroundプロパティ)に太線を描き、次の5マス分も同様に太線を描くという手順を繰返します。同じく縦方向も太線で囲う操作を繰返し、最後に左上の無効領域を灰色に塗潰します。

気を付けたいのが、サイズ入力値が5の倍数でなかったとき、最終的に端数になる時の対応です。例えば横8マスのロジックシートを作る場合、最初の5マスは問題ありませんが、残った3マス分だけを太線で囲うようにする必要があります。

プロシージャの準備ができたらFieldSizeDialogを実行してみましょう。フォームに入力した数値によって、色々なサイズのロジックシートが見栄え良く作成できます。

← 前へ   → 次へ ▲ ページトップ