Copying Layout from another drawing, the viewport layer settings are lost

nshupeFMPE3
Advocate
Advocate

Copying Layout from another drawing, the viewport layer settings are lost

nshupeFMPE3
Advocate
Advocate

I have a "template" file that has all the layouts I need. I'm making a function to copy ones that I need from the "template" to the current drawing. The viewports in the template layouts have some layers turned off. The "current" file and the "template" both have the same layers over all. 

The copy from the "template" to the "current" successfully occurs, but all the layers are visible through the viewport of the copied layouts. 

Any help would be greatly appreciated.

/*
 * Opens the "template" database in ReadWrite mode and then passes it into
 * the lambda which is after the =>
 */
ResourceFolder.ReadWriteTemplate((database =>
{
    ObjectIdCollection layoutsToCopy = new ObjectIdCollection();

    /*
     * Opens the layout dictionary for the database and passes the dictionary
     * and the transaction to the lambda
     */
    database.LayoutDictionaryId.TryGetRead<DBDictionary>((templateLayouts, tr) =>
    {
        foreach (DBDictionaryEntry entry in templateLayouts)
        {
            string[] entrySplit = entry.Key.Split(' ');
            if(entrySplit.Length < 1) continue;
            string entryMain = entrySplit[0];
            /*
             * If the _viewmodel (which for the sake of this is a list of strings)
             * contains a string that matches the first part of the layout name
             * it should be copied over
             *
             * for example: Main Layout will split to Main and will be searched in the _viewmodel
             */
            if (_viewModel.Contains(entryMain))
            {
                layoutsToCopy.Add(entry.Value);//add to list to copy

                /*
                 * My attempt to get the viewports from the layout to be copied.
                 * This opens the viewport using the transaction that is passed as the first parameter
                 * then adds each of its viewports from the Acad GetViewport() function
                 */
                entry.Value.TryGetRead<Layout>(tr, layout =>
                {
                    var vpIds = layout.GetViewports();
                    foreach (ObjectId id in vpIds)
                    {
                        layoutsToCopy.Add(id);
                    }
                });
            }
        }

        /*
         * Uses WblockCloneObjects to copy the selected entities to the Active Database
         * which is the "current" database of the open drawing, not the "template" which is the
         * source of the entities.
         */
        
        var mappings = new IdMapping();
        database.WblockCloneObjects(layoutsToCopy, Active.Database.LayoutDictionaryId, mappings, DuplicateRecordCloning.Ignore, false);


    });
}));
0 Likes
Reply
Accepted solutions (1)
985 Views
9 Replies
Replies (9)

ActivistInvestor
Advisor
Advisor

Have you tried using DuplicateRecordCloning.Replace ?

0 Likes

nshupeFMPE3
Advocate
Advocate

@ActivistInvestor I just tried your suggestion. Unfortunately when I do that I get an eWasErased error from the LayoutManager? Not sure what happened there.

0 Likes

ActivistInvestor
Advisor
Advisor

I would suggest that you try cloning a Layout into an empty drawing file with only Layer 0, and then see if the layers that are frozen in the viewports are created in the destination file. 

 

 

0 Likes

nshupeFMPE3
Advocate
Advocate

@ActivistInvestor 

It appears that the frozen layers are not copied over when I do like you described before. 

That is an interesting behavior, I'm still trying to figure out how I can use that.

 

Edit 2: I did a poor test last time, which copied over blocks and such which brought along layers. 
I re-ran the test with a layout that only had a viewport, with its layer settings applied. 
After the layout is copied to the blank file, the only layers that are brought over is the one that the viewport is on, as well as Defpoints. No other layers are copied to the blank file.

 

 

[CommandMethod(nameof(LayoutTest))]
public void LayoutTest()
{
    using (Database outside = new Database(false, true))
    {
        outside.ReadDwgFile(@"C:\Users\me\Downloads\LayoutTest.dwg", FileShare.ReadWrite, true, "");

        ObjectIdCollection idsToCopy = new ObjectIdCollection();
        idsToCopy.Add(Active.Database.GetLayoutId("My Layout 1"));
        if(idsToCopy.Count < 1) return;

        Active.Database.WblockCloneObjects(idsToCopy, outside.LayoutDictionaryId, new IdMapping(), DuplicateRecordCloning.Replace, false);

        outside.SaveAs(@"C:\Users\me\Downloads\LayoutTestDone.dwg", DwgVersion.Current);
    }

}

 

 


Edit 1:
One thing to add, is that the layers that in my real use case, not the test I just ran, The file that will be running the command, and the template file from which layouts will be copied from; They both have the same layers.

Just wanted to add that for some extra context, thanks again for the help.

0 Likes

ActivistInvestor
Advisor
Advisor

There are several ways to deal with the problem. I would probably take the easy way out which is create one object on each layer in the layout that you are going to import which will cause AutoCAD to copy the layers to the destination drawing. After that you can erase those temporary objects in the destination drawing.

nshupeFMPE3
Advocate
Advocate

Thank you for the help, I had no idea how to start trouble shooting this. I think I'm going to get a list of the frozen layers in the template file, then once the viewports are copied I'll refreeze the layers. I'll post the code once I'm finished.

0 Likes

nshupeFMPE3
Advocate
Advocate
Accepted solution

This seems to be working so far, thanks again for the help.

 

*fair warning, uses a lot of extension methods that dont come with the standard .net acad api*

 

