半角チルダ

ExcelVBA、その他。
覚え書きや、補足資料などのスクラップブック。
end-u(1037781)

■xl2007:ModifyAppliesToRangeメソッドには要注意

2010-08-17 12:00:00 | 気をつけたほうがいいこと
...というか私が抜けてるだけ?懺悔シリーズ2。

■xl2007:ModifyAppliesToRangeメソッド
re2:xl2007:ModifyAppliesToRange でTypeが変わる
ここで暢気にFor Each NextステートメントでModifyAppliesToRangeやっちゃってますが、これ大変危険です。
適用先AppliesToプロパティが同一範囲のFormatConditionに対してModifyAppliesToRangeメソッドを続けて実行すると、Excel2007が落ちます。

最小限の再現コードはこれ。
Sub test() '私の環境では確実に2007が落ちます。安易に実行しない事。
  Dim f As FormatCondition

  On Error Resume Next
  With Range("A1")
    .FormatConditions.Add( _
             Type:=xlExpression, Formula1:="=RC>2" _
             ).Interior.ColorIndex = 35
    .FormatConditions.Add( _
             Type:=xlExpression, Formula1:="=RC>1" _
             ).Interior.ColorIndex = 36
    For Each f In .FormatConditions
      f.ModifyAppliesToRange Range("B1")
    Next
  End With
End Sub

普通、こんな事はしませんが、For EachでDeleteしてみます。
Sub test2()
  Dim f As FormatCondition

  With Range("A1")
    .FormatConditions.Add( _
             Type:=xlExpression, Formula1:="=RC>2" _
             ).Interior.ColorIndex = 35
    .FormatConditions.Add( _
             Type:=xlExpression, Formula1:="=RC>1" _
             ).Interior.ColorIndex = 36
    For Each f In .FormatConditions
      f.Delete
    Next
  End With
End Sub
『9:インデックスが有効範囲にありません。』エラーです。
考えてみりゃ、前から削除とかMoveとかしちゃダメよねorz

...
......ん?前に詰まるの?
Sub test3()
  Dim f As FormatCondition

  Range("A1").FormatConditions.Add( _
        Type:=xlExpression, Formula1:="=RC>2" _
        ).Interior.ColorIndex = 35
  Range("A1:A2").FormatConditions.Add( _
          Type:=xlExpression, Formula1:="=RC>1" _
          ).Interior.ColorIndex = 36
  For Each f In Range("A1").FormatConditions
    f.ModifyAppliesToRange Range("B1")
  Next
End Sub
これは落ちないんだけどなぁ。

今いち不可解ですが...まっとうに生きたいなら
Sub test4()
  Dim i As Long

  With Range("A1").FormatConditions
          .Add(Type:=xlExpression, Formula1:="=RC>2" _
           ).Interior.ColorIndex = 35
          .Add(Type:=xlExpression, Formula1:="=RC>1" _
           ).Interior.ColorIndex = 36
    For i = .Count To 1 Step -1
      .Item(i).ModifyAppliesToRange Range("B1")
    Next
  End With
End Sub
これ?

...それにしたって、落ちなくてもいいじゃない...ねェ...orz

