TheSwamp

Code Red => .NET => Topic started by: zoltan on March 11, 2011, 11:40:50 AM

Title: Importing a Layout from a Template
Post by: zoltan on March 11, 2011, 11:40:50 AM
I'm trying to copy a layout from a template drawing into the current drawing.

I have searched the forums and I followed this thread (http://www.theswamp.org/index.php?topic=24684.0) to this thread (http://www.theswamp.org/index.php?topic=14290.msg178697#msg178697) and I think I have an idea of what needs to happen.

1. Get the database from the template file.
2. Find the layout in the Layout Dictionary.
3. Get the Block Table Record for the layout's paperspace.
4. Clone the BTR and add it to the drawing's Block Table.
5. Clone the layout and add it to the Layout Dictionary with the new BTR.

When the drawing is regenerated, I see the new layout in the layout tabs, but AutoCAD crashes when I try to activate it.  In ArxDbg, I can see the new block table record in the block table and the new layout in the layout dictionary, but it crashes when I try to view the items.

I think I am missing something BIG!  Does anyone have a working solution for this?

AutoCAD 2011

Microsoft Visual Studio 2008
Version 9.0.30729.1 SP

Microsoft .NET Framework
Version 3.5 SP1

Windows XP Pro SP3

Code: [Select]
        /// <summary>
        /// Imports a Layout from a template
        /// </summary>
        /// <param name="path">
        /// The path to the template file including the extension
        /// </param>
        /// <param name="name">
        /// The name of the layout to import.
        /// </param>
        public static void ImportLayout(string path, string name)
        {
            // the destination document and database
            Document dstDocument = Application.DocumentManager.MdiActiveDocument;
            Database dstDatabase = dstDocument.Database;
            Editor dstEditor = dstDocument.Editor;

            // get the sourse database from the template file
            Database srcDatabase = new Database(false, true);
            srcDatabase.ReadDwgFile(path, FileOpenMode.OpenForReadAndReadShare, false, null);

            using (Transaction srcTrans = srcDatabase.TransactionManager.StartTransaction())
            {

                DBDictionary srcLayoutDictionary = (DBDictionary)srcTrans.GetObject(srcDatabase.LayoutDictionaryId, OpenMode.ForRead);

                // go through the layout dictionary to find the layout that matches the name.
                foreach (DictionaryEntry layout in srcLayoutDictionary)
                {
                    if (layout.Key.ToString() == name)
                    {
                        ObjectId srcLayoutId = (ObjectId)layout.Value;
                        Layout srcLayout = (Layout)srcTrans.GetObject(srcLayoutId, OpenMode.ForRead);

                        // the block table record for the layout.
                        ObjectId srcLayoutBtr = srcLayout.BlockTableRecordId;

                        // clone the layout's block table record.
                        IdMapping btrMapping = new IdMapping();
                        dstDatabase.WblockCloneObjects(new ObjectIdCollection(new ObjectId[] { srcLayoutBtr }), dstDatabase.BlockTableId, btrMapping, DuplicateRecordCloning.Replace, false);

                        // find the ObjectId of the new block table record.
                        ObjectId dstLayoutBtr = ObjectId.Null;
                        foreach (IdPair pair in btrMapping)
                        {
                            ObjectId key = pair.Key;

                            if (ObjectId.Equals(key, srcLayoutBtr))
                            {
                                dstLayoutBtr = pair.Value;
                            }
                        }

                        // clone the layout.
                        IdMapping layoutMapping = new IdMapping();
                        dstDatabase.WblockCloneObjects(new ObjectIdCollection(new ObjectId[] { srcLayoutId }), dstDatabase.LayoutDictionaryId, layoutMapping, DuplicateRecordCloning.Replace, false);

                        // find the ObjectId of the new layout.
                        foreach (IdPair pair in layoutMapping)
                        {
                            ObjectId key = pair.Key;

                            if (ObjectId.Equals(key, srcLayoutId))
                            {

                                ObjectId dstLayoutId = pair.Value;

                                using (Transaction dstTrans = dstDatabase.TransactionManager.StartTransaction())
                                {
                                    Layout dstLayout = (Layout)dstTrans.GetObject(dstLayoutId, OpenMode.ForWrite);

                                    // add the new layout to the Layout Dictionary.
                                    dstLayout.AddToLayoutDictionary(dstDatabase, dstLayoutBtr);

                                    dstTrans.Commit();
                                }
                            }
                        }
                    }
                }

                srcTrans.Commit();
            }

            // regenerate the drawing.
            dstEditor.Regen();
        }
Title: Re: Importing a Layout from a Template
Post by: T.Willey on March 11, 2011, 03:27:06 PM
This worked for me.  I tried other ways, but couldn't get them to work correctly, as they would fatally error, and crash acad with and ' ePermanetlyErased ' error.

Code: [Select]
    public void Main ()
    {
    Document toDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument as Document;
    using( DocumentLock toLock = toDoc.LockDocument() ) {
    Database toDb = toDoc.Database as Database;
   
    string Path = @"c:/test/blank.dwg";
    string LoName = "test";
   
    Document fromDoc;
    Database fromDb = MyUtility.GetDatabaseAtPath( Path, toDb, FileOpenMode.OpenForReadAndAllShare, FindFileHint.Default, out fromDoc );
    using( Transaction toTrans = toDb.TransactionManager.StartTransaction() ) {
    using( Transaction fromTrans = fromDb.TransactionManager.StartTransaction() ) {
    DBDictionary fromLoDict = fromTrans.GetObject( fromDb.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    ObjectIdCollection IdCol = new ObjectIdCollection();
    foreach ( DBDictionaryEntry de in fromLoDict ) {
    if ( de.Key != LoName ) continue;
    IdCol.Add( de.Value );
    }
    fromDb.WblockCloneObjects( IdCol, toDb.LayoutDictionaryId, new IdMapping(), DuplicateRecordCloning.Ignore, false );
    }
    toTrans.Commit();
    }
    fromDb.Dispose();
    }
    }

Code: [Select]
public static Database GetDatabaseAtPath ( string path, Database db, Autodesk.AutoCAD.DatabaseServices.FileOpenMode openMode, FindFileHint findOpt, out Document doc )
{
// If doc != null, then the calling function must lock the document passed out.
// Also, the calling function must NOT set the HostApplication.WorkingDatabase to the
// database returned.
//
// If doc == null, then the calling function must dispose of the database returned.
// Also, the calling function must set the HostApplication.WorkingDatabase to the
// database returned, if working with text or attributes.
//
// If null is returned, then the drawing is read-only to the calling program, and the
// calling program wants to make changes to the database.

doc = GetDocumentFrom( AcadApp.DocumentManager, path );
if ( doc != null ) return doc.Database;
if (
openMode == FileOpenMode.OpenForReadAndReadShare
&&
( ( File.GetAttributes ( path ) & FileAttributes.ReadOnly ) == FileAttributes.ReadOnly )
)
return null;
string FoundAtPath = HostApplicationServices.Current.FindFile( path, db, findOpt );
if ( string.IsNullOrEmpty( FoundAtPath ) ) return null;
Database Db = new Database( false, true );
Db.ReadDwgFile( FoundAtPath, openMode, true, null );
return Db;
}
Title: Re: Importing a Layout from a Template
Post by: zoltan on March 11, 2011, 04:21:58 PM
WOW!

I guess I wasn't missing something, I was OVER COMPLICATING things!

So it makes sense that since the dictionary holds a pointer to the Block Table Record, cloning the dictionary will clone the BTR also.

What is the Layout.AddToLayoutDictionary supposed to be used for?
Title: Re: Importing a Layout from a Template
Post by: T.Willey on March 11, 2011, 04:32:08 PM
I'm not sure.  I was trying to use the ' WBlockClone ' method, but can't seem to get that to work, so I went this way and it worked.  I'm still trying some things, as this will copy straight, and I think the other way, one could copy and then rename the layout before adding it completely.  If that makes sense.
Title: Re: Importing a Layout from a Template
Post by: Jeff H on March 11, 2011, 05:41:15 PM
Also
You can create a new layout with the layermanger then use the CopyFrom() method but still need to add viewports objects etc.. wil post a example.

I believe since layouts do ont derive from the entity class is why wblockclone causes problems

Here is a similiar one that uses CopyFrom for PlotSettings
http://www.theswamp.org/index.php?topic=31867.msg398478#msg398478
Title: Re: Importing a Layout from a Template
Post by: T.Willey on March 11, 2011, 06:31:01 PM
Also
You can create a new layout with the layermanger then use the CopyFrom() method but still need to add viewports objects etc.. wil post a example.

Layouts have an Initialize method, that should take care of some of the issues with making a new Layout.


I believe since layouts do ont derive from the entity class is why wblockclone causes problems

You wouldn't think so, as the WblockClone is a method of the DBObject class, and Layout inherits from that, but I can't get it to work at all.
Title: Re: Importing a Layout from a Template
Post by: Jeff H on March 11, 2011, 07:56:04 PM
I believe since layouts do ont derive from the entity class is why wblockclone causes problems

You wouldn't think so, as the WblockClone is a method of the DBObject class, and Layout inherits from that, but I can't get it to work at all.

Sorry,
Way off there  was going off memory which I should not do.



Code: [Select]
   [CommandMethod("CopyLayout")]
            public void CopyLayout()
            {
                string layoutName = "Test";
                string fileName = @"C:\Users\Jeff\Desktop\Temp\Test\LayoutTest.dwt";


                Database templateDb = new Database(false, true) as Database;
                templateDb.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, true, "");

                Document doc = Application.DocumentManager.MdiActiveDocument;
                Editor ed = doc.Editor;
                Database db = doc.Database;

                LayoutManager layoutManager = LayoutManager.Current;

                using (Transaction trx = db.TransactionManager.StartTransaction())
                using (Transaction templateTrx = templateDb.TransactionManager.StartTransaction())           
                {   
                    DBDictionary layoutDictionary = trx.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;


                    if (layoutDictionary.Contains(layoutName))
                    {
                        ed.WriteMessage("\nThis Layout is already in drawing.");
                        return;
                    }

                    DBDictionary templateLayoutDictionary = templateTrx.GetObject(templateDb.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
                   
                    if (!(templateLayoutDictionary.Contains(layoutName)))
                    {
                        ed.WriteMessage("\nTemplate does not contain a Layout with that name.");
                        return;
                    }
                 
                        ObjectId newLayoutId = layoutManager.CreateLayout(layoutName);
                        Layout newLayout = trx.GetObject(newLayoutId, OpenMode.ForWrite) as Layout;

                        Layout templateLayout = templateLayoutDictionary.GetAt(layoutName).GetObject(OpenMode.ForWrite) as Layout;
                        newLayout.CopyFrom(templateLayout);
                                       
                        BlockTableRecord templateLayoutBtr = templateTrx.GetObject(templateLayout.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;

                        ObjectIdCollection objIdColl = new ObjectIdCollection();
                        foreach (ObjectId id in templateLayoutBtr)
                        {
                            objIdColl.Add(id);
                        }

                        BlockTable extBt = templateDb.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                        IdMapping map = new IdMapping();
                        db.WblockCloneObjects(objIdColl, newLayout.BlockTableRecordId, map, DuplicateRecordCloning.Replace, false);

                        newLayout.Initialize();           
                   
                    templateTrx.Commit();
                    trx.Commit();                   
                }
                layoutManager.CurrentLayout = layoutName;
            }

*******Edit******
Removed NOT operator @   if (layoutDictionary.Contains(layoutName))
Title: Re: Importing a Layout from a Template
Post by: Jeff H on March 11, 2011, 08:20:50 PM
Layouts have an Initialize method, that should take care of some of the issues with making a new Layout.

The documentation does not specify, but when you use LayoutManager to create a layout it asscioates it with a BlockTableRecord.
There is Layout.AddToLayoutDictionary, but have not messed with it. I guess you would have to create a new Btr yourself?
Title: Re: Importing a Layout from a Template
Post by: kaefer on March 14, 2011, 06:20:34 AM
Code: [Select]
    [CommandMethod("CopyLayout")]
        ....
        Database templateDb = new Database(false, true) as Database;

Looks good, works great, but shouldn't you dispose the side database when you're done with it?

Regards
Title: Re: Importing a Layout from a Template
Post by: T.Willey on March 14, 2011, 05:39:27 PM
Looks like you have the right idea Jeff.  Here is a different version.  One that doesn't use the layout manager, so in theory, it can be changed easily to work with a drawing open by it's database only.

Code: [Select]
    [CommandMethod("testcopylayout", CommandFlags.Session)]
    public void Main ()
    {
    Document toDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument as Document;
    using( DocumentLock toLock = toDoc.LockDocument() ) {
    Database toDb = toDoc.Database as Database;
   
    string Path = @"c:/test/blank.dwg";
    string LoName = "test";
   
    string BlkName = "*Paper_Space";
    int LoCnt = 0;
   
    Document fromDoc;
    Database fromDb = MyUtility.GetDatabaseAtPath( Path, toDb, FileOpenMode.OpenForReadAndAllShare, FindFileHint.Default, out fromDoc );
    using( Transaction toTrans = toDb.TransactionManager.StartTransaction() ) {
    using( Transaction fromTrans = fromDb.TransactionManager.StartTransaction() ) {
    DBDictionary fromLoDict = fromTrans.GetObject( fromDb.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    ObjectIdCollection IdCol = new ObjectIdCollection();
    foreach ( DBDictionaryEntry de in fromLoDict ) {
    if ( de.Key != LoName ) continue;
    Layout fromLo = fromTrans.GetObject( de.Value, OpenMode.ForRead ) as Layout;
    BlockTableRecord fromBtr = fromTrans.GetObject( fromLo.BlockTableRecordId, OpenMode.ForRead ) as BlockTableRecord;
    foreach ( ObjectId id in fromBtr ) { IdCol.Add( id ); }
   
    BlockTable toBt = toTrans.GetObject( toDb.BlockTableId, OpenMode.ForWrite ) as BlockTable;
    while ( toBt.Has( BlkName + LoCnt ) ) LoCnt++;
    Layout toLo = new Layout();
    toLo.CopyFrom( fromLo );
    toLo.LayoutName = fromLo.LayoutName;
    DBDictionary toLoDict = toTrans.GetObject( toDb.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    if ( toLoDict.Contains( toLo.LayoutName ) ) {
    PromptResult pr;
    do {
    pr = toDoc.Editor.GetString( "\n Layout name exists already, enter new name: " );
    if ( pr.Status == PromptStatus.Cancel ) return;
    } while ( string.IsNullOrEmpty( pr.StringResult ) || toLoDict.Contains( pr.StringResult ) );
    toLo.LayoutName = pr.StringResult;
    }
    BlockTableRecord toBtr = new BlockTableRecord();
    toBtr.Name = BlkName + LoCnt;
    toBt.Add( toBtr );
    toTrans.AddNewlyCreatedDBObject( toBtr, true );
    fromDb.WblockCloneObjects( IdCol, toBtr.ObjectId, new IdMapping(), DuplicateRecordCloning.Ignore, false );
    toLo.AddToLayoutDictionary( toDb, toBtr.ObjectId );
    toTrans.AddNewlyCreatedDBObject( toLo, true );
    }
    }
    toTrans.Commit();
    }
    fromDb.Dispose();
    }
    }
    }
Title: Re: Importing a Layout from a Template
Post by: Jeff H on March 14, 2011, 11:07:41 PM

Looks good, works great, but shouldn't you dispose the side database when you're done with it?

Regards

Yes sir,
thanks for catching that.

Looks like you have the right idea Jeff.  Here is a different version.  One that doesn't use the layout manager, so in theory, it can be changed easily to work with a drawing open by it's database only.

Nice you figured out how to use AddToLayoutDictionary().
Thanks for the good pointers.
Title: Re: Importing a Layout from a Template
Post by: T.Willey on March 15, 2011, 11:09:20 AM
It felt so good to be doing something in C# again I couldn't quit.   :lmao:

Code: [Select]
    public bool AddLayoutToDatabase ( Database db, string name, string templatePath, string templateLoName )
    {
    string BlkName = "*Paper_Space";
    int LoCnt = 0;
   
    using ( Transaction Trans = db.TransactionManager.StartTransaction() ) {
    DBDictionary LoDict = Trans.GetObject( db.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    if ( LoDict.Contains( name ) ) return false;
   
    Document fromDoc;
    DocumentLock fromDocLock = null;
    Database fromDb = MyUtility.GetDatabaseAtPath( templatePath, HostApplicationServices.WorkingDatabase, FileOpenMode.OpenForReadAndAllShare, FindFileHint.Default, out fromDoc );
    if ( fromDb == null ) return false;
if ( fromDoc != null ) fromDocLock = fromDoc.LockDocument();
    using ( Transaction fromTrans = fromDb.TransactionManager.StartTransaction() ) {
    DBDictionary fromLoDict = fromTrans.GetObject( fromDb.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    Layout fromLo = null;
    foreach ( DBDictionaryEntry de in fromLoDict ) {
    if ( de.Key == templateLoName ) fromLo = fromTrans.GetObject( de.Value, OpenMode.ForRead ) as Layout;
    if ( fromLo != null ) break;
    }
    if ( fromLo == null ) {
    if ( fromDocLock != null ) fromDocLock.Dispose();
    else fromDb.Dispose();
    return false;
    }
    ObjectIdCollection IdCol = new ObjectIdCollection();
    BlockTableRecord fromBtr = fromTrans.GetObject( fromLo.BlockTableRecordId, OpenMode.ForRead ) as BlockTableRecord;
    foreach ( ObjectId id in fromBtr ) { IdCol.Add( id ); }
   
    BlockTable Bt = Trans.GetObject( db.BlockTableId, OpenMode.ForWrite ) as BlockTable;
    while ( Bt.Has( BlkName + LoCnt ) ) LoCnt++;
    Layout Lo = new Layout();
    Lo.LayoutName = name;
    Lo.CopyFrom( fromLo );
    BlockTableRecord Btr = new BlockTableRecord();
    Btr.Name = BlkName + LoCnt;
    Bt.Add( Btr );
    Trans.AddNewlyCreatedDBObject( Btr, true );
    fromDb.WblockCloneObjects( IdCol, Btr.ObjectId, new IdMapping(), DuplicateRecordCloning.Ignore, false );
    Lo.AddToLayoutDictionary( db, Btr.ObjectId );
    Trans.AddNewlyCreatedDBObject( Lo, true );
    }
    Trans.Commit();
    if ( fromDocLock != null ) fromDocLock.Dispose();
    else fromDb.Dispose();
    }
    return true;
    }
   
    public bool AddLayoutToDatabase ( Database db, string templatePath, string templateLoName )
    {
    string BlkName = "*Paper_Space";
    int LoCnt = 0;
   
    using ( Transaction Trans = db.TransactionManager.StartTransaction() ) {
    DBDictionary LoDict = Trans.GetObject( db.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    if ( LoDict.Contains( templateLoName ) ) return false;
   
    Document fromDoc;
    DocumentLock fromDocLock = null;
    Database fromDb = MyUtility.GetDatabaseAtPath( templatePath, HostApplicationServices.WorkingDatabase, FileOpenMode.OpenForReadAndAllShare, FindFileHint.Default, out fromDoc );
    if ( fromDb == null ) return false;
if ( fromDoc != null ) fromDocLock = fromDoc.LockDocument();
    using ( Transaction fromTrans = fromDb.TransactionManager.StartTransaction() ) {
    DBDictionary fromLoDict = fromTrans.GetObject( fromDb.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    Layout fromLo = null;
    foreach ( DBDictionaryEntry de in fromLoDict ) {
    if ( de.Key == templateLoName ) fromLo = fromTrans.GetObject( de.Value, OpenMode.ForRead ) as Layout;
    if ( fromLo != null ) break;
    }
    if ( fromLo == null ) {
    if ( fromDocLock != null ) fromDocLock.Dispose();
    else fromDb.Dispose();
    return false;
    }
    ObjectIdCollection IdCol = new ObjectIdCollection();
    BlockTableRecord fromBtr = fromTrans.GetObject( fromLo.BlockTableRecordId, OpenMode.ForRead ) as BlockTableRecord;
    foreach ( ObjectId id in fromBtr ) { IdCol.Add( id ); }
   
    BlockTable Bt = Trans.GetObject( db.BlockTableId, OpenMode.ForWrite ) as BlockTable;
    while ( Bt.Has( BlkName + LoCnt ) ) LoCnt++;
    Layout Lo = new Layout();
    Lo.LayoutName = templateLoName;
    Lo.CopyFrom( fromLo );
    BlockTableRecord Btr = new BlockTableRecord();
    Btr.Name = BlkName + LoCnt;
    Bt.Add( Btr );
    Trans.AddNewlyCreatedDBObject( Btr, true );
    fromDb.WblockCloneObjects( IdCol, Btr.ObjectId, new IdMapping(), DuplicateRecordCloning.Ignore, false );
    Lo.AddToLayoutDictionary( db, Btr.ObjectId );
    Trans.AddNewlyCreatedDBObject( Lo, true );
    }
    Trans.Commit();
    if ( fromDocLock != null ) fromDocLock.Dispose();
    else fromDb.Dispose();
    }
    return true;
    }
   
    public bool AddLayoutToDatabase ( Database db, string name )
    {
    string BlkName = "*Paper_Space";
    int LoCnt = 0;
   
    using ( Transaction Trans = db.TransactionManager.StartTransaction() ) {
    DBDictionary LoDict = Trans.GetObject( db.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
    if ( LoDict.Contains( name ) ) return false;
BlockTable Bt = Trans.GetObject( db.BlockTableId, OpenMode.ForWrite ) as BlockTable;
while ( Bt.Has( BlkName + LoCnt ) ) LoCnt++;
Layout Lo = new Layout();
Lo.LayoutName = name;
BlockTableRecord Btr = new BlockTableRecord();
Btr.Name = BlkName + LoCnt;
Bt.Add( Btr );
Trans.AddNewlyCreatedDBObject( Btr, true );
Lo.AddToLayoutDictionary( db, Btr.ObjectId );
Lo.Initialize();
Trans.AddNewlyCreatedDBObject( Lo, true );
    Trans.Commit();
    }
    return true;
    }

Code: [Select]
    [CommandMethod("testcopylayout", CommandFlags.Session)]
    public void Main ()
    {
    Document Doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument as Document;
    Database Db = Doc.Database;
    Editor Ed = Doc.Editor;
    using( DocumentLock DocLock = Doc.LockDocument() ) {
    AddLayoutToDatabase( Db, "test1", @"c:/test/blank.dwg", "test" );
    AddLayoutToDatabase( Db, @"c:/test/blank.dwg", "test" );
    AddLayoutToDatabase( Db, "test2" );
    }
    }
   
    [CommandMethod("testcopylayout1", CommandFlags.Session)]
    public void Main1 ()
    {
    Document Doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument as Document;
    using( DocumentLock DocLock = Doc.LockDocument() ) {
    using ( Database Db = new Database( true, false ) ) {
    AddLayoutToDatabase( Db, "test1", @"c:/test/blank.dwg", "test" );
    AddLayoutToDatabase( Db, @"c:/test/blank.dwg", "test" );
    AddLayoutToDatabase( Db, "test2" );
    Db.SaveAs( @"c:/test/createlayout.dwg", DwgVersion.Current );
    }
    }
    }

Code: [Select]
public static Database GetDatabaseAtPath ( string path, Database db, Autodesk.AutoCAD.DatabaseServices.FileOpenMode openMode, FindFileHint findOpt, out Document doc )
{
// If doc != null, then the calling function must lock the document passed out.
// Also, the calling function must NOT set the HostApplication.WorkingDatabase to the
// database returned.
//
// If doc == null, then the calling function must dispose of the database returned.
// Also, the calling function must set the HostApplication.WorkingDatabase to the
// database returned, if working with text or attributes.
//
// If null is returned, then the drawing is read-only to the calling program, and the
// calling program wants to make changes to the database.

doc = GetDocumentFrom( AcadApp.DocumentManager, path );
if ( doc != null ) return doc.Database;
if (
openMode == FileOpenMode.OpenForReadAndReadShare
&&
( ( File.GetAttributes ( path ) & FileAttributes.ReadOnly ) == FileAttributes.ReadOnly )
)
return null;
string FoundAtPath = HostApplicationServices.Current.FindFile( path, db, findOpt );
if ( string.IsNullOrEmpty( FoundAtPath ) ) return null;
Database Db = new Database( false, true );
Db.ReadDwgFile( FoundAtPath, openMode, true, null );
return Db;
}
}
Title: Re: Importing a Layout from a Template
Post by: zoltan on March 18, 2011, 12:17:28 PM
Thanks Willey,

That looks great!!
Title: Re: Importing a Layout from a Template
Post by: T.Willey on March 18, 2011, 01:06:32 PM
You're welcome Zoltan.  Glad it works for you.
Title: Re: Importing a Layout from a Template
Post by: dull_blades on January 16, 2012, 05:38:17 PM
Hi folks!

Sorry to dredge up an old thread.  I am creating new layouts (by importing from a template) just as the OP is doing.  This thread has helped tremendously.

Unfortunately I am seeing some issues that I wanted to investigate.

I originally started my own routine by piecing together a bunch of samples I found on the internet. I was able to get my routine to work, but with a few issues.  So I did some searching and found this thread. 

After implementing the solution by Jeff above (Reply #6), I am seeing the same issues as I have when using my code.  Basically, the code works great on an empty drawing file (new file).  But if, I run Jeff's solution (or mine) on a drawing containing modelspace drawing content, existing layouts, viewports, etc... the newly added layout acts as if it has not been initialized.  Once I switch to a different layout, back to the newly created layout and run a zoom extents on the layout, everything looks correct.

Am I missing something that was not discussed in this thread?  Has anyone else experienced this behaviour?

Thanks!

Title: Re: Importing a Layout from a Template
Post by: zoltan on January 16, 2012, 08:23:55 PM
I've actually been having the same issues.  After I import a layout from a template, I cannot get any of the viewports with the Layout.GetViewports method until I make the layout active.
Title: Re: Importing a Layout from a Template
Post by: fixo on January 17, 2012, 04:44:14 AM
Wouldn't help addind this lineat the end?
Code: [Select]
ed.UpdateTiledViewPortsFromDataBase();
Title: Re: Importing a Layout from a Template
Post by: zoltan on January 17, 2012, 08:32:35 AM
When I use UpdateTiledViewPortsFromDatabase() or UpdateTiledViewPortsInDatabase(), it gives me eNotApplicable.

The documentation gives a lot of help on how to use these methods!  :roll:
Title: Re: Importing a Layout from a Template
Post by: Jeff H on January 17, 2012, 04:51:25 PM
Layouts have an Initialize method, that should take care of some of the issues with making a new Layout.

The documentation does not specify, but when you use LayoutManager to create a layout it asscioates it with a BlockTableRecord.
There is Layout.AddToLayoutDictionary, but have not messed with it. I guess you would have to create a new Btr yourself?

Are you calling Layout.Initialize?
Title: Re: Importing a Layout from a Template
Post by: Jeff H on January 17, 2012, 05:50:21 PM
the newly added layout acts as if it has not been initialized.  Once I switch to a different layout, back to the newly created layout and run a zoom extents on the layout, everything looks correct.


I think this is what you are after,
updating the code posted a while back I forgot to dispose the database(thanks kaefer for pointing it out) but the main change is this

Code: [Select]
                    newLayout.Initialize();
                    PlotSettingsValidator psv = PlotSettingsValidator.Current;
                    psv.SetZoomToPaperOnUpdate(newLayout, true);

Even for being just a simple quickly thrown together example it really needs to be  restructured, but the previous code with 2 changes mentioned
 
Code: [Select]
        [CommandMethod("CopyLayout")]
        public void CopyLayout()
        {
            string layoutName = "Test";
            string fileName = @"C:\Testing\LayoutTest.dwt";

            using (Database templateDb = new Database(false, true) as Database)
            {
                templateDb.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, true, "");
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Editor ed = doc.Editor;
                Database db = doc.Database;
                LayoutManager layoutManager = LayoutManager.Current;
                using (Transaction trx = db.TransactionManager.StartTransaction())
                using (Transaction templateTrx = templateDb.TransactionManager.StartTransaction())
                {
                    DBDictionary layoutDictionary = trx.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;

                    if (layoutDictionary.Contains(layoutName))
                    {
                        ed.WriteMessage("\nThis Layout is already in drawing.");
                        return;
                    }
                    DBDictionary templateLayoutDictionary = templateTrx.GetObject(templateDb.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
                    if (!(templateLayoutDictionary.Contains(layoutName)))
                    {
                        ed.WriteMessage("\nTemplate does not contain a Layout with that name.");
                        return;
                    }
                    ObjectId newLayoutId = layoutManager.CreateLayout(layoutName);
                    Layout newLayout = trx.GetObject(newLayoutId, OpenMode.ForWrite) as Layout;
                    Layout templateLayout = templateLayoutDictionary.GetAt(layoutName).GetObject(OpenMode.ForWrite) as Layout;
                    newLayout.CopyFrom(templateLayout);
                    BlockTableRecord templateLayoutBtr = templateTrx.GetObject(templateLayout.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;
                    ObjectIdCollection objIdColl = new ObjectIdCollection();
                    foreach (ObjectId id in templateLayoutBtr)
                    {
                        objIdColl.Add(id);
                    }
                    BlockTable extBt = templateDb.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                    IdMapping map = new IdMapping();
                    db.WblockCloneObjects(objIdColl, newLayout.BlockTableRecordId, map, DuplicateRecordCloning.Replace, false);
                    newLayout.Initialize();
                    PlotSettingsValidator psv = PlotSettingsValidator.Current;
                    psv.SetZoomToPaperOnUpdate(newLayout, true);
                 
                    templateTrx.Commit();
                    trx.Commit();
                }
                layoutManager.CurrentLayout = layoutName;
            }
           
        }
Title: Re: Importing a Layout from a Template
Post by: Bryco on January 17, 2012, 08:37:46 PM
Are the viewports on?(vp.On = true;)  I think when you copy a vp manually it's default is off.
Title: Re: Importing a Layout from a Template
Post by: dull_blades on January 19, 2012, 11:58:43 AM
Uggg... still stumped.

Jeff, I grabbed your update code and updated mine with the same updates you posted here and still have the same issue with your code and my code.  Everything works great in a brand new drawing file, but fails to "initialize" the imported layout in certain existing drawings.

Attached are a couple of files someone can test (time permitting).  The "Layout_Template.dwt" file contains a layout named "Template" that I am importing in both cases.  The "ImportLayoutTest.dwg" is the file that is having issues.  This was a file that had a lot of content in it.  I deleted everything in the file and purged it to see if I could narrow it down to a drawing object, but even after cleaning up the drawing, the issue still occurs.

Here are the steps to see it work properly:

-Start AutoCAD with a new drawing file.
-Run the CopyLayout command importing the "Template" layout from the attached "Layout_Template.dwt" file.
-Everything works as expected (Except for that strange viewport that pops in around the outside of the paper layout).

Here are the steps to see it fail:

-Open the "ImportLayoutTest.dwg" file.
-Run the CopyLayout command importing the "Template" layout from the attached "Layout_Template.dwt" file.
-You end up with a Paper space layout with nothing on it.  The "paper" is displayed properly, but nothing else appears to have imported.  If you do a zoom extents, then the "paper" goes away and the objects from the template are displayed.  If you switch to another layout then switch back to the newly imported layout and do another zoom extents, everything shows up and displays as expected.

I don't understand what the difference between the existing file and a new file are that casue this behaviour.

Any ideas?

Title: Re: Importing a Layout from a Template
Post by: dull_blades on January 19, 2012, 12:01:03 PM
By the way...

I even deleted the viewports out of the template to see if they were part of the issue, but after deleting them, the issue persists.
Title: Re: Importing a Layout from a Template
Post by: Jeff H on January 19, 2012, 01:04:02 PM
What happens if you do the CopyLayout() command from paperspace?
 
 
Title: Re: Importing a Layout from a Template
Post by: dull_blades on January 19, 2012, 01:41:47 PM
Well what do you know!  It works great from PaperSpace!

I thought I tried that yesterday and got a fatal error, but it seems to be working now!   So why would it work from ModelSpace from a new drawing, but not from ModelSpace of an existing drawing?

Title: Re: Importing a Layout from a Template
Post by: GVDB on January 24, 2012, 09:41:54 AM
Hi Guys,

great thread here.
I try to use your code but get stuck on it.

What I want is: from an empty new drawing clone an Inventor dwg with WBlockCloneObjects.
When I open the inventor dwg in autocad it is just a layout but with Inventor objects in it.

With your code I get the ObjectIdCollection (count = 23 in my case)
Create a new layout with the size and name of the inventor layout name, but I can't get the inventor objects to clone in my new autocad layout.

Do you have a clue?

Code: (for cloning autocad dwg it works fine)

    public static Database WblockCloneFromExternalDWGfile(string filename, bool isinventorfile)
    {
        using (DocumentLock targetDocumentLock = Active.Document.LockDocument())
        {
            Database targetDatabase = Active.WorkingDatabase;
            Database sourchDatabase = new Database(false, true);
            sourchDatabase.ReadDwgFile(filename, FileOpenMode.OpenForReadAndAllShare, true, "");
           
            using (Transaction sourchTransaction = sourchDatabase.TransactionManager.StartTransaction())
            {
                BlockTable sourchBlockTable = (BlockTable)sourchDatabase.BlockTableId.GetObject(OpenMode.ForRead);
                BlockTableRecord sourchSpace;
                if (isinventorfile) { sourchSpace = (BlockTableRecord)sourchBlockTable[BlockTableRecord.PaperSpace].GetObject(OpenMode.ForRead); }
                else { sourchSpace = (BlockTableRecord)sourchBlockTable[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForRead); }

                ObjectIdCollection objectIDCollection = new ObjectIdCollection();

                foreach (ObjectId objectID in sourchSpace) { objectIDCollection.Add(objectID); }

                using (Transaction targetTransaction = targetDatabase.TransactionManager.StartTransaction())
                {
                    BlockTable targetBlockTable = (BlockTable)targetDatabase.BlockTableId.GetObject(OpenMode.ForWrite);
                    BlockTableRecord targetSpace;
                    IdMapping idMapping = new IdMapping();

                    if (isinventorfile)
                    {
                        //DBDictionary sourchLayoutDirectory = (DBDictionary) sourchDatabase.LayoutDictionaryId.GetObject(OpenMode.ForRead);
                        //Layout sourchLayout = (Layout) sourchLayoutDirectory.GetAt("Blad").GetObject(OpenMode.ForRead);
                        //ObjectId newLayoutId = LayoutManager.Current.CreateLayout(sourchLayout.LayoutName);
                        //Layout targetLayout = (Layout)newLayoutId.GetObject(OpenMode.ForWrite);
                        //LayoutManager.Current.CurrentLayout = targetLayout.LayoutName;
                        ////Layout targetLayout = new Layout();
                        ////targetLayout.LayoutName = sourchLayout.LayoutName;
                        //targetLayout.CopyFrom(sourchLayout);
                        //BlockTableRecord targetBlockTableRecord = new BlockTableRecord();
                        //targetBlockTableRecord.Name = sourchLayout.LayoutName;
                        //targetBlockTable.Add(targetBlockTableRecord);
                        //targetTransaction.AddNewlyCreatedDBObject(targetBlockTableRecord, true);
                        //sourchDatabase.WblockCloneObjects(objectIDCollection, targetBlockTableRecord.ObjectId, idMapping, DuplicateRecordCloning.Replace, false);
                        //targetLayout.AddToLayoutDictionary(targetDatabase, targetBlockTableRecord.ObjectId);
                        ////targetTransaction.AddNewlyCreatedDBObject(targetLayout, true);

                         DBDictionary sourchLayoutDirectory = (DBDictionary)sourchDatabase.LayoutDictionaryId.GetObject(OpenMode.ForRead);
                        Layout sourchLayout = (Layout)sourchLayoutDirectory.GetAt("Blad").GetObject(OpenMode.ForRead);

                        ObjectId targetLayoutId = LayoutManager.Current.CreateLayout(sourchLayout.LayoutName);
                        Layout targetLayout = (Layout)targetLayoutId.GetObject(OpenMode.ForWrite);
                        targetLayout.LayoutName = sourchLayout.LayoutName;
                        targetLayout.CopyFrom(sourchLayout);

                        targetDatabase.WblockCloneObjects(objectIDCollection, targetLayout.BlockTableRecordId, idMapping, DuplicateRecordCloning.Replace, false);

                        targetLayout.Initialize();
                        LayoutManager.Current.CurrentLayout = targetLayout.LayoutName;
                    }
                    else
                    {
                        targetSpace = (BlockTableRecord)targetBlockTable[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite);
                        targetDatabase.WblockCloneObjects(objectIDCollection, targetSpace.ObjectId, idMapping, DuplicateRecordCloning.Replace, false);
                    }

                    targetTransaction.Commit();
                }
            }
            return targetDatabase;
        }
    }
Title: Re: Importing a Layout from a Template
Post by: Eycki on January 25, 2012, 08:34:02 AM
Unfortunatly I can't get this working in my VB.NET program.  I used an online translator C# to VB.NET

Errors like:
Initialize is not a member of 'mymodulename'
BlockTableRecordId is not a member of 'mymodulename'
Value of type 'mymodulename' cannot be converted to 'Autodesk.AutoCAD.DatabaseServices.PlotSettings'
Value of type 'Autodesk.AutoCAD.DatabaseServices.DBObject' cannot be converted to 'mymodulename'

Anybody knows what I did wrong?

Edit: Of course I could always kill some puppies (use the SendStringToExecute command)  :evil:


the newly added layout acts as if it has not been initialized.  Once I switch to a different layout, back to the newly created layout and run a zoom extents on the layout, everything looks correct.


I think this is what you are after,
updating the code posted a while back I forgot to dispose the database(thanks kaefer for pointing it out) but the main change is this

Code: [Select]
                    newLayout.Initialize();
                    PlotSettingsValidator psv = PlotSettingsValidator.Current;
                    psv.SetZoomToPaperOnUpdate(newLayout, true);

Even for being just a simple quickly thrown together example it really needs to be  restructured, but the previous code with 2 changes mentioned
 
Code: [Select]
        [CommandMethod("CopyLayout")]
        public void CopyLayout()
        {
            string layoutName = "Test";
            string fileName = @"C:\Testing\LayoutTest.dwt";

            using (Database templateDb = new Database(false, true) as Database)
            {
                templateDb.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, true, "");
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Editor ed = doc.Editor;
                Database db = doc.Database;
                LayoutManager layoutManager = LayoutManager.Current;
                using (Transaction trx = db.TransactionManager.StartTransaction())
                using (Transaction templateTrx = templateDb.TransactionManager.StartTransaction())
                {
                    DBDictionary layoutDictionary = trx.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;

                    if (layoutDictionary.Contains(layoutName))
                    {
                        ed.WriteMessage("\nThis Layout is already in drawing.");
                        return;
                    }
                    DBDictionary templateLayoutDictionary = templateTrx.GetObject(templateDb.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
                    if (!(templateLayoutDictionary.Contains(layoutName)))
                    {
                        ed.WriteMessage("\nTemplate does not contain a Layout with that name.");
                        return;
                    }
                    ObjectId newLayoutId = layoutManager.CreateLayout(layoutName);
                    Layout newLayout = trx.GetObject(newLayoutId, OpenMode.ForWrite) as Layout;
                    Layout templateLayout = templateLayoutDictionary.GetAt(layoutName).GetObject(OpenMode.ForWrite) as Layout;
                    newLayout.CopyFrom(templateLayout);
                    BlockTableRecord templateLayoutBtr = templateTrx.GetObject(templateLayout.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;
                    ObjectIdCollection objIdColl = new ObjectIdCollection();
                    foreach (ObjectId id in templateLayoutBtr)
                    {
                        objIdColl.Add(id);
                    }
                    BlockTable extBt = templateDb.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                    IdMapping map = new IdMapping();
                    db.WblockCloneObjects(objIdColl, newLayout.BlockTableRecordId, map, DuplicateRecordCloning.Replace, false);
                    newLayout.Initialize();
                    PlotSettingsValidator psv = PlotSettingsValidator.Current;
                    psv.SetZoomToPaperOnUpdate(newLayout, true);
                 
                    templateTrx.Commit();
                    trx.Commit();
                }
                layoutManager.CurrentLayout = layoutName;
            }
           
        }
Title: Re: Importing a Layout from a Template
Post by: bargool on February 15, 2013, 07:49:22 AM
I've actually been having the same issues.  After I import a layout from a template, I cannot get any of the viewports with the Layout.GetViewports method until I make the layout active.
I had this trouble and another one: I made PlotType to Layout and StdScaleType to StdScale1To1, but when I went away from created Layout and return, PlotType changed to Extents and StdScaleType to Fit.
Heh! I Just placed LayoutManager.Current.CurrentLayout at the and of this sequence of commands (Like Jeff made), and everything is fine!  :ugly: