Setting a Group of Layers Visible in a Viewport

Setting a Group of Layers Visible in a Viewport

mgorecki
Collaborator Collaborator
5,672 Views
51 Replies
Message 1 of 52

Setting a Group of Layers Visible in a Viewport

mgorecki
Collaborator
Collaborator

I have a dialog box that has 2 list boxes.  One box has the available layers in it, while the other will contain the selected layers.  I want to use those selected layers as a list of layers that will be visible in a particular viewport.  I have searched all over to find information on what kind of list I would need and how could I use the list with a viewport, but all I've found is info for one layer becoming visible.

I'm also looking into "FreezeLayersInViewport" but info sure seems limited.

Any help would be appreciated.

 

Thanks,

Mark

0 Likes
5,673 Views
51 Replies
Replies (51)
Message 21 of 52

chiefbraincloud
Collaborator
Collaborator

And For what my humble opinion is worth, there is nothing wrong with the ReadOnly Property.

 

It just means that code running from within this class can add or remove items to or from the layerIds collection, but code outside this class can only read the value of that collection through the selectedLayers Property, and can not change the selectedLayers Property (or the underlying layerIds collection)

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 22 of 52

jeff
Collaborator
Collaborator

I was pointing out that returning a public field in a Read-only property completely defeats the purpose because you can change it through the field making the classes interface confusing and inconsistent

 

 

Public Class Form2
    Public ints(10) As Integer

    ReadOnly Property integers() As Integer()
        Get
            Return ints
        End Get
    End Property

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        ints(0) = 1
        MsgBox(integers(0))
        Dim ex As New Example
    End Sub
End Class


Public Class Example

    Sub New()

        Dim frm As New Form2
        frm.ints(0) = 2
        Dim i() As Integer = frm.integers
        MsgBox(frm.integers(0))

    End Sub



End Class

 

You can also find your answers @ TheSwamp
0 Likes
Message 23 of 52

chiefbraincloud
Collaborator
Collaborator

Ah.  Being the third item declared on that line (at the top of the class), I had failed to notice it was declared as Public.  I do agree that the layerIds collection should be declared privately, or alternatively, skip the property declaration.

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 24 of 52

mgorecki
Collaborator
Collaborator

Hello all, Thank you for your answers and all your help.  Thank you also for the explainations of the code and functions.  I understand the readonly property function more now.

I was looking over my code and I saw where I was declaring layerIDs twice and I did remeove the second one and make the first one include the "= New".  It was reassuring to come out here and see that it was suggested along with an explaination.

It returns the layerIDs collection like I wanted and I can now move on.

Thank you all very much.

 

Mark

0 Likes
Message 25 of 52

mgorecki
Collaborator
Collaborator

Ok, I thought I understood layer tables and layer table records, but I just can't get this to work.

Dim lt As LayerTable = TryCast(getTexttrans.GetObject(db.LayerTableId, OpenMode.ForRead), LayerTable)
                                        For Each layerID As ObjectId In lt
                                            Dim ltr As LayerTableRecord = getTexttrans.GetObject(layerID, OpenMode.ForRead)
                                            allLayerIDs.Add(ltr.ObjectId)
                                        Next

 I just want to get each layer ID and add it to the list (allLayerIDs) to use it to freeze all layers in a selected viewport.  When it gets to the line: 

allLayerIDs.Add(ltr.ObjectId)

 

It crashes.

lt is the layer table.  ltr is the layer table record for that particular layer that the for each is on at that moment.  ltr.ObjectId should be the layers object ID, shouldn't it?

 

Mark

0 Likes
Message 26 of 52

mgorecki
Collaborator
Collaborator

OK, never mind, I got it.:

 Dim lt As LayerTable = getTexttrans.GetObject(db.LayerTableId, OpenMode.ForRead)
                                        For Each layerID As ObjectId In lt
                                            Dim ltr As LayerTableRecord = getTexttrans.GetObject(layerID, OpenMode.ForRead)
                                            allLayerIDs.Add(ltr.ObjectId)
                                        Next

 

0 Likes
Message 27 of 52

fieldguy
Advisor
Advisor

You don't show where alllayerIDs is defined.  It should be something like

Dim alllayerIDs As ObjectIdCollection = new ObjectIdcollection() or maybe you define it as

Public alllayerIDs As ObjectIdCollection at the class level.  If you do it that way, you will need alllayerIDs = New ObjectIdcollection() somewhere - before you try to add to it.

 

I understand what you are doing now with the ReadOnly Property selectedlayers() (message 9).  You can access listboxes on the form from somewhere else directly using (for example) diaTitleText.lstAvailableLayers.SelectedItems.Count.  This would allow you to create the list of layerids where you need it - not in your form code.  My rule of thumb is to have as little code as possible in form definitions.  Create a form to obtain user input and access that input from somewhere else when / where you need to.

 