環境:[Windows]XP pro 5.1.2600 SP3 [EXCEL]2007 12.0.6535.5002 SP2
Comments (2)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■xl2007:SetFirstPriorityメソッド(その後

2010-08-16 21:00:00 | 雑記
懺悔シリーズ。

■xl2007:SetFirstPriorityメソッド
この記事では、2007の[条件付き書式]に関して、[適用先]のセル範囲が条件ごとに違う場合、コピーによって優先順位がひっくり返ってしまうので、SetFirstPriorityメソッドを使って設定し直したほうが良い...
...などとホザいてやがる。

うそでした。ごめんなさいorz

追加検証する機会があったので訂正しておきます。
まず前記事のおさらい。
優先順位 適用先   ルール 背景色
1    A1:C1 >2   
2    A1:B1 >1   
3    A1    >0   
こんな[条件付き書式]を設定してコピーすると、優先順位が逆転してるように見えます。

Sub test1()
  With Sheets.Add
    'A1:C1に条件1
    .Range("A1:C1").FormatConditions.Add( _
            Type:=xlExpression, Formula1:="=RC>2" _
            ).Interior.ColorIndex = 35
    'A1:B1に条件2
    .Range("A1:B1").FormatConditions.Add( _
            Type:=xlExpression, Formula1:="=RC>1" _
            ).Interior.ColorIndex = 36
    'A1に条件3
    .Range("A1").FormatConditions.Add( _
           Type:=xlExpression, Formula1:="=RC>0" _
           ).Interior.ColorIndex = 37
    .Range("A1").Value = 3
    .Range("A1:C1").Copy .Range("A2")
  End With
End Sub

(test1結果)


優先順位 適用先   ルール 背景色
1    A2    >0   
2    A2:B2 >1   
3    A2:C2 >2   

この結果から、単純にSetFirstPriorityメソッドで優先順位をひっくり返せばいいかというと、
ダメです。
同じ[条件付き書式]を、追加する順番を変えてみます。
追加後にPriorityを変更したものを、コピーします。
優先順位 適用先   ルール 背景色
    A1    >0   
    A1:B1 >1   
    A1:C1 >2   

Sub test2()
  With Sheets.Add
    'A1に条件3
    .Range("A1").FormatConditions.Add( _
           Type:=xlExpression, Formula1:="=RC>0" _
           ).Interior.ColorIndex = 37
    'A1:B1に条件2
    With .Range("A1:B1").FormatConditions.Add( _
               Type:=xlExpression, Formula1:="=RC>1")
      .Interior.ColorIndex = 36
      .SetFirstPriority '●
    End With
    'A1:C1に条件1
    With .Range("A1:C1").FormatConditions.Add( _
               Type:=xlExpression, Formula1:="=RC>2")
        .Interior.ColorIndex = 35
        .SetFirstPriority '●
    End With
    .Range("A1").Value = 3
    .Range("A1:C1").Copy .Range("A2")
  End With
End Sub

(test2結果)


優先順位 適用先   ルール 背景色
1    A2:C2 >2   
2    A2:B2 >1   
3    A2    >0   

こんな結果。なんと優先順位は変わってません。

もう1つ。
優先順位 適用先   ルール 背景色
1    A1:C1 >2   
2    A1    >1   
3    A1    >0   
適用先が同じ範囲である条件と、適用先が違う条件とが混在する場合。

Sub test3()
  With Sheets.Add
    'A1:C1に条件1
    .Range("A1:C1").FormatConditions.Add( _
            Type:=xlExpression, Formula1:="=RC>2" _
            ).Interior.ColorIndex = 35
    '●A1に条件2
    .Range("A1").FormatConditions.Add( _
           Type:=xlExpression, Formula1:="=RC>1" _
           ).Interior.ColorIndex = 36
    'A1に条件3
    .Range("A1").FormatConditions.Add( _
           Type:=xlExpression, Formula1:="=RC>0" _
           ).Interior.ColorIndex = 37
    .Range("A1").Value = 3
    .Range("A1:C1").Copy .Range("A2")
  End With
End Sub

(test3結果)


優先順位 適用先   ルール 背景色
1    A2    >1   
2    A2    >0   
3    A2:C2 >2   

こんな結果です。
A1に設定された2つの[条件付き書式]は適用先が違うわけではないので逆転しません。


2007の FormatConditions は、Add の順番、いわゆる作成時の Index を保持していると思われます。
[適用先]のセル範囲が条件ごとに違う[条件付き書式]設定をコピーすると、
FormatConditions 作成時の Index が逆転してしまいます。

Priority が逆転するわけではなかったんですね。
※逆転するのは[適用先]のセル範囲が違う条件のみ。それに1度コピーして逆順になったらそのまま。2回コピーしても戻りません。

#なんともややこしいバグです。 :P
Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■(Shapes)SelectionのLoopについて

2010-08-11 22:00:00 | 雑記
きっかけは
現在、エクセルで、選択したグラフの大きさや縦横軸を統一するマクロを作成... - 教えて!goo
このスレッドだったんですが、ちょっと追加検証してみました。
(下記コードの内容)
新規シートにShapeを3コ追加します。
順番に名前をRect1、Rect2、Rect3、とつけます。
Rect2、Rect1、Rect3、の順番で選択して、選択した順番を保持するかどうかテストしてみます。

Sub test1()
  Dim i As Long
  Dim s As Object

  With Sheets.Add
    For i = 1 To 3
      .Shapes.AddShape(msoShapeRectangle, 100, 100 * i, 100, 50).Name = "Rect" & i
    Next

    .Shapes("Rect2").Select
    .Shapes("Rect1").Select False
    .Shapes("Rect3").Select False

    Debug.Print vbLf & "For To 1回目"
    For i = 1 To Selection.Count
      Debug.Print Selection.Item(i).Name
    Next

    Debug.Print vbLf & "For To 2回目"
    For i = 1 To Selection.Count
      Debug.Print Selection.Item(i).Name
    Next

    .Shapes("Rect2").Select
    .Shapes("Rect1").Select False
    .Shapes("Rect3").Select False

    Debug.Print vbLf & "For Each 1回目"
    For Each s In Selection
      Debug.Print s.Name
    Next

    Debug.Print vbLf & "For Each 2回目"
    For Each s In Selection
      Debug.Print s.Name
    Next

  End With
End Sub



そのままSelectionをLoopした場合、

【Excel2003 test1】
For To 1回目
Rect2
Rect2
Rect3

For To 2回目
Rect1
Rect2
Rect3

For Each 1回目
Rect2
Rect1
Rect3

For Each 2回目
Rect1
Rect2
Rect3

【Excel2007 test1】
For To 1回目
Rect1
Rect2
Rect3

For To 2回目
Rect1
Rect2
Rect3

For Each 1回目
Rect2
Rect1
Rect3

For Each 2回目
Rect2
Rect1
Rect3

こんな結果です。
期待するのはRect2、Rect1、Rect3、なんですが、どうにも不思議な結果です...

Selectionを一旦変数に受けたらどうなるか。

Sub test2()
  Dim i As Long
  Dim s As Object
  Dim ss As Object

  With Sheets.Add
    For i = 1 To 3
      .Shapes.AddShape(msoShapeRectangle, 100, 100 * i, 100, 50).Name = "Rect" & i
    Next

    .Shapes("Rect2").Select
    .Shapes("Rect1").Select False
    .Shapes("Rect3").Select False
    Set ss = Selection

    Debug.Print vbLf & "For To 1回目"
    For i = 1 To ss.Count
      Debug.Print ss.Item(i).Name
    Next

    Debug.Print vbLf & "For To 2回目"
    For i = 1 To ss.Count
      Debug.Print ss.Item(i).Name
    Next

    .Shapes("Rect2").Select
    .Shapes("Rect1").Select False
    .Shapes("Rect3").Select False
    Set ss = Selection

    Debug.Print vbLf & "For Each 1回目"
    For Each s In ss
      Debug.Print s.Name
    Next

    Debug.Print vbLf & "For Each 2回目"
    For Each s In ss
      Debug.Print s.Name
    Next

  End With
  Set ss = Nothing
End Sub

【Excel2003 test2】
For To 1回目
Rect2
Rect1
Rect3

For To 2回目
Rect2
Rect1
Rect3

For Each 1回目
Rect2
Rect1
Rect3

For Each 2回目
Rect2
Rect1
Rect3

【Excel2007 test2】
For To 1回目
Rect1
Rect2
Rect3

For To 2回目
Rect1
Rect2
Rect3

For Each 1回目
Rect2
Rect1
Rect3

For Each 2回目
Rect2
Rect1
Rect3

Excel2003では期待通りに取れてるようですが、2007とはやはりバージョンによる違いが出てます。
2007はtest1とtest2の結果が同じなので、まあ、首尾一貫してると言えば言えるのかも。
追加テストでtest2直後、選択した状態でもう一度下記For Eachでのテストを実行すると

Sub test3()
  Dim s As Object
  Dim ss As Object

  Set ss = Selection
  For Each s In ss
    Debug.Print s.Name
  Next
End Sub

【Excel2003 test3】
Rect1
Rect2
Rect3

【Excel2007 test3】
Rect2
Rect1
Rect3

って結果です。
あまりニーズがない案件でしょうけど、何かの時に参考に...
...ならないか X(
Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

re2:xl2007:ModifyAppliesToRange でTypeが変わる

2010-08-09 19:30:00 | 雑記
■xl2007:ModifyAppliesToRangeメソッド にコメントいただいた内容についての返信記事です。
コメントには文字数制限があるようで、そちらに書ききれなかったので別記事にしました。

>複数の条件付き書式が設定されていて
>その中に「数式」があった場合、
>ModifyAppliesToRangeを実行後、Typeが「セルの値」に変わってしまいました。
>対処策はあるのでしょうか?


どうも、Typeが変わるわけでは無いようです?

ちょっと実験しました。
元記事の Macro1 を少し変更して、条件3に Type:=xlExpression を設定してみます。

Sub 準備()
  With Sheets.Add
    'A1:C1に条件1
    With .Range("A1:C1").FormatConditions
      .Delete
      With .Add(Type:=xlCellValue, _
           Operator:=xlEqual, _
           Formula1:="1")
        .Interior.ColorIndex = 35
      End With
    End With
    'B1:C1に条件2
    With .Range("B1:C1").FormatConditions
      With .Add(Type:=xlCellValue, _
           Operator:=xlEqual, _
           Formula1:="2")
        .Interior.ColorIndex = 36
      End With
    End With
    'C1に条件3
    With .Range("C1").FormatConditions
      With .Add(Type:=xlExpression, _
           Formula1:="=RC<4")
        .Interior.ColorIndex = 37
      End With
    End With
    .Range("A1:C1").Value = [{1,2,3}]
  End With
End Sub

できたシートをActiveにして、ダイレクトにModifyAppliesToRangeメソッドで[適用先]セル範囲を変更します。

Sub test()
  Dim fc As FormatCondition
  Dim i As Long

  With ActiveSheet.Range("C1")
    For Each fc In .FormatConditions
      i = i + 1
      Debug.Print "before" & i, fc.Type, _
                   fc.Formula1, _
                   fc.AppliesTo.Address
      fc.ModifyAppliesToRange .Item(1)
      'ModifyAppliesToRange直後のAppliesToプロパティはエラー
      Debug.Print "after " & i, fc.Type, _
                   fc.Formula1 ', _
                   fc.AppliesTo.Address
    Next
    i = 0
    Stop
    Debug.Print
    For Each fc In .FormatConditions
      i = i + 1
      Debug.Print "replace" & i, fc.Type, _
                    fc.Formula1, _
                    fc.AppliesTo.Address
    Next
  End With
End Sub

(結果)
before1    1      =1      $A$1:$C$1
after 1    1      =2
before2    1      =2      $B$1:$C$1
after 2    2      =C1<4
before3    2      =C1<4     $C$1
after 3    2      =C1<4

replace1    1      =1      $C$1
replace2    1      =2      $C$1
replace3    2      =C1<4     $C$1

FormatConditionsをLoopしてModifyするんですけど、
ModifyAppliesToRange直後はFormatConditionsのItemを正しく取得できてないようですね。
#リセットされたような感じで次のfcを見に行ってる?
コメントしてるように、AppliesToプロパティを取得しようとするとエラーになりますし。
For EachではなくFor..ToでインデックスをLoopしても同じ。
どうにも挙動不審です。

>Typeが「セルの値」に変わってしまいました。
Loopし直して再取得してみてください。

作業用にコピーしてた場合はインデックスも逆転してるので気をつけてください。
■xl2007:SetFirstPriorityメソッド
Comments (2)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■ワークシート上のPictureの倍率を取得する(後編

2010-08-03 20:00:00 | 雑記
さて、と。
勘違いの前編は捨て記事扱いでいいですけど、その Sub test2 を2007で実行するとエラーです。
>With Selection.Duplicate.ShapeRange
がイケてません。

Pictureに対して
With Selection.Duplicate
  MsgBox .Name
End With

Dim pic As Picture
Set pic = Selection.Duplicate
ってやると2003は通りますが2007はダメです。
Duplicateが失敗しているわけではなく、複製はできています。
複製と同時にObjectとして扱えないような感じです。

しつこくチェックしてみると
Dim x
With ActiveSheet
  With .Pictures
    Set x = .Item(.Count).Duplicate 'A
    MsgBox TypeName(x)
  End With
  With .Shapes
    Set x = .Item(.Count).Duplicate 'B
    MsgBox TypeName(x)
  End With
End With
Set x = Nothing

2003では
A Picture
B Shape

2007では
A エラー
B Shape

何の事はない、Shapeに対してDuplicateすれば良いだけだったり?
つまり
>With Selection.Duplicate.ShapeRange
With Selection.ShapeRange.Duplicate

Sub test3() 'Pictureを選択して実行
  Dim x As Single
  Dim y As Single

  If TypeName(Selection) = "Picture" Then
    With Selection.ShapeRange.Duplicate
      With .PictureFormat
        .CropTop = 0#
        .CropLeft = 0#
        .CropRight = 0#
        .CropBottom = 0#
      End With
      x = .Width
      y = .Height
      .LockAspectRatio = msoFalse
      .ScaleWidth 1, msoTrue
      .ScaleHeight 1, msoTrue
      x = x / .Width
      y = y / .Height
      .Delete
    End With
    MsgBox "x:= " & x & vbLf & "y:= " & y
  End If
End Sub

以上、余談でした(ぇ


で、本題ですが :D
一見上手くいきそうな Sub test も2007ではイケてません。
上辺、左辺をトリミングした画像を Top = 0 , Left = 0 の位置で実行すると
>.CropTop = 0#
>.CropLeft = 0#
この時、画面外にハミ出す(?)為か、トリミング解除後のWidthとHeightが正しく取得できないようです。

(Excel2007:Sub test 実行前)


(Excel2007:Sub test 実行後)


一応、2007では http://oshiete.goo.ne.jp/qa/6074786.html の回答コードで、トリミングの場合でも取得できてます。
でもトリミング画像に対するScaleWidth|Heightメソッドの挙動が、2007から変わってたようです。
なので2003の場合は対応できていませんでした。
トリミング画像に対して

With Selection.ShapeRange
  .ScaleWidth 1, msoTrue
  .ScaleHeight 1, msoTrue
End With

ScaleWidth|Heightメソッドを実行した場合、
2003ではトリミング後のWidth,Heightを元サイズのWidth,Heightに戻します。
2007では元サイズのWidth,Heightからトリミング値を考慮した値になります。

(2003,2007比較)


結論としては、バージョンによる分岐処理が必要...という事になるのでしょうか。
Option Explicit
'---------------------------------------------------------------------
Sub try() 'Pictureを選択して実行
  Dim ret

  If TypeName(Selection) = "Picture" Then
    ret = fGetPicScale(Selection)
    If IsArray(ret) Then
      MsgBox "x:= " & ret(0) & vbLf & "y:= " & ret(1)
    End If
  End If
End Sub
'---------------------------------------------------------------------
Function fGetPicScale(ByRef pic As Picture)
  Dim x As Single
  Dim y As Single
  Dim T As Single
  Dim L As Single
  Dim W As Single
  Dim H As Single
  Dim LC As Long

  On Error GoTo errExit
  With pic.ShapeRange
    T = .Top
    L = .Left
    W = .Width
    H = .Height
    LC = .LockAspectRatio
    '画像位置によっては元サイズに戻しきれない場合の対策
    .Top = 0
    .Left = 0
    .LockAspectRatio = msoFalse
    .ScaleWidth 1, msoTrue, msoScaleFromTopLeft
    .ScaleHeight 1, msoTrue, msoScaleFromTopLeft
    'Ver2003以前のみトリミング値を考慮する
    If CLng(Application.Version) < 12 Then
      With .PictureFormat
        x = .CropLeft + .CropRight
        y = .CropTop + .CropBottom
      End With
    End If
    x = W / (.Width - x)
    y = H / (.Height - y)
    .Top = T
    .Left = L
    .Width = W
    .Height = H
    .LockAspectRatio = LC
  End With
  fGetPicScale = Array(x, y)
errExit:
End Function
Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする