.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Edit a Block via C#

28 REPLIES 28
Reply
Message 1 of 29
klahie
5366 Views, 28 Replies

Edit a Block via C#

Hi community,

 

my job is to make an editor for blocks in C#. There should be a WPF-window were the user can see a list of blocks. He selects one and than he should see an overview about block attributes like insertionpoint, basepoint, rotation and scaling and he should be able to edit these attributes in this form. 

 

I know how i can populate a list of block names, but I fail at finding an opportunity to edit these attributes. 

I get the blocktablerecords, but then?

 

I am very, very new to Autocad, and I've googled the last five hours unsuccessfully for a solution which is suitable for my problem. 

 

Please help me, it is very urgent! 

 

Kind regards, 

Klaus

28 REPLIES 28
Message 21 of 29
Alfred.NESWADBA
in reply to: klahie

Hi,

 

>> I see many attributes like kmh, beleuchtet, mast_id, etc. but non of those, I've mentioned.

Male a breakpoint and watch the variable tAttDef and it's properties. There you find e.g. a property Layer, you find a property Position for insertion point, you also find the property Height and Rotation.

But once more, be careful ... changing these values for AttributeDefinition(s) will only effect the next commands _INSERT (EINFÜGE) .... so for the blockreferences you create after making the changes to your definitions.

 

If you want to change the properties and values for the BlockReferences already inserted in your drawing you have first to select the BlockReferences, then scan through their AttributeCollection (= collection of ObjectIDs pointing to the AttributeReference).

 

You have a lot of learning quite in front of you ==> your screen (showing the help-file) 😉

 

Good luck, - alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
Ingenieur Studio HOLLAUS ... www.hollaus.at ... blog.hollaus.at ... CDay 2024
------------------------------------------------------------------------------------
(not an Autodesk consultant)
Message 22 of 29
klahie
in reply to: Alfred.NESWADBA

Okay, next problem:

 

When I try to show the window:

 

Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessWindow(window);

 an errorpage comes up in visual studio.

Error:

No source Available

No symbols are loaded for any call stack frame. The source code cannot be displayed.

 

What does this mean to me?

 

Kind Regards

Message 23 of 29
klahie
in reply to: klahie

Okay, error is gone. I don't know why, but its gone...

 

Now I've excluded the databaselogic in a new class, before it was in the myPlugin class.

The problem now is, I can't read data from BlockTable, BlockTableRecord, etc. because of an AccessViolationException. Is there any option to give the rights, which the myPlugin class has, to another class?

 

Reading data from BlockTable, etc. works great in the myPlugin class, but if i exclude the logic, the programm fails.

Message 24 of 29
Alfred.NESWADBA
in reply to: klahie

Hi,

 

>> No source Available. No symbols are loaded for any [...]

look to >>>that link<<< that shows what's going on when getting this message.

 

The other problem I'm sorry, I have not understood what is now going on with your code and when BlockTableRecord-reading works and when not. Maybe you can minimize the code and upload a snippet that makes it more clear for us.

 

- alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
Ingenieur Studio HOLLAUS ... www.hollaus.at ... blog.hollaus.at ... CDay 2024
------------------------------------------------------------------------------------
(not an Autodesk consultant)
Message 25 of 29
klahie
in reply to: Alfred.NESWADBA

Okay, this is the code:

 

My Plugin Class:

 

using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Windows;
using System.Collections.Generic;
using System.Collections;



[assembly: ExtensionApplication(typeof(AutoCAD_BE.MyPlugin))]
[assembly: CommandClass(typeof(AutoCAD_BE.MyPlugin))]

namespace AutoCAD_BE
{   
    public class MyPlugin : IExtensionApplication
    {   
        [CommandMethod("loadbe")]
        public void LoadBe()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Transaction tr = db.TransactionManager.StartTransaction();
            Datenbank datenbank = new Datenbank(doc, db, tr);

            Window1 window = new Window1(datenbank);
            Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessWindow(window);

        }
    }

This is my Window Class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Autodesk.AutoCAD.DatabaseServices;

namespace AutoCAD_BE
{
    public partial class Window1 : System.Windows.Window
    {
        Datenbank datenbank;

        public Window1(Datenbank datenbank)
        {
            this.datenbank = datenbank;
            InitializeComponent();
        }
        // fills the listBox in the UI with blocknames
        public void fillList(List<String> blockList)
        {
            foreach (String s in blockList)
            {
                listBlocks.Items.Add(s.ToString());
            }

        }
        // fills the attribute-editting form with the attributes of the in the listbox selected block
        private void listBlocks_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            datenbank.setBlockAttributes(listBlocks.SelectedItem.ToString());
            AttributeDefinition tAttDef = datenbank.getAttributeDefinition();
            tbRotation.Text = tAttDef.Layer;
            tbLayer.Text = tAttDef.Layer;
            tbEinfuegepunktX.Text = tAttDef.Position.X.ToString();
            tbEinfuegepunktY.Text = tAttDef.Position.Y.ToString();
            tbEinfuegepunktZ.Text = tAttDef.Position.Z.ToString();
        }
        
        //calls the fillList-method with the in the class datenbank generated blockList
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            fillList(datenbank.getBlockTableRecordNames());
        }
    }
}

and this is my Class whitch holds the Transactions with the database:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Windows;

namespace AutoCAD_BE
{
    public class Datenbank
    {
        Database db;
        Transaction tr;
        Document doc;
        AttributeDefinition AttDef;
        List<String> BlockList = new List<String>();

        public Datenbank(Document doc, Database db, Transaction tr)
        {
            this.doc = doc;
            this.db = db;
            this.tr = tr;
            setBlockTableRecordNames();
        }

