Creating first form application

Creating first form application

Anonymous
Not applicable
1,697 Views
13 Replies
Message 1 of 14

Creating first form application

Anonymous
Not applicable

Hi,

 

I have created some simple code which takes some blocks, draws a polyline (and sweeps a solid along it) from the block to a line and groups all the objects into a individually grouped name. The code is below and works perfectly for what I want when using the command line. What I want to do now is apply this is a form but I don't have much of an idea as to how to start.

 

I think I want to create a modal win form, and have created the design in VBE but I'm not sure how to connect the code to the buttons etc and then launch it within cad.

 

My CAD code is here

[CommandMethod("CBC")]
        public static void ConnectBlocksToCurves()
        {
            var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            if (doc == null)
                return;
            var db = doc.Database;
            var ed = doc.Editor;
            // Get the block to connect

            try{
                                
            //Filter list to only choose blocks 
            TypedValue[] filterlist = new TypedValue[1];
            filterlist[0] = new TypedValue(0, "INSERT");

            SelectionFilter filter = new SelectionFilter(filterlist);
            PromptSelectionResult psr1 = ed.GetSelection(filter);

            if (psr1.Status != PromptStatus.OK)
                return;

            SelectionSet psrSet = psr1.Value;
        
            // Define gully depth

            PromptDoubleOptions pdo = new PromptDoubleOptions("\nSelect gully depth to invert (mm)");
            pdo.AllowArbitraryInput = true;
            pdo.AllowNone = false;
            pdo.Keywords.Add("600");
            pdo.Keywords.Add("750");
            pdo.UseDefaultValue = true;
            pdo.Keywords.Default = "600";
                

            PromptDoubleResult res = ed.GetDouble(pdo);
            if (res.Status != PromptStatus.OK)
            return;
            double depth = res.Value / 1000; // Converts depth to m

            // Get the curve to connect it to

            var peo2 = new PromptEntityOptions("\nSelect the curve");
            peo2.SetRejectMessage("\nMust be a curve.");
            peo2.AddAllowedClass(typeof(Curve), false);
            var per2 = ed.GetEntity(peo2);
            if (per2.Status != PromptStatus.OK)
                return;


            //Specify connection pipe diameter
            PromptDoubleOptions pdo1 = new PromptDoubleOptions("\nSpecify pipe diameter (mm)");
            pdo1.AllowNone = false;
            pdo1.AllowZero = false;
            PromptDoubleResult pdr1 = ed.GetDouble(pdo1);

            if (pdr1.Status != PromptStatus.OK)
                return;

            double dia = pdr1.Value / 1000;

            var cId = per2.ObjectId;

            ed.WriteMessage("\nFound {0} references. ", psrSet.Count);


            
            
            using (var tr = db.TransactionManager.StartTransaction())
            {

                var c = tr.GetObject(cId, OpenMode.ForRead) as Curve;
                var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                var btr = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead);

                //Sort blocks based on distance along string
ObjectIdCollection ids = psr1.Value.GetObjectIds();
                                var sorted = new BlockReference[ids.Count];
                
                for (int i = 0; i < ids.Count; i++)
                {
                    sorted[i] = tr.GetObject(ids[i], OpenMode.ForRead) as BlockReference;
                    Polyline3d pl = new Polyline3d();
                    btr.AppendEntity(pl);
                    tr.ConnectBlockToCurve(btr, sorted[i], c, depth, pl);//Call extension to draw a temporary polyline from the block to the curve

                    //Create temp cirlc with which to sweep
                    
                    Circle circ = new Circle();
                    circ.Center = pl.StartPoint;
                    circ.Diameter = dia;


                    //Create sweep options
                    SweepOptionsBuilder sob = new SweepOptionsBuilder();

                    //Sweep Alignment
                    sob.Align = SweepOptionsAlignOption.AlignSweepEntityToPath;

                    //Sweep base point
                    sob.BasePoint = pl.StartPoint;

                    //Profile rotates to folow path

                    sob.Bank = true;

                    Entity ent;

                    Solid3d sol = new Solid3d();

                    sol.CreateSweptSolid(
                        circ,
                        pl,
                        sob.ToSweepOptions()
                        );
                    ent = sol;


                    btr.AppendEntity(ent);

                    tr.AddNewlyCreatedDBObject(ent, true);

                    pl.Erase(true);

                    // Create group and add items

                    //Check gd is empty or not
                    
                    gd.UpgradeOpen();
                    Group grp = new Group("3D Drainage Elements", true);

                    string grpName = "Group" + i.ToString();
                                          
                   ObjectId grpID = gd.SetAt(grpName, grp);
                   
                    
                    tr.AddNewlyCreatedDBObject(grp, true);
                    grp.Append(sorted[i].ObjectId);
                    grp.Append(ent.ObjectId);


                };
            

                    tr.Commit();

                }
            
            }catch(System.Exception ex){
                System.Windows.Forms.MessageBox.Show(ex.Message,"Error");
           }

            }

 

My form is here:

 

public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void depthBox_TextChanged(object sender, EventArgs e)
        {

        }

        private void blocks_Click(object sender, EventArgs e)
        {

        }

        private void curves_Click(object sender, EventArgs e)
        {

        }

        private void diaBox_TextChanged(object sender, EventArgs e)
        {

        }

        private void go_Click(object sender, EventArgs e)
        {

        }

                
        
    }

 

Thanks for reading.

0 Likes
Accepted solutions (1)
1,698 Views
13 Replies
Replies (13)
Message 2 of 14

Anonymous
Not applicable

As an example of what I have tried already, this is what I have in the block select button:

 

private PromptSelectionResult blocks_Click(object sender, EventArgs e)
        {
            var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;
            //Filter list to only choose blocks 
            TypedValue[] filterlist = new TypedValue[1];
            filterlist[0] = new TypedValue(0, "INSERT");

            SelectionFilter filter = new SelectionFilter(filterlist);
            PromptSelectionResult psr1 = ed.GetSelection(filter);
            return psr1;

        }

 

This is the only way I can think of to store the results of clicking the button, but this gives an error in the form code:

 

this.blocks.Click += new System.EventHandler(this.blocks_Click);

 

[Error: has the wrong return type]

0 Likes
Message 3 of 14

_gile
Consultant
Consultant

Hi,

 

An event handler must return 'void'.

You should create a property in the Form2 class and set its value from the event hadler, so, you can access it outside the Fom2 class.

 

        public SelectionSet BlockSelection { get; private set; }

        private void blocks_Click(object sender, EventArgs e)
        {
            var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;
            //Filter list to only choose blocks 
            TypedValue[] filterlist = new TypedValue[1];
            filterlist[0] = new TypedValue(0, "INSERT");

            SelectionFilter filter = new SelectionFilter(filterlist);
            PromptSelectionResult psr1 = ed.GetSelection(filter);
            if (psr1.Status == PromptStatus.OK)
                BlockSelection = psr1.Value;
             else
                BlockSelection = null;
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 14

Anonymous
Not applicable
Fantastic. Thank you. That's another one solved.

Could I also ask how you would pass data from the main code into the form designer code?

The reason being is that I have a number of blocks within the block table. I have created the list of blocks without issue but I want to create a listbox for each block within the form.

I tried to create a class called BlockList within Form2 but when I use the class in the form I get the error 'The type 'System.Windows.Forms.Form' has no property named 'BlockList'.
0 Likes
Message 5 of 14

_gile
Consultant
Consultant

One way is to add a parameter to the Form2 constructor and pass it the block list.

 

 

In the Form2 partial class:

public partial class Form2 : Form

{
    public Form2(BlockTableRecord[] blockList)
    {
        this.listbox1.DataSource = blockList;
        this.listbox1.DisplayMember = "Name";
    }

    public BlockTableRecord SelectedBlock
    {
        get { return this.listbox1.SelectedItem; }
    }

    // ...
}

In the 'main code' (assuming blockList is a BlockTablerecord array):

Form2 dialog = new Form2(blockList);
DialogResult result = AcAp.ShowModalDialog(dialog);
if (result = DailogResult.OK)
{
    BlockTableRecord btr = dialog.SelectedBlock;
    // ...
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 14

Anonymous
Not applicable

Morning,

 

My block list actually a list string, but I tried to adapt the code you provided as per the following:

 

public Form3(List<string> blocklist)
        {

            var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            if (doc == null)
                return;
            var db = doc.Database;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                List<string> retVal = new List<string>();
                foreach (ObjectId objId in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)objId.GetObject(OpenMode.ForRead);
                    if ((btr.XrefStatus == XrefStatus.NotAnXref)
                    && !btr.IsAnonymous && !btr.IsLayout && !btr.IsDependent)
                    {
                        string bloName = btr.Name;
                        retVal.Add(bloName);
                    }
                }
                retVal.Sort();


                this.blockList1.DataSource = retVal;
                this.blockList1.DisplayMember = "Name"; 
                
                tr.Commit();
                       
            } 

        }

        public string SelectedBlock
        {
            get { return this.blockList1.SelectedItem.ToString(); }
        }

 

When I ran the code, it didn't populate the blockList1. I was also unsure what the last section you wrote does, or where it should be located? Does this little section affect the population of block names in blockList1?

Form2 dialog = new Form2(blockList);
DialogResult result = AcAp.ShowModalDialog(dialog);
if (result = DailogResult.OK)
{
    BlockTableRecord btr = dialog.SelectedBlock;
    // ...
}

 

Apologies for my naiveté.

 

Cheers

 

0 Likes
Message 7 of 14

