First line program

First line program

pedcarson
Enthusiast Enthusiast
2,313 Views
9 Replies
Message 1 of 10

First line program

pedcarson
Enthusiast
Enthusiast

Hi everyone,

 

I'm trying to create a basic program using the code given on the AutoCAD developers guide.

 

I've created a class and then called the class with a button in a c# windows form. The code is for the class is below. The line "Document acDoc = Application.DocumentManager.MdiActiveDocument;" causes the program to stop? Any ideas? The button calls class DrawLine.AddLine

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

namespace AutoCAD_Line_Test
{
     class DrawLine
    {
        [CommandMethod("AddLine")]
        public static void AddLine()

        {

            // Get the current document and database

            Document acDoc = Application.DocumentManager.MdiActiveDocument;

            Database acCurDb = acDoc.Database;



            // Start a transaction

            using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())

            {

                // Open the Block table for read

                BlockTable acBlkTbl;

                acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,

                                             OpenMode.ForRead) as BlockTable;



                // Open the Block table record Model space for write

                BlockTableRecord acBlkTblRec;

                acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],

                                                OpenMode.ForWrite) as BlockTableRecord;



                // Create a line that starts at 5,5 and ends at 12,3

                Line acLine = new Line(new Point3d(5, 5, 0),

                                       new Point3d(12, 3, 0));



                acLine.SetDatabaseDefaults();



                // Add the new object to the block table record and the transaction

                acBlkTblRec.AppendEntity(acLine);

                acTrans.AddNewlyCreatedDBObject(acLine, true);



                // Save the new object to the database

                acTrans.Commit();

            }

        }
    }

}


0 Likes
Accepted solutions (1)
2,314 Views
9 Replies
Replies (9)
Message 2 of 10

norman.yuan
Mentor
Mentor

Well, since you know the code stops at the first line, all lines of the code after that are irrelevant, and the relevant code that might be the real cause of the issue is the caller: the windows form, which you did not show, nor describe how/where it runs.

 

Is this win form in another DLL file that also runs as AutoCAD Add-in (NETLOADED into AutoCAD)? or it is part of stand-alone app (EXE)? If it is later, then from the EXE, you CANNOT use DLLs built with AutoCAD .NET API.

 

Describe what kind of project you are doing, and from where the win form runs.

Norman Yuan

Drive CAD With Code

EESignature

Message 3 of 10

_gile
Consultant
Consultant

Hi,

 

The AutoCAD .NET API is designed to run in-process only.

If your project is a standalone executable (e.g. a Windows Form based project) you cannot directly use the AutoCAD .NET API.

See this topic in the Developer's Guide.

 

If you want to create an AutoCAD custom command as your example, you have to build a DLL (starting from a Class Library template) and load it in AutoCAD (the class containing the command have to be public).

See this section in the Developer's Guide.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 10

pedcarson
Enthusiast
Enthusiast
I wasn't aware of netloading. I assumed I could run an exe. My aim is to
have a windows form program with different values to fit in which would
then draw certain shapes, eventually import a block and then scale it etc.
All from a couple of inputs from the user.

This is obviously my first program I'm writing in c# for autocad. I don't
see much in tutorials for c# on the internet.
0 Likes
Message 5 of 10

norman.yuan
Mentor
Mentor

@pedcarson wrote:
I wasn't aware of netloading. I assumed I could run an exe.
......
I'm writing in c# for autocad. I don't
see much in tutorials for c# on the internet.

Since you seems emphasizing C#, and not aware of "NETLOAD":

 

Using C#, or other .NET language (VB.NET, F#...) does not matter here. What matters is which AutoCAD API to use: if you use AutoCAD .NET API, as your code showed in your original post, you CANNOT use them in EXE application, you would need redesign/restructure of your solution: app runs inside AutoCAD (as DLL), or app run outside AutoCAD (exe, which in turn automate AutoCAD in someway), based on the APIs you use (.NET API, or COM API, or both mixed).

Norman Yuan

Drive CAD With Code

EESignature

Message 6 of 10

BKSpurgeon
Collaborator
Collaborator

Hi OP

 

my estimable colleagues @_gile and @norman.yuan have said it all and anything they write is very good. but i'm not sure if you understand what they are saying given your comments. so i thought to simplify it:

 

in process

this means that the user has to first open autocad. i.e. double click on the AutoCAD icon and start up autocad. once autocad is open and a drawing is opened, then the user has to type in a command: "NETLOAD" and has to select the a file - the result of all your programming/coding efforts. once that file is selected the user then has to run the command "AddLine". the command will run as you have coded it. in order to do it this way you will need the .Autocad net API

 

 

out of process

In this case, you don't necessarily have to manually open AutoCAD up. you create your own program, and you open it (much like you would open MS word etc) and your "AddLine" command would run without you, as a user, manually opening autocad and netloadin etc.. if you're going down this path you need to use the COM interop API.

 

using both

if you really want to use the .net API, but did not want to manually netload, then you can use a combination of both of the above:

 

From the documentation:

 

 

If you need to create a stand-alone application to drive AutoCAD, it is best to create an application that uses the CreateObject and GetObject methods to create a new instance of an AutoCAD application or return one of the instances that is currently running. Once a reference to an AcadApplication is returned, you can then load your in-process .NET application into AutoCAD by using the SendCommand method that is a member of the ActiveDocument property of the AcadApplication.

 

> As an alternative to executing your .NET application in-process, could use COM interop for your application.

 

 

hope this clears it  up.

 

 

 

0 Likes
Message 7 of 10

pedcarson
Enthusiast
Enthusiast
Thanks for such a detailed response. I would prefer to use an external
program but I think the main objective is to have variables at the
beginning that are defined by the user. So instead of just typing add line
and having x and y coordinates, I could have x and y set to variables that
are set at the start through a form. Then when addline is executed, the
line changes according to the variables.

Similarly I was hoping to have a block inserted and scaled. I'd rather get
away from typing commands and have a user interface that anyone could use?


0 Likes
Message 8 of 10

_gile
Consultant
Consultant
Accepted solution

Hi,

 

If you want to use a standalone Windows Form executable, you have to keep mind you have to use the COM API which is less powerfull than the .NET one and which have compatibility issues with platform (32 or 64 bits) and with AutoCAD major versions.

 

 

Here's a trivial example using a very simple Windows Form:

 

DrawLine.png

The Form Class code (Autodesk.AutoCAD.Interop and Autodesk.AutoCAD.Interop.Common libraries corresponding to targetted version of AutoCAD have to be referenced).

 

using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WinFormStandAlone
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            btnOK.Click += BtnOK_Click;
        }

        private void BtnOK_Click(object sender, EventArgs e)
        { 
            double[] startPoint, endPoint;
            if (!TryParseToPoint(txtStartPoint.Text, out startPoint))
                MessageBox.Show($"Invalid Start point.");
            if (!TryParseToPoint(txtEndPoint.Text, out endPoint))
                MessageBox.Show($"Invalid End point.");

            AcadApplication acadApp;
            string progId = "AutoCAD.Application.19";
            try
            {
                acadApp = (AcadApplication)Marshal.GetActiveObject(progId);
            }
            catch
            {
                try
                {
                    acadApp = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(progId), true);
                }
                catch
                {
                    MessageBox.Show("Instance of 'AutoCAD.Application' could not be created.");
                    return;
                }
            }
            while (true)
            {
                try { acadApp.Visible = true; break; }
                catch { }
            }
            acadApp.WindowState = AcWindowState.acMax;
            AcadDocument doc = acadApp.ActiveDocument;
            AcadModelSpace modelSpace = doc.ModelSpace;
            modelSpace.AddLine(startPoint, endPoint);
        }

        private static bool TryParseToPoint(string source, out double[] point)
        {
            point = null;
            string[] array = source.Split(',');
            if (array.Length < 2 || array.Length > 3)
                return false;
            double x, y, z = 0.0;
            if (!double.TryParse(array[0], out x))
                return false;
            if (!double.TryParse(array[1], out y))
                return false;
            if (array.Length == 3 && !double.TryParse(array[2], out z))
                return false;
            point = new[] { x, y, z };
            return true;
        }
    }
}

 

 

Using the AutoCAD .NET API, you can also have a user interface as modal dialog (the dialog form have the same controls as upper):

 

Dialog.png

 

 

 

using System.Windows.Forms;

namespace InProcessDialog
{
    public partial class Dialog : Form
    {
        public string StartPoint => txtStartPoint.Text;

        public string EndPoint => txtEndPoint.Text;

        public Dialog()
        {
            InitializeComponent();
            btnOK.DialogResult = DialogResult.OK;
        }
    }
}

