2013-06-27 2 views
3

J'ai été chargé de traduire un plugin AutoCAD de VBA vers VB.NET, et je suis actuellement un peu coincé.Commande AutoCAD -INSERT dans .NET

La commande sur laquelle je travaille crée une nouvelle couche (ou la sélectionne comme couche active si elle existe déjà), puis exécute 2 commandes "-INSERT" donnant un point sélectionné par l'utilisateur, et un fichier dwg . Ensuite, la couche active précédente est réinitialisée en tant que couche active.

L'insert-commande ressemble à ceci:

-INSERT 
C:\path\to\file.dwg 
<point.x>,<point.y>,<point.z> 
<documentScale> 

Note: Tous les sauts de ligne-dans la commande sont ajoutés comme vbCR (non vbCrLf).

Ma question est, comment puis-je obtenir le même résultat dans .NET contre ObjectARX? Je ne peux pas utiliser SendStringToExecute car il est asynchrone (sans rappel), donc en d'autres termes, je ne peux pas réinitialiser le calque courant une fois l'exécution terminée. Il doit y avoir un moyen de reproduire cette fonctionnalité dans le code pur. NET, probablement en utilisant le BlockTable, mais je n'ai aucune idée de comment.

J'ai essayé de suivre l'article trouvé ici: http://through-the-interface.typepad.com/through_the_interface/2006/08/import_blocks_f.html, mais cela n'a eu aucun effet visible sur le document. J'ai également essayé d'utiliser myDatabase.Insert(transform, otherDatabase, False) et l'invite de commande a dit quelque chose à propos des blocs déjà existants et donc être sauté, mais je n'ai toujours pas vu de changements. Je n'ai aucune idée de la magie de la commande "-INSERT" dans les coulisses, mais serait-il viable de la répliquer dans .NET? Ou est-il en quelque sorte disponible pour être appelé comme une méthode normale (pas comme une commande de texte envoyée pour être traitée par AutoCAD)?

Répondre

5

L'exemple de code dans la publication Through the Interface importe les blocs, mais ne les insère pas dans le dessin. Vous devez créer un BlockReference et l'ajouter à l'espace objet. Il insère également tous les blocs du fichier, pas le fichier comme un seul bloc.

Voici le code que j'utilise pour importer un fichier en bloc entier. Cette fonction renvoie une référence de bloc que vous pouvez insérer dans votre dessin.

Private Shared Function InsertFile(ByVal FileName as String, ByVal dwgdb As Database, ByVal tr As Transaction) As BlockReference 

     Dim br As BlockReference 
     Dim id As ObjectId 

     'use a temporary database 
     Using TempDB As New Database(False, True) 

      'Get block table 
      Dim bt As BlockTable = tr.GetObject(dwgdb.BlockTableId, OpenMode.ForWrite, False) 

      'Create unique block name 
      Dim BlockName As String = FileName.Replace("\", "").Replace(":", "").Replace(".", "") 

      'check if block already exists 
      If Not bt.Has(BlockName) Then 
       'check if file exists 
       If IO.File.Exists(FileName) Then 
        'read in the file into the temp database 
        TempDB.ReadDwgFile(FileName, IO.FileShare.Read, True, Nothing) 
        'insert the tempdb into the current drawing db, id is the new block id 
        id = dwgdb.Insert(BlockName, TempDB, True) 
       Else 
        'Throw exception for missing file 
        Throw New System.Exception(String.Format("File {0} is not found for library item {1}", FileName, item.PartNo)) 
       End If 

      Else 
       id = bt.Item(BlockName) 
      End If 

      'create a new block reference 
      br = New BlockReference(New Point3d(0, 0, 0), id) 
     End Using 

     Return br 


    End Function 

Voici l'exemple de l'utilisation de cette fonction pour insérer un bloc dans le fichier. Dans cet exemple, j'utilise un gabarit, qui permet à l'utilisateur de déposer l'objet sur l'emplacement souhaité, sinon vous pouvez simplement définir la position.

 ' Get Editor 
     Dim ed As Editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor 
     ' Get Database 
     Dim dwg As Database = ed.Document.Database 

     'Lock document 
     Using dl As DocumentLock = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument() 

      '### Changed Try Finally to using, try was hiding errors 
      'Begin Transaction 
      Using trans As Transaction = dwg.TransactionManager.StartTransaction() 

       Dim blockRef As BlockReference = InsertFile(FileName, dwg, trans) 

       'check if layer exists/create 
       AcadUtil.AcadFunctions.CheckLayer(LayerName, trans, dwg) 
       blockRef.Layer = LayerName 

       'set focus to the editor 
       Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView() 

       'have the user pick insert point 
       Dim BlockMove As New AcadJigs.JigBlockMove(blockRef, False, 0) 
       ed.Drag(BlockMove) 
       'optionally you could just set the .Position of the block reference 


       ' add it to the current space, first open the current space for write 
       Dim btr As BlockTableRecord = trans.GetObject(dwg.CurrentSpaceId, OpenMode.ForWrite, True, True) 

       ' Add block reference to current space 
       btr.AppendEntity(blockRef) 

       'Capture the handle 
       handle = blockRef.Handle.Value.ToString 

       ' remember to tell the transaction about the new block reference so that the transaction can autoclose it 
       trans.AddNewlyCreatedDBObject(blockRef, True) 

       'commit the transaction 
       trans.Commit() 

      End Using 

     End Using 

Et voici aussi la fonction CheckLayer que j'ai appelée.

Public Shared Sub CheckLayer(ByVal Name As String, ByVal tr As Transaction, ByVal dwg As Database) 


     Dim lt As LayerTable = CType(tr.GetObject(dwg.LayerTableId, OpenMode.ForWrite), LayerTable) 

     If lt.Has(Name) Then 
      Return 
     Else 
      Dim ly As New LayerTableRecord 
      ly.Name = Name 
      lt.Add(ly) 
      tr.AddNewlyCreatedDBObject(ly, True) 
     End If 

    End Sub 

Tout comme une note, le blog de Kean est un grand ressources, j'appris à peu près tout le code ci-dessus à partir de là.

Pour être complet, voici la classe Jig i référence dans le code d'insertion,

Class JigBlockMove 
     Inherits EntityJig 

     Private _CenterPt As Point3d 
     Private _ActualPoint As Point3d 
     Private _LockZ As Boolean 
     Private _Z As Double 

     Public ReadOnly Property SelectedPoint() As Point3d 
      Get 
       Return _ActualPoint 
      End Get 
     End Property 

     Public Sub New(ByVal BlockRef As BlockReference, ByVal LockZ As Boolean, ByVal Z As Double) 
      MyBase.New(BlockRef) 
      _CenterPt = BlockRef.Position 
      _LockZ = LockZ 
      _Z = Z 
     End Sub 

     Protected Overloads Overrides Function Sampler(ByVal prompts As JigPrompts) As SamplerStatus 
      Dim jigOpts As New JigPromptPointOptions() 
      jigOpts.UserInputControls = (UserInputControls.Accept3dCoordinates Or UserInputControls.NoZeroResponseAccepted Or UserInputControls.NoNegativeResponseAccepted) 
      jigOpts.Message = vbLf & "Enter insert point: " 
      Dim dres As PromptPointResult = prompts.AcquirePoint(jigOpts) 
      If _ActualPoint = dres.Value Then 
       Return SamplerStatus.NoChange 
      Else 
       _ActualPoint = dres.Value 
      End If 

      Return SamplerStatus.OK 
     End Function 

     Protected Overloads Overrides Function Update() As Boolean 
      If _LockZ Then 
       _CenterPt = New Point3d(_ActualPoint.X, _ActualPoint.Y, _Z) 
      Else 
       _CenterPt = _ActualPoint 
      End If 

      Try 
       DirectCast(Entity, BlockReference).Position = _CenterPt 
      Catch generatedExceptionName As System.Exception 
       Return False 
      End Try 

      Return True 
     End Function 

     Public Function GetEntity() As Entity 
      Return Entity 
     End Function 

    End Class 

Une note concernant le travail dans ObjectARX .NET, il y a un problème avec la nature un seul thread d'AutoCAD, et le fait que le garbage collector .NET s'exécute sur un thread séparé. Si vous créez des objets AutoCAD temporaires qui ne sont pas ajoutés à la base de données, vous devez explicitement appeler le .Dispose() ou AutoCAD peut tomber en panne! Le crash semblera aléatoire aussi, car il sera déclenché par le thread garbage collector. Voir cet article, http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html.

+0

Merci.Je vérifie ça une fois que je serai au travail demain. En outre, j'utilise l'instruction Using sur presque tout, donc tout est disposé (garanti, même avec des exceptions etc.). – Alxandr

+0

Savez-vous s'il existe un moyen simple de faire apparaître autocad avec ses "enter (multiple) attributes (strings)" - fenêtre? Comme, si je viens d'utiliser la commande "-IMPORT", il apparaîtra avec une fenêtre m'indiquant d'entrer "Numéro de Révision" et "Nom de Révision". Puis-je avoir le même comportement d'une certaine manière dans .NET? – Alxandr

+0

Je ne suis pas sûr de cela, mais vous pouvez définir des attributs dans le code afin que vous puissiez toujours afficher votre propre boîte de dialogue Modale pour obtenir les informations. – Kratz