マクロで「並べ替え」をする

Excel2007 で「並べ替え」をするには、以下のようなVBAのコードになります。
これは自動記録したマクロにちょこっと手を加えただけのものです。

Sub Macro1R()
    Dim shtA As Worksheet
    Dim sortA As Sort
    Dim rngA As Range

    Set shtA = Worksheets("Sheet1")
    Set sortA = shtA.Sort
    Set rngA = shtA.Range("A1").CurrentRegion

    sortA.SortFields.Clear
    sortA.SortFields.Add Key:=rngA.Item(1, 1) _
                       , SortOn:=xlSortOnValues _
                       , Order:=xlAscending _
                       , DataOption:=xlSortNormal
    sortA.SetRange rngA
    sortA.Header = xlYes
    sortA.MatchCase = False
    sortA.Orientation = xlTopToBottom
    sortA.SortMethod = xlPinYin
    sortA.Apply
End Sub

Excel2003 までの「並べ替え」のコードとずいぶん変わっています。
Excel2003 まではキーの数が3つまでという制限があったのですが、Excel2007 ではその制限をなくしたためにコードの方にも変更が生じたのでしょう。
 
まぁそれはいいとして、気になる点が2つあります。
 
1つ目は、一般操作のときとエラーチェックの基準が異なることです。
うっかり

    sortA.SortFields.Clear

を忘れると、「並べ替え」の設定がえらいことになりますが、そのときにエラーメッセージが出ないことです。
一般機能で「並べ替え」ダイアログで、重複した並べ替えの条件を指定すると、それを実行しようとしたときにエラーメッセージが出ます。
しかし、VBA で重複した並べ替えの条件を指定してもエラーにはなりません。

    sortA.SortFields.Add Key:=rngA.Item(1, 1) _
                       , SortOn:=xlSortOnValues _
                       , Order:=xlAscending _
                       , DataOption:=xlSortNormal
    sortA.SortFields.Add Key:=rngA.Item(1, 1) _
                       , SortOn:=xlSortOnValues _
                       , Order:=xlAscending _
                       , DataOption:=xlSortNormal

として、Apply メソッドを実行してもエラーにならないのです。
実際に、それを実行した後に「並べ替え」ダイアログを出すと、重複した並べ替えの条件が設定されたままになっています。
 
しかし、それを実行した後に保存して、再度そのブックを開くときにエラーメッセージが出ます。
「並べ替え」の条件はシートの属性として保存されます。
シートの属性に不具合があるかどうか開くときにチェックされるので、そこで初めてエラーメッセージが出るということになるようです。
 
2つ目は、ヘルプが適切じゃないことです。
一番最後の Apply メソッドについてヘルプで調べてみました。
sortA.Apply より前の部分が「並べ替え」の設定に関するコードで、このメソッドは「並べ替え」そのものを実行するものです。
しかし、ヘルプの記述では・・・

Excel 開発者用リファレンス 
Sort.Apply メソッド 
コピーした並べ替え書式を適用します。

となっています。
これではなんのことやら分かりませんね。

OnAction と引数

MOUG(Excel VBA)2008/05/27 22:42:47
『OnActionで実行するプロシージャに引数を渡すと2度実行される?』
http://www.moug.net/faq/viewtopic.php?t=28150
で、なかなかおもしろいことがあったので書いておきます。
だいたいタイトルのとおりなんですが、もう少し正確に書くと、「OnActionで実行するプロシージャに引数を渡すときに、ある書き方をすると、そのプロシージャは2度実行される」という話です。
ま、コードの書き方にちょっと問題があるだけのことなんですが…
質問者は Excel2003 で冒頭の現象が起こっているらしいのですが、Excel2002 でも再現しました。
以下は実験用のサンプルコードです。

Public Sub Sample()
  Dim MenuA As Office.CommandBar

  Set MenuA = Application.CommandBars("Cell")
  MenuA.Reset
  With MenuA.Controls.Add
    .Caption = "追加Menu1"
    .OnAction = "Macro1(""Test1"")"
  End With
  With MenuA.Controls.Add
    .Caption = "追加Menu2"
    .OnAction = "'Macro1(""Test2"")'"
  End With
  With MenuA.Controls.Add
    .Caption = "追加Menu3"
    .OnAction = "'Macro1 ""Test3""'"
  End With
End Sub
 
Public Sub Macro1(ArgText As String)
  MsgBox ArgText
End Sub

コードを見てのとおり、Sample プロシージャを実行すると、セルの右クリックメニューにリストが3つ追加されます。
そして、そのうちの"追加Menu1"をクリックした場合だけ、メッセージボックスが2度表示されます。
質問者は、1つ目の書き方をしていました。
正しい書き方は3つ目です。
ヘルプの使用例では ' は書かれていないとか、引数なしの場合は ' なしでも動くとか、↑のとおり意図したとおりにならないけどエラーにもならないとか… ちょっとばかしややこしいです。
 
実験が終わったら、イミディエイトウィンドウででも
Application.CommandBars("Cell").Reset
を走らせて、右クリックメニューを戻しておくことをお忘れなく。
 
余談ですが、シートの書き方にもちょっと似たところがありますね。

MsgBox デバッグに御用心

Excel VBA でプログラムを作っている途中で、「この場合、この変数に何が入るのかな?」と確かめたくなった場合に、一時的に MsgBox を入れておいて実行し、その変数の中身を確認するということをしばしば私はやります。
 
しかし、API等から取得した文字列の場合は、注意が必要です。
文字列の途中に vbNullChar が存在すると、そこまでしか MsgBox は表示してくれないからです。
Debug.Print なら、全部見ることができます。

Sub Sample()
  Dim X As String
  X = "AB" & vbNullChar & "CD"

  MsgBox X & vbCrLf & Len(X)
  Debug.Print X
End Sub

 
 
10年ほど前に、日立の CommuniNet Extension を使ってオンライン画面の自動制御のプログラムを VB4 で作ったときに、このことを知らなくて「あっれ〜。なんで画面の文字列が取得できているはずなのに見れないんだ?」とずいぶん悩んだもんです。
画面上にフィールドが定義されていない部分がヌルキャラだったのが原因でした。

線が乗るセル

Excel2007 で、枠線に合わせて縦線と横線を引いてから、↓のマクロを実行してみました。

Sub Macro1()
  Dim lineV As Shape ' 縦線
  Dim lineH As Shape ' 横線

  Set lineV = ActiveSheet.Shapes(1)
  Set lineH = ActiveSheet.Shapes(2)
  Debug.Print "◆◇◆ Macro1 ◆◇◆"
  Debug.Print lineV.Name
  Debug.Print lineV.TopLeftCell.Address _
       , lineV.BottomRightCell.Address
  Debug.Print lineH.Name
  Debug.Print lineH.TopLeftCell.Address _
       , lineH.BottomRightCell.Address
End Sub

 
実行結果は以下のとおりです。
一見、不思議なところは何もないように思えます。
いや、よくよく考えると、縦線と横線の考え方の整合性がとれていません。

◆◇◆ Macro1 ◆◇◆
直線コネクタ 2
$B$3     $C$6
直線コネクタ 4
$D$3     $F$3

 
念のため、同じことを Excel2002 でやってみると…

◆◇◆ Macro1 ◆◇◆
Line 1
$C$3     $C$6
Line 2
$D$3     $F$3

 
Name プロパティで取得されるものが違うのはどうでもいいのですが、やはり Excel2007 での縦線がややおかしいようです。
前回、前々回にとりあげた Top,Left や Height,Width に比べれば、かわいい違いですけど。
 
P.S.
枠線に合わせて図形を配置するには Alt キーを押しながらやるのが楽チンです。

同じ場所なのに、違う位置?


Excel2007 で、開始位置が同じである縦線と横線を引いてから、↓のマクロを実行してみました。

Sub Macro1()
  Dim lineA(1 To 2) As Shape

  Set lineA(1) = ActiveSheet.Shapes(1)
  Set lineA(2) = ActiveSheet.Shapes(2)
  Debug.Print "◆◇◆ Macro1 ◆◇◆"
  Debug.Print "上:" & Range("C3").Top, "左:" & Range("C3").Left
  Debug.Print lineA(1).Name
  Debug.Print "上:" & lineA(1).Top, "左:" & lineA(1).Left
  Debug.Print lineA(2).Name
  Debug.Print "上:" & lineA(2).Top, "左:" & lineA(2).Left
End Sub

 
実行結果は以下のとおりです。
縦線の方がトンでもない位置を示しています。

◆◇◆ Macro1 ◆◇◆
上:24    左:96
直線コネクタ 12
上:42    左:78
直線コネクタ 14
上:24    左:96

 
Shape オブジェクトではなく、Line オブジェクトにしても同じ結果になります。
 
どうも、縦線ってのはダメダメのようです。

高さが幅で、幅が高さ


Excel2007 で縦の直線を引いて、↓のマクロを実行してみました。

Sub Macro1()
  Dim lineA As Shape

  Set lineA = ActiveSheet.Shapes(1)
  Debug.Print "◆◇◆ Macro1 ◆◇◆"
  Debug.Print "高さ:" & lineA.Height, "幅:" & lineA.Width
End Sub

Sub Macro2()
  Dim lineA As Line

  Set lineA = ActiveSheet.Lines(1)
  Debug.Print "◆◇◆ Macro2 ◆◇◆"
  Debug.Print "高さ:" & lineA.Height, "幅:" & lineA.Width
End Sub

 
実行結果は以下のとおりです。
どう見ても、Height と Width が逆なんですけど…

◆◇◆ Macro1 ◆◇◆
高さ:0.1250394       幅:36
◆◇◆ Macro2 ◆◇◆
高さ:0.125039368867874   幅:36

 
横線だと大丈夫なんですけどね。