The dialog is opened from AutoCAD with the "DRAWLINEDLG" command:

 

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Windows.Forms;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace InProcessDialog
{
    public class Commands
    {
        [CommandMethod("DrawLineDlg")]
        public void DrawLine()
        {
            var dlg = new Dialog();
            if (AcAp.ShowModalDialog(dlg) == DialogResult.OK)
            {
                Point3d startPoint, endPoint;
                if (!TryParseToPoint(dlg.StartPoint, out startPoint))
                {
                    MessageBox.Show($"Invalid Start point.");
                    return;
                }
                if (!TryParseToPoint(dlg.EndPoint, out endPoint))
                {
                    MessageBox.Show($"Invalid End point.");
                    return;
                }

                var doc = AcAp.DocumentManager.MdiActiveDocument;
                var db = doc.Database;
                var ed = doc.Editor;
                using (var tr = db.TransactionManager.StartTransaction())
                {
                    BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(
                        SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
                    Line line = new Line(startPoint, endPoint);
                    modelSpace.AppendEntity(line);
                    tr.AddNewlyCreatedDBObject(line, true);
                    tr.Commit();
                }
            }
        }

        private static bool TryParseToPoint(string source, out Point3d point)
        {
            point = default(Point3d);
            string[] array = source.Split(',');
            if (array.Length < 2 || array.Length > 3)
                return false;
            double x, y, z = 0.0;
            if (!double.TryParse(array[0], out x))
                return false;
            if (!double.TryParse(array[1], out y))
                return false;
            if (array.Length == 3 && !double.TryParse(array[2], out z))
                return false;
            point = new Point3d(x, y, z);
            return true;
        }
    }
}

You can also use a modeless user interface from AutoCAD (typically a palette set)

The user control (a palette tab of the palette set) contains the same controls as the upper forms.

 

 

Palette.png

 

 

With modeless user interfaces, it's simpler and safer to call cusom commands from event handlersso that AutoCAD takes care of locking the document and setting the focus to AutoCAD window.

 

using System;
using System.Windows.Forms;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace InProcessPalette
{
    public partial class DrawLineTab : UserControl
    {
        public DrawLineTab()
        {
            InitializeComponent();
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            if (!IsValidPoint(txtStartPoint.Text))
            {
                MessageBox.Show("Invalid Start Point.");
                return;
            }
            if (!IsValidPoint(txtEndPoint.Text))
            {
                MessageBox.Show("Invalid End Point.");
                return;
            }
            var doc = AcAp.DocumentManager.MdiActiveDocument;
            doc?.SendStringToExecute($"DrawLineCmd {txtStartPoint.Text} {txtEndPoint.Text} ", false, false, false);
        }

        private bool IsValidPoint(string source)
        {
            string[] array = source.Split(',');
            if (array.Length < 2 || array.Length > 3)
                return false;
            double x;
            if (!double.TryParse(array[0], out x))
                return false;
            if (!double.TryParse(array[1], out x))
                return false;
            if (array.Length == 3 && !double.TryParse(array[2], out x))
                return false;
            return true;
        }
    }
}

The palette is showned by running the "CMD_PALETTE" command, and a click on the OK button launches the "DrawLineCmd" which can also be directly called.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.Windows;

namespace InProcessPalette
{
    public class Commands
    {
        static PaletteSet palette;
        static bool wasVisible;

        [CommandMethod("CMD_PALETTE")]
        public void CmdPalette()
        {
            if (palette == null)
            {
                palette = new PaletteSet(
                    "Palette",
                    "CMD_PALETTE",
                    new Guid("{4E63230E-EEB0-45FF-A9A1-65D4FDEAE5DC}"));
                palette.Style =
                    PaletteSetStyles.ShowAutoHideButton |
                    PaletteSetStyles.ShowCloseButton |
                    PaletteSetStyles.ShowPropertiesMenu;
                palette.MinimumSize = new System.Drawing.Size(250, 150);
                palette.Add("Draw Line", new DrawLineTab());

                var docs = AcAp.DocumentManager;
                docs.DocumentBecameCurrent += (s, e) =>
                    palette.Visible = e.Document == null ? false : wasVisible;
                docs.DocumentCreated += (s, e) =>
                    palette.Visible = wasVisible;
                docs.DocumentToBeDeactivated += (s, e) =>
                    wasVisible = palette.Visible;
                docs.DocumentToBeDestroyed += (s, e) =>
                {
                    wasVisible = palette.Visible;
                    if (docs.Count == 1)
                        palette.Visible = false;
                };
            }
            palette.Visible = true;
        }

        [CommandMethod("DrawLineCmd")]
        public void DrawLine()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            var options = new PromptPointOptions("\nStart Point: ");
            var result = ed.GetPoint(options);
            if (result.Status != PromptStatus.OK)
                return;
            var startPoint = result.Value;

            options.BasePoint = startPoint;
            options.UseBasePoint = true;
            options.Message = "\nend Point: ";
            result = ed.GetPoint(options);
            if (result.Status != PromptStatus.OK)
                return;
            var ucs = ed.CurrentUserCoordinateSystem;
            var endPoint = result.Value.TransformBy(ucs);
            startPoint = startPoint.TransformBy(ucs);

            using (var tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(
                    SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
                Line line = new Line(startPoint, endPoint);
                modelSpace.AppendEntity(line);
                tr.AddNewlyCreatedDBObject(line, true);
                tr.Commit();
            }
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 9 of 10

BKSpurgeon
Collaborator
Collaborator

As @_gile has kindly noted in his post - and also very kindly provided sample code too: you can certainly have forms in your plugin - if you're doing that then I recommend you do things in process -  use the powerful .net API. just type in "AddLine" and the form will come up. Refer to Gilles' post.

0 Likes
Message 10 of 10

pedcarson
Enthusiast
Enthusiast

Thank you for the detailed response, this will certainly help with understanding how to put all this together!

0 Likes