TheSwamp

Code Red => .NET => Topic started by: stevenh0616 on June 01, 2012, 08:14:14 AM

Title: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 08:14:14 AM
I am working on a command to change the surface style programmatically with vb.NET and am having issues. I work with a lot of surfaces in my drawings at times and they are pasted into one compiled surface. When making modifications, you have to go through each surface and change the styles indivually from 'No Display' to 'Something that Displays'. So my thought was design a menu that lists the surface styles in the drawing and lists all the surfaces. Then pick what style you want and which surfaces to apply it to. Seems simple... but it's not working.

Under Autodesk.Civil.Land.DatabaseServices, Surface, TinSurface, & GridSurface are all exposed. They all list the StyleID as ObjectID and the StyleName as String that says it is a Public Overridable Property. However when trying to change their current values I receive an Unhandled Exception stating "need to override property StyleID()/StyleName". I would think that this would work.

I have done some research and saw that there is another option down the COM route with Surface.Style. However, I am unfamiliar with programming in COM and I was under the impression most of Civil3D has been ported to vb.NET.

Anyone know what I'm doing wrong?? Here is a little snippet of my code.

Code - vb.net: [Select]
  1. For Each Style As ObjectId In Styles
  2.                 Dim oStyle As SurfaceStyle = TryCast(Style.GetObject(OpenMode.ForRead), SurfaceStyle)
  3.                 If cbxSurfStyles.Text = oStyle.Name Then
  4.                     Dim oStyleId As ObjectId = oStyle.ObjectId
  5.                     'Dim oStyleNm As String = oStyle.Name
  6.                    
  7.                     For Each SelSurfName In clbSurfaces.CheckedItems
  8.                         Dim surfaces As ObjectIdCollection = m_CivilDoc.GetSurfaceIds
  9.  
  10.                         For Each surfaceId As ObjectId In surfaces
  11.                             Dim oSurface As ACLB.TinSurface = TryCast(SelSurfID.GetObject(OpenMode.ForWrite), ACLB.TinSurface)
  12.                             If oSurface.Name = SelSurfName Then
  13.                                 oSurface.StyleId = oStyleId
  14.                                 'oSurface.StyleName = oStyleNm
  15.                                 tr.Commit()
  16.                             End If
  17.                         Next
  18.                     Next
  19.                 End If
  20.             Next

Thanks!!

Steve
Windows 7
Civil3D 2011

Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Delegate on June 01, 2012, 08:56:42 AM
Just to say in 2011 some of Civil 3D not covered by .NET:

Read the developer guide here for more info - quote below:

http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=16766674

Quote
The .NET API does not expose all the functionality of AutoCAD Civil 3D, and it exposes less than the COM
API. The following areas are not yet exposed in .NET:
■ Survey
■ Points
■ Surfaces
■ Sites and Parcels
■ Sections
■ Data Bands
■ Labels (except Alignment, Profile and Profile View labels)

Looks like improvements in later versions though.
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: BlackBox on June 01, 2012, 09:15:46 AM
When making modifications, you have to go through each surface and change the styles indivually from 'No Display' to 'Something that Displays'. So my thought was design a menu that lists the surface styles in the drawing and lists all the surfaces. Then pick what style you want and which surfaces to apply it to.

I also work with Civil 3D (2011/2012), and really like this idea.. it (this concept) seems applicable to many other Aecc* Objects as well.

It would be greatly appreciated if you were to post the source code, or VS project here (before or after completion at your discretion)... Either way, thanks for the idea!

Cheers!
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Delegate on June 01, 2012, 09:35:38 AM
From the developers guide - does not use style ID.

Code: [Select]
Dim oSurfaceStyle As AeccSurfaceStyle
oSurfaceStyle = oDocument.SurfaceStyles.Add("New Style")
' The surface is displayed according to the
' oSurfaceStyle style we have just created.
oSurface.Style = oSurfaceStyle.Name
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 09:42:23 AM
Grrrr....Thanks for looking into. I started working with the COM method, but am unfamiliar with it. I was getting object issues if I remember correctly. I think I was having issues referencing the active document with the COM method. I'll have to dig deeper.
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Jeff_M on June 01, 2012, 10:15:43 AM
I think your code should work fine in 2011. I don't use VB much so I can't quickly write something to test/debug with unless I do it in C#. If you could share some code I could run I could probably advise you how to get it to work without needing COM.

Scratch that, I just setup a quick test and am getting the same error you are. I think I know how to work around it, but I need to go out in the field for a bit. Will try to have something later today.
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 12:13:27 PM
Thanks everyone.

So now I've attempted to go the COM route, which I am unfamiliar with. Below is my code. This is the same error I was getting the first time around with COM - "Object not set to an instance of an object" on the 3rd line down in the code below 'm_oAeccApp = m_oAcadApp.GetInterfaceObject(m_sAeccAppProgId)'. And this was all done following the COM .net development guide on Autodesk's website and via the Sample project in the Civil 3D API/COM folder.   http://docs.autodesk.com/CIV3D/2012/ENU/API_Reference_Guide/com/dotnet_dev.htm (http://docs.autodesk.com/CIV3D/2012/ENU/API_Reference_Guide/com/dotnet_dev.htm)

Any ideas what I'm doing wrong here? Thanks.

Code - vb.net: [Select]
  1. m_sAcadProdID = "AutoCAD.Application"
  2.         m_sAeccAppProgId = "AeccXUiLand.AeccApplication.7.0"
  3.         m_oAeccApp = m_oAcadApp.GetInterfaceObject(m_sAeccAppProgId)
  4.         m_oAeccDb = m_oAeccApp.ActiveDocument.Database
  5.  
  6.         Using tr As Autodesk.AutoCAD.DatabaseServices.Transaction = db.TransactionManager.StartTransaction
  7.             Dim oStyles As AeccLandLib.AeccSurfaceStyles = m_oAeccDb.SurfaceStyles
  8.             For Each oStyleID As ObjectId In oStyles
  9.                 Dim oStyle As AeccLandLib.AeccSurfaceStyle = TryCast(oStyleID.GetObject(OpenMode.ForRead), AeccLandLib.AeccSurfaceStyle)
  10.                 If cbxSurfStyles.Text = oStyle.Name Then
  11.                     Dim oSelStyle As AeccLandLib.AeccSurfaceStyle = oStyle
  12.  
  13.                     For Each SelSurfName In clbSurfaces.CheckedItems
  14.                         Dim oSurfaces As AeccLandLib.AeccSurfaces = m_oAeccDb.Surfaces
  15.  
  16.                         For Each oSurfaceID As ObjectId In oSurfaces
  17.                             Dim oSurface As AeccLandLib.AeccSurface = TryCast(oSurfaceID.GetObject(OpenMode.ForWrite), AeccLandLib.AeccSurface)
  18.                             If oSurface.Name = SelSurfName Then
  19.                                 oSurface.Style = oSelStyle
  20.                                 tr.Commit()
  21.                             End If
  22.                         Next
  23.                     Next
  24.                 End If
  25.             Next
  26.         End Using
  27.         ed.Regen()
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: huiz on June 01, 2012, 12:47:14 PM
Surfaces are accessible via .NET since 2012, not 2011. But you could do via COM.

A developer's guide for 2011 can be found here: http://images.autodesk.com/adsk/files/civil_api_developers_guide0.pdf


Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 01:12:50 PM
Right, but when attempting the COM method I cannot connect to the current drawing. The following code is right from the Autodesk Document explaining how to Root Objects in COMM. See code below, ThisDrawing in Line 9 is not exposed. How do I access it?

Code - vb.net: [Select]
  1. Imports AutoCAD = Autodesk.AutoCAD.Interop
  2. Imports AeccLandLib = Autodesk.AECC.Interop.Land
  3. Imports AeccUiLandLib = Autodesk.AECC.Interop.UiLand
  4. Imports AecUIBase = Autodesk.Aec.Interop.UIBase
  5. Imports Autodesk.AECC.Interop.UiLand
  6. Imports Autodesk.AutoCAD.Interop
  7.  
  8. Dim oAcadApp As AcadApplication
  9.         oAcadApp = ThisDrawing.Application
  10.         ' Specify the COM name of the object we want to access.
  11.         ' Note that this always accesses the most recent version
  12.         ' of AutoCAD Civil 3D installed.
  13.         Const sCivilAppName = "AeccXUiLand.AeccApplication.7.0"
  14.         Dim oCivilApp As AeccApplication
  15.         oCivilApp = oAcadApp.GetInterfaceObject(sCivilAppName)
  16.         ' Now we can use the AeccApplication object.
  17.         ' Get the AeccDocument representing the currently
  18.         ' active drawing.
  19.         Dim oDocument As AeccDocument
  20.         oDocument = oCivilApp.ActiveDocument
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Delegate on June 01, 2012, 01:46:15 PM
How about this method in c#: (Maybe does not work for getting at Civil3D stuff?)

AcadApplication app = (AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject("AutoCAD.Application");

AcadDocument drawing = app.ActiveDocument;
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Jeff_M on June 01, 2012, 01:57:54 PM
Or this:
AcadApplication acadApp = (AcadApplication)Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication;
AcadDocumant acadDoc = acadApp.ActiveDocument;

The AeccApplication for 2011 is 8.0, not 7.0

You can also make it versionless by doing this instead of what the 'help' docs say (sorry for the c#, hopefully you can figure out the VB equivalent):
AeccApplication aeccapp = new AeccApplicationClass();
aeccapp.init(acadApp);
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 02:20:16 PM
Thanks.

I converted both of those over to VB.net and still won't work. I did catch the 8.0 vs 7.0 and that still didn't work. I wish Autodesk would be a bit more clear on what needs to happen from a COM standpoint in VB.net since half the program is in VB.net and the rest in COM.

The command is almost there (See attached PNG)... just have to get it to work... lol.
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Jeff_M on June 01, 2012, 02:54:32 PM
This seems to be a fairly simple way around this issue, mixing .NET managed code with COM. Note that I did not need to cast anything as the AeccApplication.
Code: [Select]
Imports System
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.Civil.ApplicationServices
Imports Autodesk.Civil.DatabaseServices
Imports Autodesk.Civil.Land.DatabaseServices
Imports Autodesk.Civil.Land.DatabaseServices.Styles
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AECC.Interop.Land


    <CommandMethod("TestSurfStyle")> _
    Public Sub TestSurfStyle()

        Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim CivilDoc As CivilDocument = CivilApplication.ActiveDocument
        Using tr As Transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
            Dim surf As TinSurface = TryCast(CivilDoc.GetSurfaceIds(0).GetObject(OpenMode.ForRead), TinSurface)
            Dim style As SurfaceStyle = TryCast(CivilDoc.Styles.SurfaceStyles(1).GetObject(OpenMode.ForRead), SurfaceStyle)
            Dim comSurf As AeccTinSurface = TryCast(surf.AcadObject, AeccTinSurface)
            Dim comStyle As AeccSurfaceStyle = TryCast(style.AcadObject, AeccSurfaceStyle)
            comSurf.Style = comStyle
            tr.Commit()
        End Using

    End Sub
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 03:29:56 PM
Thanks Jeff. I was really excited for this because it was something completely different than what I've tried. I used your code almost exact and got the Object not set to an instance of an object.

Only thing I added was my code to pick the surface and plugged the surfaceID into the Surface tryCast.

I get the error on the line: comSurf.Style = comStyle

I suppose the objectID isn't getting passed in the trycast??

Code - vb.net: [Select]
  1. Dim SelSurfID As ObjectId = promptForSurface()
  2.         If ObjectId.Null = SelSurfID Then
  3.             'MsgBox appears under prompt for surface
  4.             ' We don't have a surface; we can't continue.
  5.             Return
  6.         End If
  7.         Dim CivilDoc As CivilDocument = CivilApplication.ActiveDocument
  8.         Using tr As Transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
  9.             Dim surf As ACLB.TinSurface = TryCast(SelSurfID.GetObject(OpenMode.ForWrite), ACLB.TinSurface)
  10.             Dim style As SurfaceStyle = TryCast(CivilDoc.Styles.SurfaceStyles(1).GetObject(OpenMode.ForRead), SurfaceStyle)
  11.             Dim comSurf As AeccLandLib.AeccTinSurface = TryCast(surf.AcadObject, AeccLandLib.AeccTinSurface)
  12.             Dim comStyle As AeccLandLib.AeccSurfaceStyle = TryCast(style.AcadObject, AeccLandLib.AeccSurfaceStyle)
  13.             comSurf.Style = comStyle
  14.             tr.Commit()
  15.         End Using
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Jeff_M on June 01, 2012, 03:56:12 PM
Have you stepped through the code when it's running to see what is getting set or not? Also, don't open the surface for write, the COM wrapper does that internally so all you need is to read the .NET object. Maybe try a different number when getting the surface styles, although the 1 I used would get the second one in the collection, so you'd need at least 2 styles for it to work.

Other than that, I really can't say without having your full code to test with.
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 04:40:01 PM
Thanks Jeff.

I have done that a few times, and here it is once again. This time I rewrote the code out as you had provided. It doesn't seem to be passing the object over to COM, that's where I'm getting the object not set to instance of an object error.

Below is new code for picking surface. Upon doing the trycast of the style from acad object to COM is where the error occurs.

Code - vb.net: [Select]
  1. Dim SelSurfID As ObjectId = promptForSurface()
  2.         If ObjectId.Null = SelSurfID Then
  3.             'MsgBox appears under prompt for surface
  4.             ' We don't have a surface; we can't continue.
  5.             Return
  6.         End If
  7.  
  8.         Using tr As Transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
  9.             Dim CivilDoc As CivilDocument = CivilApplication.ActiveDocument
  10.             Dim styles As SurfaceStyleCollection = CivilDoc.Styles.SurfaceStyles
  11.             For Each StyleID As ObjectId In styles
  12.                 Dim style As SurfaceStyle = TryCast(StyleID.GetObject(OpenMode.ForRead), SurfaceStyle)
  13.                 If cbxSurfStyles.Text = style.Name Then
  14.                     MsgBox(style.Name)
  15.                     Dim comStyle As AeccLandLib.AeccSurfaceStyle = TryCast(style.AcadObject, AeccLandLib.AeccSurfaceStyle)
  16.                     MsgBox(comStyle.Name)
  17.                     Dim Surface As ACLB.Surface = TryCast(SelSurfID.GetObject(OpenMode.ForRead), ACLB.Surface)
  18.                     MsgBox(Surface.Name)
  19.                     Dim comsurf As AeccLandLib.AeccSurface = TryCast(Surface.AcadObject, AeccLandLib.AeccSurface)
  20.                     MsgBox(comsurf.Name)
  21.                     comsurf.Style = comStyle
  22.                     tr.Commit()
  23.                 End If
  24.             Next
  25.         End Using
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Jeff_M on June 01, 2012, 04:49:03 PM
OK, I think I see where it may be failing. Move the tr.Commit() out of the For Each loop or Exit the loop after you commit. Once you commit(), the Transaction is closed so the next style never gets set.
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 04:55:45 PM
Grrrr.... no go on that either. Still same errror on the trycast from style.acadobject to aeccsurfstyle, line 15.

Here is new code:

Code - vb.net: [Select]
  1. Dim SelSurfID As ObjectId = promptForSurface()
  2.         If ObjectId.Null = SelSurfID Then
  3.             'MsgBox appears under prompt for surface
  4.             ' We don't have a surface; we can't continue.
  5.             Return
  6.         End If
  7.         Dim tr As Transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
  8.         Using tr
  9.             Dim CivilDoc As CivilDocument = CivilApplication.ActiveDocument
  10.             Dim styles As SurfaceStyleCollection = CivilDoc.Styles.SurfaceStyles
  11.             For Each StyleID As ObjectId In styles
  12.                 Dim style As SurfaceStyle = TryCast(StyleID.GetObject(OpenMode.ForRead), SurfaceStyle)
  13.                 If cbxSurfStyles.Text = style.Name Then
  14.                     MsgBox(style.Name)
  15.                     Dim comStyle As AeccLandLib.AeccSurfaceStyle = TryCast(style.AcadObject, AeccLandLib.AeccSurfaceStyle)
  16.                     MsgBox(comStyle.Name)
  17.                     Dim Surface As ACLB.Surface = TryCast(SelSurfID.GetObject(OpenMode.ForRead), ACLB.Surface)
  18.                     MsgBox(Surface.Name)
  19.                     Dim comsurf As AeccLandLib.AeccSurface = TryCast(Surface.AcadObject, AeccLandLib.AeccSurface)
  20.                     MsgBox(comsurf.Name)
  21.                     comsurf.Style = comStyle
  22.                 End If
  23.             Next
  24.         End Using
  25.         tr.Commit()
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: Jeff_M on June 01, 2012, 05:14:59 PM
Not sure, Steve, but since I don't have your promptForSurface, or the form with the combobox, I made some adjustments and this works for me:
Code: [Select]

    <CommandMethod("TestSurfStyle")> _
    Public Sub TestSurfStyle()

        Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim CivilDoc As CivilDocument = CivilApplication.ActiveDocument
        Using tr As Transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
            Dim prmpt = New PromptEntityOptions("Select Surface")
            prmpt.SetRejectMessage("Not a surface, try again...")
            prmpt.AddAllowedClass(GetType(ACLB.Surface), False)
            Dim res As PromptEntityResult = doc.Editor.GetEntity(prmpt)
            If res.Status <> PromptStatus.OK Then Return
            Dim SelSurfID As ObjectId = res.ObjectId
            Dim styles As SurfaceStyleCollection = CivilDoc.Styles.SurfaceStyles
            For Each StyleID As ObjectId In styles
                Dim style As SurfaceStyle = TryCast(StyleID.GetObject(OpenMode.ForRead), SurfaceStyle)
                If "Border Only" = style.Name Then
                    MsgBox(style.Name)
                    Dim comStyle As AeccLandLib.AeccSurfaceStyle = TryCast(style.AcadObject, AeccLandLib.AeccSurfaceStyle)
                    MsgBox(comStyle.Name)
                    Dim Surface As ACLB.Surface = TryCast(SelSurfID.GetObject(OpenMode.ForRead), ACLB.Surface)
                    MsgBox(Surface.Name)
                    Dim comsurf As AeccLandLib.AeccSurface = TryCast(Surface.AcadObject, AeccLandLib.AeccSurface)
                    MsgBox(comsurf.Name)
                    comsurf.Style = comStyle
                End If
            Next
            tr.Commit()
        End Using

    End Sub
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 01, 2012, 05:35:16 PM
Jeff, you rock!!

That does work!! However there may have been one minor error on my part with reference C3D 2012 COM files but anyways, I'll post a link to the program this weekend once I have it all tweeked.

Thanks so much!! Great news for last minute on a Friday!!

Have a good one!!
Title: Re: Programmatically Apply Style to Existing Civil 3D Surface
Post by: stevenh0616 on June 03, 2012, 09:33:59 PM
Thanks again for all your help with making this command work. You can find the command and the VB.net project links on my blog, ENJOY!!   :-D

http://beyondcivil3d.blogspot.com/2012/06/apply-surface-style-to-multiple.html (http://beyondcivil3d.blogspot.com/2012/06/apply-surface-style-to-multiple.html)