AutoCAD のブロックには様々な機能がありますが、個々のブロック参照に別々の文字列を設定することのできる属性参照は、その中でも特によく使う機能ではないでしょうか。そこで本稿では、あらかじめ作成しておいた属性定義を使って属性参照を設定する方法についてご紹介したいと思います。

なおサンプルコードは VisualStudio2005 の C# で作成し、 AutoCAD2007 / 2008 / 2009 で動作確認を行いました。

属性参照のサンプル

属性参照は、まず AttributeReference クラスのインスタンスを生成し、それを BlockReference オブジェクトに登録します。このとき、あらかじめ取得しておいた属性定義を AttributeReference オブジェクトに設定すると、その属性定義の設定をそのまま引き継ぐことができます。

まずは下記のサンプルコードをビルドし、その成果物を [NETLOAD] してください。次にサンプル図面を開いてから [INSERT_SAMPLE] コマンドを発行すると、原点に "Sample" ブロックが挿入されます。このブロックには "SampleName" という属性参照が付いていることに注目してください。 なおサンプル図面は、前々回に作成した [CREATE_SAMPLE] コマンドの修正版で "Sample" ブロック定義を登録し、ブロックエディタで "Name" 属性定義を追加したものです。

サンプルコード

[CommandMethod( "INSERT_SAMPLE" )]
public void InsertSample()
{
    Transaction trans = null;
    try
    {
        Document active_doc = Application.DocumentManager.MdiActiveDocument;
        Database active_db = active_doc.Database;
        Editor edit = active_doc.Editor;
        trans = active_db.TransactionManager.StartTransaction();

        // Sample ブロック定義のオブジェクトIDを取得する
        BlockTable blk_tbl
            = (BlockTable)trans.GetObject( active_db.BlockTableId, OpenMode.ForRead );
        if( !blk_tbl.Has( "Sample" ) )
        {
            edit.WriteMessage( "\nSample ブロックが登録されていません" );
            return;
        }
        ObjectId sample_id = blk_tbl[ "Sample" ];

        // Sample ブロック参照を作成する
        BlockReference sample_ref = new BlockReference( Point3d.Origin, sample_id );
        ObjectId model_space_id = blk_tbl[ BlockTableRecord.ModelSpace ];
        BlockTableRecord model_space
            = (BlockTableRecord)trans.GetObject( model_space_id, OpenMode.ForWrite );
        model_space.AppendEntity( sample_ref );
        trans.AddNewlyCreatedDBObject( sample_ref, true );

        // Name 属性定義を取得する
        BlockTableRecord sample_blk
            = (BlockTableRecord)trans.GetObject( sample_id, OpenMode.ForRead );
        AttributeDefinition name_def = null;
        foreach( ObjectId chiled_id in sample_blk )
        {
            // Sample ブロック定義を走査して、名称が "Name" の属性定義を探す
            DBObject chiled_obj = trans.GetObject( chiled_id, OpenMode.ForRead );
            if( chiled_obj.GetType() != typeof( AttributeDefinition ) )
            {
                continue;
            }

            AttributeDefinition att_def = (AttributeDefinition)chiled_obj;
            if( att_def.Tag.ToUpper() != "NAME" )
            {
                continue;
            }
            name_def = att_def;
            break;
        }
        if( name_def == null )
        {
            edit.WriteMessage( "\nName 属性定義が登録されていません" );
            return;
        }

        // Name 属性参照を作成する
        AttributeReference name_ref = new AttributeReference();
        name_ref.SetAttributeFromBlock( name_def, Matrix3d.Identity );
        name_ref.TextString = "SampleName";
        
        // 属性参照オブジェクトをブロック参照に登録する
        sample_ref.AttributeCollection.AppendAttribute( name_ref );
        trans.AddNewlyCreatedDBObject( name_ref, true );

        trans.Commit();
    }
    catch( System.Exception ex )
    {
        Editor edit = Application.DocumentManager.MdiActiveDocument.Editor;
        edit.WriteMessage( "\n" + ex.Message );
    }
    finally
    {
        if( trans != null )
        {
            trans.Dispose();
        }
    }
}

環境によっては、コンパイル時に "この参照を解決できませんでした" という警告が出ます。この場合 "acdbmgd.dll" と "acmgd.dll" を削除し、 AutoCAD のインストールディレクトリから参照しなおしてください。

