C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

Drawのテーブルに最後の列を追加

Drawの穴寸法/座標寸法テーブルで、最後の列を増やしたい事が
結構あるのですが、恐らく手動では出来ないような気がしてます。
手動で出来ます(コメント部で、mineさんに教えて頂きました)
f:id:kandennti:20171023192119p:plain
(方法知っている方いらっしゃいましたら、教えて頂けると助かります)

仕方ないので、マクロを作成しました。

'vba DrawTable_Add_LastColumn ver0.0.1  using-'KCL0.0.12'  by Kantoku

Option Explicit

Sub CATMain()

    'ドキュメントのチェック
    If Not CanExecute("DrawingDocument") Then Exit Sub

    'テーブル選択
    Dim Msg As String
    Msg = "最後に列を追加するテーブルを選択 // [Esc]=Cancel"
    
    Dim Tbl As DrawingTable
    Set Tbl = KCL.SelectItem(Msg, "DrawingTable")
    If Tbl Is Nothing Then Exit Sub
    
    '一列追加
    Dim CntCol As Long: CntCol = Tbl.NumberOfColumns
    Call Tbl.AddColumn(CntCol)
    
    'コピー
    Dim CntRow As Long: CntRow = Tbl.NumberOfRows
    Dim Txt As String, i As Long
    
    For i = 1 To CntRow
        Txt = Tbl.GetCellString(i, CntCol + 1)
        Call Tbl.SetCellString(i, CntCol, Txt)
        Call Tbl.SetCellString(i, CntCol + 1, "")
    Next
End Sub

最後から2番目の位置に列を追加し、最後の列をコピーしつつ
最後の列を空欄にしています。(vbNullStringではNG)

コード的にはくだらないのですが、これを手動でやると本当に
手間がかかるんですよね。Excelの様に複数のセルをまとめて
コピー出来ないですし。

・・・最後に追加出来ない! ってMS製品でも多いですよね?

参考寸法

手抜きメニューに登録しながらマクロを修正していたら
地味なヤツを見つけました。

こんな感じの、丸括弧付きの寸法って何と呼ぶのが正式なんでしょうか?
(きっとJISとかで決まっていると思うのですが)

f:id:kandennti:20171020102205p:plain

僕は参考寸法と呼んでいます。 こちらでもそんな記載になってます。
寸法の記入の基本ルール
「2重寸法の禁止」の所に記載されています。
(個人的に重複した寸法は、かなり重罪です)

こちらでは括弧寸法と記載されていました。
括弧寸法の使い方をここまで細かく語るエンジニアはいない | メカ設計のツボ|マザーマシンの元エンジニアが語る


これ、図面を描いているAdvanceCADには、寸法をクリックしただけで
行ってくれる機能があるのですが、CATIAには無いですよね?

これを行うだけのマクロです。

'vba Sample_Draw_Ref_Dimension ver0.0.2  using-'KCL0.0.12'

Option Explicit

Private Enum Dimension_Value
    Main_Value = 1
    Dual_Value = 2
End Enum

Sub CATMain()

    'ドキュメントのチェック
    If Not CanExecute("DrawingDocument") Then Exit Sub
    
    Dim Msg$: Msg = "参考/非参考化する寸法を選択 // [Esc]=Cancel"
    Dim DrwDim As DrawingDimension
    Dim DimVal As Variant 'DrawingDimValue
    Dim Bfr$, Afr$, Upr$, Lwr$
    
    Do
        Set DrwDim = KCL.SelectItem(Msg, "DrawingDimension")
        If DrwDim Is Nothing Then Exit Sub
        
        Set DimVal = DrwDim.GetValue
        Call DimVal.GetBaultText(Dimension_Value.Main_Value, _
                                 Bfr, Afr, Upr, Lwr)
        If IsRefDimension(Bfr, Afr) Then
            '既に参考寸法
            Call DimVal.SetBaultText(Dimension_Value.Main_Value, _
                                    Mid(Bfr, 2), _
                                    Left(Afr, Len(Afr) - 1), _
                                    Upr, Lwr)
        Else
            'ただの寸法
            Call DimVal.SetBaultText(Dimension_Value.Main_Value, _
                                    "(" & Bfr, _
                                    Afr & ")", _
                                    Upr, Lwr)
        End If
    Loop