I will have some time this weekend to look into this further if you want to post all of your code.  I am doing something similar with batch plotting viewports, and trying to decide if I should use layerstates or lists of layers as you are doing.  Layerstates are OK but not as easy to manage in a multi-user environment.  

0 Likes
Message 28 of 52

chiefbraincloud
Collaborator
Collaborator

For what its worth, with the code the way you have it there, you should be calling ltr.Dispose before the Next.

 

However, ltr.ObjectId is the same as layerID, so you don't need to open the LayerTableRecord inside the loop, just do:

 

For Each layerID as ObjectId in lt

     allLayerIDs.Add(layerID)

Next

 

And then also make sure to call lt.Dispose, when you are finished with the LayerTable.

 

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 29 of 52

mgorecki
Collaborator
Collaborator

Hi fieldguy,

I have the objectIdCollections defined at the beginning of the sub:

Dim selectedLayerIDs As ObjectIdCollection, allLayerIDs AsNewObjectIdCollection

 

selectedLayerIDs is defined as New in the form because that's where I added the layers to it.  It was just easy to add the one line the the loop that was already getting the selected layers for the other listbox:

For i = 0 To lstAvailableLayers.SelectedItems.Count - 1
                ' Add only the selected layers to the right side list box
                lstLayersToUse.Items.Add(lstAvailableLayers.SelectedItems(i))
                selectedLayerIds.Add(lt(lstAvailableLayers.SelectedItems(i)))
            Next

 

I declared allLayerIDs in the sub (like you said) because that's where I was going to use it.  I do still have trouble understanding how to send information from the form to the sub, but you all have helped me get better at doing that.

I went with using layer lists to allow the user the ability to select which layers they want to have visible in their new viewport.  Since the actual layer names are unknown, I thought this would be the best option.

 

Thanks so much for your help and guidance.

Mark

 

0 Likes
Message 30 of 52

mgorecki
Collaborator
Collaborator

Hi chief,

I added the lt.Dispose()

I also changed the "For Each" loop as you suggested. 

Do you have any links or books that I should check out to better understand getting information from layertables and layertablerecords?

 

Thank you very much.

Mark

0 Likes
Message 31 of 52

Anonymous
Not applicable

Hi Mark,

 

I would strongly recommend that you go through the AutoCAD Developer's Guide.

In particular the 'Basics of the AutoCAD .NET API -> Collection Objects' section found here:
http://exchange.autodesk.com/autocad/enu/online-help/browse#WS73099cc142f48755-5c83e7b1120018de8c0-2...

And the 'Create and Edit AutoCAD Entities -> Use Layers, Colors and Linetypes -> Work with Layers' section found here:
http://exchange.autodesk.com/autocad/enu/online-help/browse#WS1a9193826455f5ff2566ffd511ff6f8c7ca-3d...

 

My other suggestion would be to look at Kean Walmsley's through-the-interface blog:
http://through-the-interface.typepad.com/through_the_interface/

Each post is like a mini-tutorial. If you do a search for "Layer", you'll get a whole bunch of good posts. For example this one titled 'Creating an AutoCAD layer using .NET':
http://through-the-interface.typepad.com/through_the_interface/2010/01/creating-an-autocad-layer-usi...

Just looking at this post right now I discovered the SymbolUtilityServices.ValidateSymbolName() method and then looked it up in the ObjectARX Managed Class Reference Guide doc. Looks handy.

Anyways, although most of the code provided is in C#, you can use one of the following online conversion websites to convert the code to VB.NET:
http://www.carlosag.net/Tools/CodeTranslator/
http://m.developerfusion.com/tools/convert/csharp-to-vb/

Although these tools aren't perfect they'll get you most of the way there. Or of course you could just learn C# Smiley Happy

 

Art

0 Likes
Message 32 of 52

mgorecki
Collaborator
Collaborator

Hello Art,

I have bookmarked these links and will be exploring a lot.

 

Thank you very much.

Mark

0 Likes
Message 33 of 52

fieldguy
Advisor
Advisor

Attached is code that shows my approach to some of the problem.  Give it a run - it's good to play with,  You have to remove the txt extensions.

 

There is plenty missing - like a specific viewport in a specific layout.

 

 

 

 

0 Likes
Message 34 of 52

fieldguy
Advisor
Advisor

I forgot about the form designer (attached).

 

The form has 2 listboxes and 2 buttons.

0 Likes
Message 35 of 52

mgorecki
Collaborator
Collaborator

Hi fieldguy,

Here is my code.  I'm still working on the rename layers and add a layer parts.  One thing I've searched all over for is a way to clone a layer and everything on it.  In my case, the layer would have a polyline or lines and some text, maybe a few more things, but I would like to know how to copy the entire layer and all it's contents to another layer name.

 

Have a great day,

Mark

0 Likes
Message 36 of 52

Anonymous
Not applicable

Hi Mark,

 

It's important to understand that Entitys are "owned" by BlockTableRecords, but are NOT owned by LayerTableRecords - i.e. each Entity has a Layer / LayerId property that "points to" a LayerTableRecord.

 