実行結果

[INSERT_SAMPLE] コマンド発行後

Fig01: [INSERT_SAMPLE] 発行後

ブロック定義とブロック参照

ソースコードの解説に入る前に、属性定義と属性参照の関係について解説します。

「属性参照」とは、ブロックの一部として図面に表示されている文字列のことです。挿入したブロック参照を右クリック→[属性編集] で表示される [拡張属性編集] ダイアログを使って文字列の内容を変えることができますが、この編集結果は他のブロック参照に影響しません。このことから、属性参照はブロック内の図形と違ってブロック参照オブジェクトごとに別々のインスタンスが生成されていることがわかります。

しかし[拡張属性編集] ダイアログを見ていただければわかるとおり、属性参照にはかなりたくさんの設定項目があります。表示内容はもとより、表示位置、文字高さ、回転角度、等々。属性参照オブジェクトを生成するたびにこれらすべてを設定するのは大変な作業です。そこでそのテンプレートとしてあらかじめブロックに定義しておいたものが「属性定義」です。

上記のような関係がありますので、属性参照を作成するには以下のような少々ややこしい手順を踏みます。

  1. ブロック定義から必要な属性定義を取得する。
  2. 属性参照のインスタンスを生成し、取得した属性定義で初期化する。
  3. 生成した属性参照のインスタンスをブロック参照に登録する。

サンプルコード解説

ブロック定義のオブジェクトIDを取得する

ブロック定義のオブジェクトIDを取得する方法の詳細は、弊社サイト内の "ブロックを挿入する" を参照してください。

今回はブロック参照オブジェクトの生成以外に属性定義の取得でも使用しますので、オブジェクトIDを変数として取得しました。

ブロック参照を作成する

これも詳細は "ブロックを挿入する" で解説しています。

ブロック参照は、属性参照を登録するよりも前にモデルスペースに登録しなければなりません。さもなければ eNoDatabase エラーが発生しますので気をつけてください。

属性定義を取得する

属性参照を初期化するために、まずは属性定義を取得します。そのためには、ブロック定義の従属属性を走査(*1)(*2)しなければなりません。

BlokTableRecordforeach にかけると、従属図形のオブジェクトIDを順番に取得することができます。このオブジェクトIDを使って図形を開き、「型が AttributeDefinition」かつ「名称が "Name"」のものを探します。

従属図形の型を調べる

従属図形にはどんな型のオブジェクトが入っているかわかりませんので、いきなりキャストするわけにはいきません。そこでまず GetType() メソッドを使ってオブジェクトの型判定をします。

名称を判定する

AttributeDefinition.Tag プロパティで属性定義の名称を取得することができます。これが望みのものと一致するかを判定すればいいでしょう。ただし、属性定義の名称は大文字と小文字を区別しないことに注意してください。

属性参照を作成する

まず属性参照のインスタンスを生成し、SetAttributeFromBlock() メソッドを使って属性定義の設定と一致させます。SetAttributeFromBlock() の第二引数はどのように変形するかを Matrix3d で指定しますが、変形の必要がなければ Matrix3d.Identity を指定すればいいでしょう(*3)。

属性参照オブジェクトをブロック参照に登録する

先述の通り、属性参照はブロック参照ごとに別々のインスタンスを作成しますので、その登録先はモデルスペースやブロック定義ではなくブロック参照です。また登録先のブロック参照オブジェクトは、事前にモデルスペースに登録しておいてください。

更新履歴

2008/10/10
新規作成。
2008/10/13
finally ブロックの処理にミスがあったため修正。
(MINERVA 深津貴成)
  1. ^ 対象から連続的に値を取り出すことを "走査" あるいは "イテレイト(iterate)" と言います。ブラウン管の "走査線" の走査と同じ意味です。C# では foreach でループを回すこととほぼ同義です。
  2. ^ ブロック定義から属性定義を取り出す処理はよくやると思うんですが、今のところ走査するしか方法がなさそうです。そこで属性定義の取り出し処理だけを関数化しておくと、何かと便利です。
  3. ^ ブロック参照は横に伸ばした文字は普通の形のままにしたい場合などに使用します。逆にブロック参照の変形と合わせるなら BlockReference.BlockTransform プロパティを指定します。