End Sub

Private Function IsRefDimension(ByVal Bfr As String, _
                                ByVal Afr As String) As Boolean
    IsRefDimension = False
    
    If Bfr = vbNullString Or Afr = vbNullString Then Exit Function
    
    Dim Tp$, Ed$
    Tp = Left(Bfr, 1)
    Ed = Right(Afr, 1)
    
    If Tp = "(" And Ed = ")" Then IsRefDimension = True
End Function

寸法のプロパティの [寸法テキスト]タブ [関連テキスト] の
・前側の最初の文字が「(」
・後側の最後の文字が「)」
の二つの条件がそろった時に、丸括弧を外し
そうでないときは丸括弧を追加します。
f:id:kandennti:20171020102233p:plain

こんな感じです。

・・・地味

手抜きマクロ起動用メニュー3

こちらの続きです。
手抜きマクロ起動用メニュー2 - C#ATIA

「Cat_Macro_Menu_Model.bas」の宣言セクション部に幾つか定数を
設けています。お好みで変更してください。

'Cat_Macro_Menu_Model.bas
'using-'KCL0.0.12'
'タグを追記したモジュールをプロジェクトに追加するだけで
'自動的にボタンを追加します

Const FormTitle = "Macro"

'----- メニューの仕様 お好みで ---------------------------------------

'メニューの表示方法
'True-モーダレス表示  False-モーダル表示
Private Const MENU_SHOW_TYPE = True

'メニューを閉じるタイミング
'True-フォームのXボタンを押して閉じます False-ボタンクリック後閉じます
Private Const MENU_HIDE_TYPE = True

'メニューのマルチページの設定
'変更する際は
'{ タグのグループ番号 : ページのタイトル文字 }
'の状態にして下さい
Private Const GROUP_NUMBER_CAPTION = _
            "{ 1 : Part }" & _
            "{11 : Assy }" & _
            "{21 : Draw }" & _
            "{51 : Other }"
'-----------------------------------------------------------------
・・・

・「MENU_SHOW_TYPE」
  メニューの表示で、モーダレスかモーダルかをBoolean型で切り替えられます。

・「MENU_HIDE_TYPE」
  メニュー終了タイミングですが、Upしたデータのコメント間違えていました・・。
  正しくは上記の状態で、Boolean型で切り替えられます。
  ボタンクリック後に終了させるか、表示しっぱなしかです。

  実質、「モーダレス + 表示しっぱなし」 か 「モーダル + クリック後終了」
  に、なるのだろうと思います。

・「GROUP_NUMBER_CAPTION」
  マルチページの分類と表示させる文字になります。
  挿入する標準モジュールの {Gp}タグは、こちらの番号にない場合は
  ボタンが表示されません。


続いて、フォームコードの「Cat_Macro_Menu_View.frm」の宣言セクション部です。
フォームのレイアウト等はこちらの定数で、若干調整が可能です。
(環境によってはボタンの文字が途切れる等あるかもしれないため・・・))

'Cat_Macro_Menu_View.frm
'メニューのUIです。

'フォーム
Private FrmMargin As Variant '上,下,左,右のボタン配置のマージン
Private Const ADJUST_F_W = 13 'フォームの左右の調整幅
Private Const ADJUST_F_H = 30 'フォームの上下の調整幅

'マルチページ
Private Const ADJUST_M_W = 5 'マルチページの左右の調整幅
Private Const ADJUST_M_H = 18 'マルチページの上下の調整幅

'ボタン
Private Const BTN_W = 70 'ボタンの幅-フォームの最低幅以下にすると余白が増える
Private Const BTN_H = 20 'ボタン1個の高さ

・・・

各マルチページに該当するボタンがない場合は、ページ自体が作成されません。
又、各ページに配置されるボタンの順番は、モジュール名でソートして表示させています。
好みの順番で表示させたい場合は・・・・モジュール名を工夫してください。
(良いアイデアが思いつきませんでした)

あと何か有ったかなぁ? マクロ用のメニューなんて今更だし。


念の為、このままではOffice製品では機能しないです。

プロジェクト名・パスの取得方法は、imihitoさんが
こちらのコメント欄に記載して頂いてます。
実行中のプロジェクト名とパス取得する - C#ATIA

ボタン押した際の各マクロの実行方法は、こちらの
「Application.Run」の方法で可能だろうと思います。
外部のマクロを実行する2 - C#ATIA


正規表現もまともに使ったの初めてでしたし、まさか自分が
CallByNameを使うコードを書く日が来るとは思っていませんでしたよ。

Excelフォームボタンからマクロの起動 (未確認)

Excelのフォームのボタンを押したら、指定した座標系を元に
 XYZの値を入力した位置に、点を作成したい」
と言う内容の御質問を頂きました。

生憎、古いExcelしかインストールされていないため(マクロ動かない)
完全には再現できませんが、近い事はしてみたいと思います。


絶対座標系を元に点1を作成し、点1を元に座標系2を作成しました。
f:id:kandennti:20171017184349p:plain
この座標系2を元に座標を指定した点を作成したい と解釈しましたので
その通りにマクロの記録をとってみると、この様になりました。

'vba
Sub CATMain()

Dim partDocument1 As PartDocument
Set partDocument1 = CATIA.ActiveDocument

Dim part1 As Part
Set part1 = partDocument1.Part

Dim hybridShapeFactory1 As HybridShapeFactory
Set hybridShapeFactory1 = part1.HybridShapeFactory