For example in model space, all of the entities are owned by the "*Model_Space" BlockTableRecord, and each entity's Layer / LayerId property points to a particular LayerTableRecord.

 

If you clone a LayerTableRecord, the layer will be cloned, but the entities that point to that layer will not be cloned with it.

 

So after creating / cloning the new layer, there are 2 ways I know to achieve what I think you are looking to do:

 

1. The first technique is to iterate the BlockTableRecord, i.e.:
    i)   Iterate the BlockTableRecord and open each Entity one at a time.
    ii)  For each Entity, check if it is pointing to the layer you wish to copy from. If so, clone the Entity and change its layer
         property to point to the layer you wish to copy to.

 

2. Similar to the first technique, but instead of iterating the BlockTableRecord you could use a SelectionFilter to select
    entities from the layer you wish to copy from. Refer to this post for further details:
    http://through-the-interface.typepad.com/through_the_interface/2008/07/conditional-sel.html

 

Art

0 Likes
Message 37 of 52

mgorecki
Collaborator
Collaborator

Hi Art,

Yes, I was looking at cloning the objects on the original layer, but was having trouble figuring out how to change just those objects layer property.  I guess there must be a way to immediately change the layer the object is pointing to as you clone it.  I also like the idea of a selection set.  I was thinking about that avenue as well.  I had worked with selection sets and filters before, so I thought that would be another route I could take.  I will check out the link you posted.

0 Likes
Message 38 of 52

Anonymous
Not applicable

Hi Mark,

 

I think I posted the wrong link. This one is titled 'Finding all the AutoCAD entities on a particular layer using .NET' and might be more relevant to you:
http://through-the-interface.typepad.com/through_the_interface/2008/05/finding-all-the.html

 

To change an Entity's layer, you can set it's Layer property (string name of a layer) or LayerId property (ObjectId of LayerTableRecord), i.e.:

   Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
   Entity entClone = ent.Clone() as Entity;  // Cloned Entity.
   entClone.Layer = "SomeLayerName";    // Layer string name option.

 

Or instead of setting the Layer property, you could set the LayerId property, i.e.:
   entClone.LayerId = layerId;                    // LayerTableRecord ObjectId option.

 

Art

0 Likes
Message 39 of 52

mgorecki
Collaborator
Collaborator

Hi Art,

I created the following code per your suggestion.  Even though it seems to execute correctly, no entities appear on the target layer.

Public Sub CopyObjectsToNewLayer(ByVal pageNumber As String, ByVal pageToCopy As String)
        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        Dim acCurDb As Database = acDoc.Database

        Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()

            ' Request for objects to be selected in the drawing area
            Dim bt As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
            Dim ed As Editor = acDoc.Editor
            Dim sourceLayer As String = "DETAIL_" & pageToCopy
            Dim targetLayer As String = "DETAIL_" & pageNumber

            ' Build a filter list so that only entities on the specified layer are selected
            Dim tvs As TypedValue() = New TypedValue(0) {New TypedValue(CInt(DxfCode.LayerName), sourceLayer)}
            Dim sf As New SelectionFilter(tvs)
            Dim sourceLayerPSR As PromptSelectionResult = ed.SelectAll(sf)
            'Dim sourceObjects As EditorInput.SelectionSet
            'Dim sourceObjIDs As DatabaseServices.ObjectIdCollection

            'sourceObjects = sourceLayerPSR.Value
            'sourceObjIDs = New DatabaseServices.ObjectIdCollection(sourceObjects.GetObjectIds)
            ' Step through the objects in the selection set
            For Each SelectionSetObj As SelectedObject In sourceLayerPSR.Value
                ' Check to make sure a valid SelectedObject object was returned
                If Not IsDBNull(SelectionSetObj) Then
                    ' Open the selected object for write
                    Dim sourceEnt As Entity = acTrans.GetObject(SelectionSetObj.ObjectId, OpenMode.ForRead)
                    Dim entClone As Entity = TryCast(sourceEnt.Clone(), Entity)
                    ' Cloned Entity
                    entClone.Layer = targetLayer
                End If
            Next
            ' Save the new object to the database
            acTrans.Commit()
            acTrans.Dispose()
        End Using
    End Sub

I'm just not sure why it's not creating the clone and placing oit on the target layer.  Can you help me out again? 

Thanks,

Mark

0 Likes
Message 40 of 52

chiefbraincloud
Collaborator
Collaborator

You are creating temporary Non Database Resident entities without appending them to the Model (or Paper) Space.  Before your For Each loop you need to open the Current Space using AcCurDb.CurrentSpaceId, then at the end of your IF block inside your For Each loop,  Append the entities to the CurrentSpace, and AddNewlyCreatedDbObject to the Transaction.

 

One other things to note, the SelectAll method will only return objects that are visible on the screen, so you may want to programmatically Zoom Extents prior to making the selection.

Dave O.                                                                  Sig-Logos32.png
0 Likes