/*
 * Read the frozen layers from the template viewport, and then save that object id and
 * list in the dictionary. That way after the copy, the object Id can be used with the
 * mapping to get the new object id, and determine what layers to turn on and off in the
 * copied (new) viewport.
 */
Dictionary<ObjectId, List<string>> viewportFrozenLayers = new Dictionary<ObjectId, List<string>>();
var mappings = new IdMapping();
ObjectIdCollection layoutsToCopy = new ObjectIdCollection();

/*
 * Opens the "template" database in ReadWrite mode and then passes it into
 * the lambda which is after the =>
 */
ResourceFolder.ReadWriteTemplate((database =>
{
    /*
     * Opens the layout dictionary for the database and passes the dictionary
     * and the transaction to the lambda
     */
    database.LayoutDictionaryId.TryGetRead<DBDictionary>((templateLayouts, tr) =>
    {
        foreach (DBDictionaryEntry entry in templateLayouts)
        {
            string[] entrySplit = entry.Key.Split(' ');
            if(entrySplit.Length < 1) continue;
            string entryMain = entrySplit[0];
            /*
             * If the _viewmodel (which for the sake of this is a list of strings)
             * contains a string that matches the first part of the layout name
             * it should be copied over
             *
             * for example: Main Layout will split to Main and will be searched in the _viewmodel
             */
            if (_viewModel.Contains(entryMain))
            {
                layoutsToCopy.Add(entry.Value);//add to list to copy
                
                //open the layout in order to find the viewports and record their frozen layers
                entry.Value.TryGetRead<Layout>(tr, (layout, tran) =>
                {
                    ObjectIdCollection layoutViewportIds = layout.GetViewports();
                    //open each viewport
                    layoutViewportIds.TryForEachRead<Viewport>(tran, viewport =>
                    {
                        if (viewport.Number == 1) return;//if the viewport is number one, its the main "view" of the application

                        List<string> frozenLayers = new List<string>();//list of frozen layers for this layouts viewport

                        //use the built in GetFrozenLayers method to get the layer record ids, then open those id's and record the names.
                        viewport.GetFrozenLayers().TryForEachRead<LayerTableRecord>(tran, record =>
                        {
                            frozenLayers.Add(record.Name);
                        });

                        viewportFrozenLayers.Add(viewport.ObjectId, frozenLayers);//keeps track of which layers are frozen on which viewport once its copied
                    });
                });
            }
        }

        /*
         * Uses WblockCloneObjects to copy the selected entities to the Active Database
         * which is the "current" database of the open drawing, not the "template" which is the
         * source of the entities.
         */
        
        
        database.WblockCloneObjects(layoutsToCopy, Active.Database.LayoutDictionaryId, mappings, DuplicateRecordCloning.Ignore, false);


    });
}));

//open the layout dictionary in the current drawing
Active.Database.LayoutDictionaryId.TryGetRead<DBDictionary>(layoutDictionary =>
{
    foreach (ObjectId originalLayoutId in layoutsToCopy)
    {
        //find the mapping for the layouts that were copied to the current file
        if(!mappings.Contains(originalLayoutId)) continue;
        var map = mappings.Lookup(originalLayoutId);//get the layout that was copied from the template

        map.Value.TryGetRead<Layout>(newLayout =>//the layout that is now in the current file
        {
            ObjectIdCollection viewports = new ObjectIdCollection();

            //open the layouts block table record to find all the viewports
            //because sometimes the built in GetViewports method doesn't work
            newLayout.BlockTableRecordId.TryGetRead<BlockTableRecord>((btr, tr) =>
            {
                btr.ForEachRead<Entity>(tr, (entity, tran) =>
                {
                    if (entity.IsDerivedFrom(typeof(Viewport))) viewports.Add(entity.ObjectId);
                });
            });


            foreach (ObjectId viewportId in viewports)
            {
                var viewMap = mappings.LookupValue(viewportId);
                ObjectId originalViewport = viewMap.Key;//the viewport id from the template
                if(!viewportFrozenLayers.ContainsKey(originalViewport)) continue;
                List<string> needToFreeze = viewportFrozenLayers[originalViewport];//list of layers that need freezing
                viewMap.Value.TryGetWrite<Viewport>(viewport =>
                {
                    ObjectIdCollection layerIds = new ObjectIdCollection();

                    //open layer table in order to get layer ids
                    Active.Database.LayerTableId.TryGetRead<LayerTable>(layerTable =>
                    {
                        foreach (ObjectId layerId in layerTable)
                        {
                            layerId.TryGetRead<LayerTableRecord>(record =>
                            {
                                if (needToFreeze.Contains(record.Name)) layerIds.Add(layerId);
                            });
                        }
                    });
                    
                    //uses the layer id's to freeze them using built in FreezeLayersInViewport
                    viewport.FreezeLayersInViewport(layerIds.GetEnumerator());
                });
            }
        });
    }
});



 

0 Likes

kdub_nz
Advisor
Advisor

@nshupeFMPE3 

 

Perhaps it would benefit your peers if you posted those extension methods.
This situation may arise for someone else in the future and having a complete solution would be 'nice' for them.

 

Regards,


// Called Kerry in my other life.

Everything will work just as you expect it to, unless your expectations are incorrect.
Sometimes the question is more important than the answer.

class keyThumper<T> : Lazy<T>;      another  Swamper

0 Likes

ActivistInvestor
Advisor
Advisor

Your code should work fine for viewport - Frozen layers but, if there are viewport overrides for layer color, linetype, etc, that would require a bit more work.

0 Likes