Dim hybridShapePointCoord1 As HybridShapePointCoord
Set hybridShapePointCoord1 = hybridShapeFactory1.AddNewPointCoord(10#, 20#, 30#)

Dim axisSystems1 As AxisSystems
Set axisSystems1 = part1.AxisSystems

Dim axisSystem1 As AxisSystem
Set axisSystem1 = axisSystems1.Item("座標系.2")

Dim reference1 As Reference
Set reference1 = part1.CreateReferenceFromObject(axisSystem1)

hybridShapePointCoord1.RefAxisSystem = reference1

Dim hybridBodies1 As HybridBodies
Set hybridBodies1 = part1.HybridBodies

Dim hybridBody1 As HybridBody
Set hybridBody1 = hybridBodies1.Item("形状セット.1")

hybridBody1.AppendHybridShape hybridShapePointCoord1

part1.InWorkObject = hybridShapePointCoord1

part1.Update

End Sub

こちらにも記載しましたが、僕の場合は記録を取った
マクロを実行し再現されるかどうかを確認しています。
VBAでマクロを作成する際の我流な手順1 - C#ATIA
今回は、再現できたので記録は取れているようです。

コードを見ると "座標系.2" が見つかるので、その続きを見てみると

・・・

Dim axisSystem1 As AxisSystem
Set axisSystem1 = axisSystems1.Item("座標系.2")

Dim reference1 As Reference
Set reference1 = part1.CreateReferenceFromObject(axisSystem1)

hybridShapePointCoord1.RefAxisSystem = reference1

・・・

リファレンスを取得し、作成した点の「RefAxisSystem」プロパティに
座標系2のリファレンスを割り当てています。
どうやらこれが キモ のようです。

念の為、「リファレンスとは何者か?」 と聞かれてもお答えする
自信がありません。
(CATIAのマクロに取り組み始めた際、最初にぶつかる壁が
 リファレンスのような気がしてます)
プログラム的に言えば、パラメータ(引数)として受け取る型が
複数種類ある場合、オーバーロードを利用する事になると思うのですが
多数あるため大量になりすぎ、手間です。
それだったら必要な情報だけを持った抽象的なクラスを作成し、
受け取った側で情報が足りなかったら、エラー出したほうが
面倒じゃないな って感じで存在しているのかとは
思っているのですが、真実はわかりませんし
ここは読み飛ばしてください・・・。


やることがわかったので、後は座標系の指定と座標値の入力を
追加してやれば良いですね。

Sub CATMain()

    Dim partDocument1 As PartDocument
    Set partDocument1 = CATIA.ActiveDocument
    
    'セレクション
    Dim Sel As Variant 'Selectionだとエラーになります
    Set Sel = partDocument1.Selection
    
    '選択の為のフィルター
    Dim Filter As Variant
    Filter = Array("AxisSystem")
    
    '座標系選択
    Dim Msg As String
    Msg = "座標系を選択してください / ESC-中止"
    
    Sel.Clear
    Select Case Sel.SelectElement2(Filter, Msg, False)
        Case "Cancel", "Undo", "Redo"
            Exit Sub '中止
    End Select
    
    '選択した座標系を代入
    Dim axisSystem1 As AxisSystem
    Set axisSystem1 = Sel.Item(1).Value
    
    '座標値入力
    Msg = "座標値を入力して下さい。 例)10,20,30"
    
    Dim PosStr As String
    PosStr = InputBox(Msg, , "10,20,30")
    
    If PosStr = vbNullString Then Exit Sub '空欄/キャンセル中止
    
    '入力値判断
    Dim PosStrAry As Variant
    PosStrAry = Split(PosStr, ",")
    
    If UBound(PosStrAry) < 2 Then Exit Sub 'XYZ分無いので中止
    
    '文字を数値化
    Dim Pos(2) As Variant
    Dim i As Long
    For i = 0 To 2
        If Not IsNumeric(PosStrAry(i)) Then
            Exit Sub '数字じゃないので中止
        End If
        Pos(i) = CDbl(PosStrAry(i))
    Next
    
    Dim part1 As Part
    Set part1 = partDocument1.Part
    
    Dim hybridShapeFactory1 As HybridShapeFactory
    Set hybridShapeFactory1 = part1.HybridShapeFactory
    
    Dim hybridShapePointCoord1 As HybridShapePointCoord
'    Set hybridShapePointCoord1 = hybridShapeFactory1.AddNewPointCoord(10#, 20#, 30#)
    Set hybridShapePointCoord1 = hybridShapeFactory1 _
                        .AddNewPointCoord(Pos(0), Pos(1), Pos(2))
    
'    Dim axisSystems1 As AxisSystems
'    Set axisSystems1 = part1.AxisSystems
'
'    Dim axisSystem1 As AxisSystem
'    Set axisSystem1 = axisSystems1.Item("座標系.2")
    
    Dim reference1 As Reference
    Set reference1 = part1.CreateReferenceFromObject(axisSystem1)
                                    '選択した座標系です↑
    
    hybridShapePointCoord1.RefAxisSystem = reference1
    
    Dim hybridBodies1 As HybridBodies
    Set hybridBodies1 = part1.HybridBodies
    
    Dim hybridBody1 As HybridBody
    Set hybridBody1 = hybridBodies1.Item("形状セット.1")
    
    hybridBody1.AppendHybridShape hybridShapePointCoord1
    
    part1.InWorkObject = hybridShapePointCoord1
    
    part1.Update

End Sub

追記した部分にはコメントを入れ、不要部分はコメント化して残りました。
(個人的には嫌いなのですが、この方がわかりやすいと思ったので)

"形状セット.1" が必要になりますが、これで動作すると思います。
これで、目的の半分は達成しました。



続いて、"Excelから" ですが、これを "外部から" と置き換え進めます。
探したところ過去にこれを書いていました。

仕方なく、OOoBasicをやってみました3 - C#ATIA

外部の場合は、記録を取ったマクロの冒頭の

Sub CATMain()

    Dim partDocument1 As PartDocument
    Set partDocument1 = CATIA.ActiveDocument

  ・・・

の "CATIA" が見当たらないので、リンク先のような記述で
"CATIA" を教えてあげれば良いだけです。
(但し、CATIAが起動している状態で実行が条件です)

つまり、Excelのボタンのクリックイベントに

Sub CATMain()
    
    Dim Cat As Application
    Set Cat = CreateObject("CATIA.Application")
    
    Dim partDocument1 As PartDocument
'    Set partDocument1 = CATIA.ActiveDocument
    Set partDocument1 = Cat.ActiveDocument

  ・・・

の様に記載すれば、同じような動作が出来ます。
(やったことは無いのですが、PythonRuby等の他言語同様です)

恐らく
"点は、多数作成したい"
"毎回、座標系を指定するのは面倒"
"セルの値を元に、点を作成したい"
等もあるかと思いますので、わからない事があれば
コメント下さい。(メールはあまりチェックしていないもので・・・)


追記です。
参照設定について記載し忘れました。

「参照設定が不要」 なのではなく 「参照設定なしでも動きます」
です。 OOoBasicのところにも記載しましたが、参照設定無しで
行う場合は全て置換機能で「As 」 → 「'As 」に置き換えコメント化
させてしまいます。

Excel側で参照設定を行う場合は、CATIAのVBエディタで参照設定
されているものを同様に設定する必要があります。
が、面倒なのが本音です。
こちらは以前のC#時の画像がありますが、
DotNetでCATIA V5のマクロを作成する際の準備 - C#ATIA
それっぽい名称になっているため、控えていなくても
何となくチェックを入れるべきものはわかるかと思います。

手抜きマクロ起動用メニュー2

こちらの続きです。
手抜きマクロ起動用メニュー1 - C#ATIA

CATVBAプロジェクトにマクロのモジュールを追加するだけで、
メニューボタンを自動的に追加します。

但し、追加するモジュールにボタンとなる情報を追記する
必要が有り、以下 [タグ] と表記して説明します。


〇ファイルの構成
同一のプロジェクト内に以下のファイルをD&Dで追加してください。

・Cat_Macro_Menu_Model.bas
・Cat_Macro_Menu_View.frm
・Cat_Macro_Menu_View.frx
・Button_Evt.cls
・KCL.bas

f:id:kandennti:20171017152458p:plain
ツールバーに登録する、通常のマクロのメニューから起動する際は
「Cat_Macro_Menu_Model.bas」の「CATMain」を指定してください。

〇タグ記載位置
タグは各モジュールの宣言セクション(先頭部分)に記載する必要が有ります。
f:id:kandennti:20171017152335p:plain
こちらで説明されている宣言セクションになります。
Office TANAKA - VBAの変数[変数の適用範囲]

特に注意が必要なのは、上記の赤矢印のラインより上に記載する
と言うことです。
このような状態(ラインがない状態)ではタグを認識する事が
出来ない為メニューにボタンが表示されません。
f:id:kandennti:20171017152345p:plain

こちらの様に出来るだけ [Option Explicit] を宣言し、
その上にタグを記載する事をオススメします。
f:id:kandennti:20171017152427p:plain


〇タグフォーマット
タグのフォーマットは
{ キーワード : 値 }
の状態になっている必要が有ります。("{" ":" "}" は半角です)
Upしたサンプルでは1行に1タグになっていますが。

'{GP:1}{BackColor:12648447}
Option Explicit

Sub CATMain()
    MsgBox "hoge"
End Sub

の様に一行に複数のタグを記載しても大丈夫です。
※タグのキーワードは半角文字で大・小文字は問いません


〇タグ内容
タグは自動生成するボタンの情報を記載する必要が有ります。
{Gp} : グループの略称でボタン作成する際は必須です。
   値はLong型で指定してください。

   メニュー内のどのタブにボタンを作成するかを示しています。
   Upしたファイルの「Cat_Macro_Menu_Model.bas」の宣言セクション部
   の「GROUP_NUMBER_CAPTION」定数部分の数値と一致させる
   必要が有ります。

'vba CATIA V5用 マクロ起動(テヌキ)メニュー Ver0.0.1  by Kantoku
'Cat_Macro_Menu_Model.bas
'using-'KCL0.0.12'

・・・

'メニューのマルチページの設定
'変更する際は
'{ タグのグループ番号 : ページのタイトル文字 }
'の状態にして下さい
Private Const GROUP_NUMBER_CAPTION = _
            "{ 1 : Part }" & _
            "{11 : Assy }" & _
            "{21 : Draw }" & _
            "{51 : Other }"
・・・

   現状では、1-Part 11-Assy 21-Draw 51-Other
   の設定になっているので、任意の数値を記載してください。
   
   逆にライブラリのようなモジュールファイルの場合、ボタンを作成したくない
   はずです。{Gp}タグを記載しない事でメニューに表示させない事に
   なります。

{Ep} : エントリーポイントの略称で任意の設定で、値は文字列型です。
   各モジュールのエントリーポイント(実行されるプロシージャ名)を指定
   しますが、省力時は「CATMain」になります。

   Upしている「sample_IsExists_LeftAxis.bas」では
   「CATMain」が無く、以下の状態になっています。

'vba sample_IsExists_LeftAxis_ver0.0.1  using-'KCL0.0.12'  by Kantoku
'Part内の左手座標系の有無チェック

'{Gp:1}
'{Ep:LeftHand}
'{Caption:LeftHandAxis}
'{ControlTipText:Part内の左手座標系の有無チェック}
'{BackColor:33023}
Option Explicit

Sub LeftHand()
    'ドキュメントのチェック
    If Not CanExecute("PartDocument") Then Exit Sub
・・・

   {Ep:LeftHand}となっており、ボタンを押した際
   「LeftHand()」が実行されるようになっています。

   {Ep}タグで指定されたプロシージャが見つからない場合は
   自動的に「CATMain」を探しますが、両方見つからない場合は
   ボタンが作成されません。

   制限としてチェックがちょっと甘く「Private Sub」や「Function」
   となっているプロシージャは指定しないで下さい。

   又、引数付きのプロシージャも指定しないで下さい。

他は全てでは有りませんが、CommandButtonオブジェクトのプロパティを
指定する事が可能です。制限はこちらです。
・レイアウトを維持する為これらのキーワードを指定しても
 無視されます。
「Top」「Left」「Height」「Width」

・プロパティの値がオブジェクトとなっているものは指定されても
 無視されます。
「Font」「MouseIcon」「Picture」

・プロパティの値が定数となっている場合、定数ではなく
 数値で指定する必要があります。(後に説明します)

{Caption} : ボタンに表示される文字で、値は文字列型です。
   何のマクロのボタンかわからなくなるため設定を推奨します。

{ControlTipText:} : ボタン上にマウスカーソルを持っていく
   ことでに表示される文字で、値は文字列型です。
   何のマクロのボタンかわからなくなるため設定を推奨します。

{BackColor}:ボタンの色で、値はLong型です。
   通常プロパティでは、[&H000000FF&]のような状態で
   表示されていますが、値部分にそのまま入力しても
   色は反映されません。
f:id:kandennti:20171017152557p:plain
   イミディエイトウインドウで「?」(又はprint)で表示させると
   Long型の値が返って来ますので、この値を設定してください。

?&H000000FF&
 255 

   又、定数を指定いるプロパティに関しても同様の方法で
   Long型で設定してください。
   ・・・正直に書くと、試していませんが。



タグについてグダグダ記載しましたが、見た目。機能を
度外視すれば

'{Gp:1}

さえ追記すれば、メニューにボタンが表示されるようになります。

フォーム側の説明は次回に

手抜きマクロ起動用メニュー1

CATIAのマクロを実行する際、こちらの前半に記載した方法で
ツールバーに登録して使っているのですが

マクロを高速に実行させる(非公式) - C#ATIA

正直な所、登録が面倒です。
(何処で見たか忘れましたが、全てのファイルを閉じた状態で
ツールバーを新規に作成すると、ワークベンチに依存しないツールバーが
作成可能なようです)

その為、フォームで自作のメニューを作り自作メニューだけをツールバーに
登録してマクロを起動して運用しています。
・・・が、非常に出来が悪く使い勝手もイマイチでした。

そこで重い腰を上げ、新たに作り直しました。こんな感じです。
f:id:kandennti:20171016183642p:plain
驚く程の手抜き感です。
態々ブログに載せるほどのものか? と、さえ思えます。


実は見た目以上に手抜きなのが、マクロの登録なんです。

最初にメニューを起動した際、"Part" タブにはボタンが1つしか
有りませんが、途中でVBエディタにモジュールをD&Dで追加し
再度メニューを起動すると、ボタンが追加されています。

これぐらい楽が出来るものが、前から欲しかったのですが
なかなか取り掛かれませんでした。

サンプルマクロを含めこちらにUpしております。

GrabCAD - CAD library

ちょっとだけ仕組みがあるため、それは次回に。

ProductTree の リオーダー2

こちらの続きです。
ProductTree の リオーダー - C#ATIA

サンプル用のマクロを用意していたのですが、Product用のマクロを持っていない為、
以前の物を修正し、インスタンス名でソートするマクロに変更しました。

'vba sample_ReOrder_Product ver0.0.1  using-'KCL0.0.12'  by Kantoku
'インスタンス名でのソート順にTreeを並び替えます

Option Explicit

Sub CATMain()
    'ドキュメントのチェック
    If Not CanExecute("ProductDocument") Then Exit Sub
    
    'Doc取得
    Dim ProDoc As ProductDocument: Set ProDoc = CATIA.ActiveDocument
    Dim Pros As Products: Set Pros = ProDoc.Product.Products
    If Pros.Count < 2 Then Exit Sub
    
    'オプション変更
    Dim AssyMode As AsmConstraintSettingAtt
    Set AssyMode = CATIA.SettingControllers.Item("CATAsmConstraintSettingCtrl")
    Dim OriginalMode As CatAsmPasteComponentMode
    OriginalMode = AssyMode.PasteComponentMode
    
    'オプション切り替え
    AssyMode.PasteComponentMode = catPasteWithCstOnCopyAndCut
    
    'ソート済み名前リスト
    Dim Names: Set Names = Get_SortedNames(Pros)
    
    'カット
    Dim Sel As Selection: Set Sel = ProDoc.Selection
    Dim Itm As Variant
    
    CATIA.HSOSynchronized = False
    
    Sel.Clear
    For Each Itm In Names
        Sel.Add Pros.Item(Itm)
    Next
    Sel.Cut
    
    'ペースト
    With Sel
        .Clear
        .Add Pros
        .Paste
        .Clear
    End With
    
    CATIA.HSOSynchronized = True
    
    'オプション戻し,UpDate
    AssyMode.PasteComponentMode = OriginalMode
    ProDoc.Product.Update
End Sub

'インスタンス名でソート済みの名前リスト
Private Function Get_SortedNames(ByVal Pros As Products) As Object
    Dim Lst As Object
    Set Lst = KCL.InitLst()
    
    Dim Pro As Product
    For Each Pro In Pros
        Lst.Add Pro.Name
    Next
    
    Lst.Sort
    
    Set Get_SortedNames = Lst
End Function

又、ソート処理が毎回面倒な為、KCLをVer0.0.12としました。
非常に個人的なCATVBA用ライブラリ - C#ATIA