_gile
Consultant
Consultant

Hi,

 

If the type of the collection passed to the constructor is List<string>, just do this in the Windows.Form part:

 

public Form3(List<string> blocklist)
{
    foreach (string s in blocklist)
    {
        this.blockList1.items.Add(s);
    }
}

public string SelectedBlock { get { return this.blockList1.SelectedItem.ToString(); } }

 

In the calling class (AutoCAD part):

 

var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
if (doc == null)
    return;
var db = doc.Database;
List<string> retVal = new List<string>();
using (var tr = db.TransactionManager.StartTransaction())
{
    var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
    foreach (ObjectId objId in bt)
    {
        BlockTableRecord btr = (BlockTableRecord)objId.GetObject(OpenMode.ForRead);
        if ((btr.XrefStatus == XrefStatus.NotAnXref)
            && !btr.IsAnonymous && !btr.IsLayout && !btr.IsDependent)
        {
            string bloName = btr.Name;
            retVal.Add(bloName);
        }
    }
    retVal.Sort();
    tr.Commit();   
}
	
Form3 dialog = new Form3(retVal);
DialogResult result = AcAp.ShowModalDialog(dialog);
if (result = DailogResult.OK)
{
    BlockTableRecord btr = dialog.SelectedBlock;
    // ...
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 14

Anonymous
Not applicable
Hi,

Thanks for your help so far and sorry to keep pestering. I'm just struggling to understand the 'pass the constructor' bit. I have written the code which successfully grabs all the blocks in a drawing and populates the List<String>. I just don't know how this string gets to the public Form3()... section.
0 Likes
Message 9 of 14

_gile
Consultant
Consultant

Hi,

 

The upper snipet is tyring to show how to separate the AutoCAD business concern and the Windows User Interface one.

 

On the User Interface part:

  • use the string list passed to the constructor to populate the ListBox
  • expose the ListBox selected item

 

On the AutoCAD part:

  • build the block names list
  • create a new instance of the UI passing it the block names list
  • show the dialog and get DialogResult
  • get the selected item in dialog and use it

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 10 of 14

Anonymous
Not applicable
Thanks, Giles! I'm pulling my hair out here trying to get it to work and tried everything I can think of but I don't think I possess the understanding of the net languages enough. I think therefore, I'll have to leave it and use a work around.

Thanks again
0 Likes
Message 11 of 14

_gile
Consultant
Consultant
Accepted solution

Here's a simple working example.

 

The Form1 class:

 

using System.Collections.Generic;
using System.Windows.Forms;

namespace SimpleDialogSample
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// Creates a new instance of Form1
        /// </summary>
        /// <param name="blockNames">List of strings used to populate the list box</param>
        public Form1(List<string> blockNames)
        {
            InitializeComponent();

            // populate the list box with blockNames contents
            foreach (string name in blockNames)
            {
                listBox1.Items.Add(name);
            }
        }

        /// <summary>
        /// Gets the selected name
        /// </summary>
        public string SelectedName
        {
            get { return listBox1.SelectedItem.ToString(); }
        }
    }
}

The AutoCAD command class

 

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;

namespace SimpleDialogSample
{
    public class Commands
    {
        /// <summary>
        /// Shows a dialog to select a block name
        /// </summary>
        [CommandMethod("CMD1")]
        public void Cmd1()
        {
            Database db = Application.DocumentManager.MdiActiveDocument.Database;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // build the block names list
                BlockTable blockTable = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                List<string> names = new List<string>();
                foreach (ObjectId id in blockTable)
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                    if (!(btr.IsLayout || btr.IsAnonymous || btr.IsFromExternalReference || btr.IsDependent))
                    {
                        names.Add(btr.Name);
                    }

                }

                // create a new instance of Form1 passing it the block names list
                Form1 dialog = new Form1(names);

                // show the dialog and get the result
                if (Application.ShowModalDialog(dialog) == System.Windows.Forms.DialogResult.OK)
                {
                    string blockName = dialog.SelectedName;
                    Application.ShowAlertDialog($"{blockName} {blockTable[blockName]}");
                }
                tr.Commit();
            }
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 12 of 14

Anonymous
Not applicable
Thanks so much, Giles! I have finally got it working on my form. Just one final thing, if I may? Hypothetically, if I wanted to bring in two List<strings>, how would I initiate Form3 to use both these variables, without creating two instances of the form?
0 Likes
Message 13 of 14

_gile
Consultant
Consultant

The Form3 constructor may have 2 (or as many as needed) parameters.

 

public Form1(List<string> list1, List<string> list2)
{
initializeComponents();

// populate the controls with list1 and list2
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 14 of 14

Anonymous
Not applicable

Perfect. Thank you very much for your help. Much appreciated.

 

I'll leave you in peace now 🙂

 

Cheers

 

0 Likes