        //Gets the Blocktablerecordnames out of the blocktable and adds them into a list.
        public void setBlockTableRecordNames()
        {
            using (tr)
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId objId in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(objId, OpenMode.ForRead);

                    BlockList.Add(btr.Name);
                }
            }
        }

        public List<String> getBlockTableRecordNames()
        {
            return (BlockList);
        }

        //Gets the AttributeDefinitions out of the BlocktableRecord
        public void setBlockAttributes(String blockname)
        {
          using (tr)
            {
               BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId objId in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(objId, OpenMode.ForRead);
                    if (btr.Name == blockname)
                    {
                        foreach (ObjectId tObjID in btr)
                        {
                            if ((tObjID.IsValid) && (!tObjID.IsErased) && (tObjID.ObjectClass.DxfName.ToUpper() == "ATTDEF"))
                            {
                                AttributeDefinition tAttDef = (AttributeDefinition)tr.GetObject(tObjID, OpenMode.ForRead);
                                setAttributeDefinition(tAttDef);
                            }
                        }
                    }
                }
            }
        }
        public void setAttributeDefinition(AttributeDefinition tAttDef)
        {
            AttDef = tAttDef;
        }

        public AttributeDefinition getAttributeDefinition()
        {
            return AttDef;
        }

        public void refreshBlockList(Transaction tr, Database db)
        {
            this.tr = tr;
            this.db = db;
            getBlockTableRecordNames();
        }
    }
}

 Attached, you find a screenshot of the error and the whole "solution". Smiley Wink

 

What I want to do:

MyPlugin.cs gives the Document, Database and Transaction to Datenbank.cs. 

Datenbank.cs gets the Blocktablerecordnames, my WPF-Window Window1.xaml is loaded and gets the datenbank.cs. In the Window1.xaml, the blocktablerecordnames are shown in a listbox, the user selects one and the setBlockAttributes-method in datenbank.cs is called. The datenbank.cs sets the right AttributeDefinition, the Window-class gets it and fills the attributes in to a form.

 

As I've understood the error, I have no permissions to access the Blocktable/Blocktablerecord/AttributeDefinitions/etc. in another class than myPlugin.cs. 

 

Kind Regards

Klaus

 

Message 26 of 29
Alfred.NESWADBA
in reply to: klahie

Hi,

 

you load your application window with this procedure:

[CommandMethod("loadbe")]
public void LoadBe()
  {
     Document doc = Application.DocumentManager.MdiActiveDocument;
     Database db = doc.Database;
     Transaction tr = db.TransactionManager.StartTransaction();
     Datenbank datenbank = new Datenbank(doc, db, tr);

     Window1 fenster = new Window1(datenbank);
            Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessWindow(fenster);

  }

Now the problem is, that you start a transaction, then you open your application window modeless ==> and modeless means your code does run to the end of "Public void LoadBe". That means also that the livetime of your transaction finishes (and the GarbageCollector purges it from memory at any time).

 

So when you now click to any button on your form you try to use a transaction that does not exist any more. ==> That's the reason why your code crashes.

 

Let me give some tips how I do see the structure you built:

  • You have a modeless dialog, so the user can goon working with AutoCAD. He also can change the active document by creating a new one or opening an existing DWG or close your DWG you just analyzed. ==> Your form does not recognize this, any click to any function points to the document that was active during load of your app.
  • If you have a modeless dialog, don't assign objects that are DWG-specific (doc, db, tr) in the code that starts the dialog. Assign these variables when the user presses a button on your form (or does any other action that accesses AutoCAD). In this functions (that get called not from a self defined command) use also doc.LockDocument to get all rights as the document is then really locked.
  • I haven't seen any Try-Catch statements within your code, so your code crashes AutoCAD. If you use Try-Catch you may do some errors, but as long as they are in a Try{}-zone your dotNET code will run into the catch and the error gets handled without crashing AutoCAD (in most cases).

 

Please don't see that as criticism, it's just a small list of tips. 😉

 

- alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
Ingenieur Studio HOLLAUS ... www.hollaus.at ... blog.hollaus.at ... CDay 2024
------------------------------------------------------------------------------------
(not an Autodesk consultant)
Message 27 of 29
klahie
in reply to: Alfred.NESWADBA

This is no criticism, this is great! I've never had such an good support in any forum. Smiley Happy

 

Okay, I thought about that a while and came to the conclusion that it probably had a reason that the old VB version of this blockeditor had a modal window. Smiley Wink

 

If I want to do that with 

Autodesk.AutoCAD.ApplicationServices.Application.ShowModalWindow(window);

I probably have to do more than that to get it working. Am I right?

I did the doc.LockDocument() too but its still not working... Smiley Sad

 

PS: Wirklich, aller herzlichsten Dank für deine Hilfe, ich wäre wohl ziemlich aufgeschmissen ohne dich. Smiley Wink

Message 28 of 29
klahie
in reply to: klahie

I still have the same problem. Smiley Sad

 

Can anybody help me again?

Message 29 of 29
jeff
in reply to: klahie

When you say a 'Block's attributes' do you mean its properties or an actual Attribute object.

 

 

From there are you wanting a AttributeDefinition or AttributeReference?

 

You will never see a AttributeDefinition in a drawing. What you see is a AttributeReference which is built from a AttributeDefinition and from there they are no longer linked.

 

So for one block when you make a edit do you want to change all 'insertions' or just one?

Wanting to show a single value in your window for layer, scale, etc.... would lead me to think a AttributeDefinition since each AttributeReference could vary.

 

Keep in mind if you want to change position or scale the existing AttributeReferences could already been scaled and moved separately from the BlockReference so should changes be relative to existing AttributeReference or created from new AttributeDefinition? 

You can also find your answers @ TheSwamp

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost