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

How to store a list of objectids in a global variable

12 REPLIES 12
SOLVED
Reply
Message 1 of 13
MS7000
832 Views, 12 Replies

How to store a list of objectids in a global variable

Hi,

 

is it possible to store a list of objectids in a global list. This list should be available each time I call a command.

Right I do not want to take care about a the mdi environment, I am just looking for a solution for a single document.

I have written a short class to clarify my requiement.

 

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput

Public Class Class1
    Implements IExtensionApplication

    Private _lObjIds As List(Of ObjectId)

    Public Sub Initialize() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Initialize
        _lObjIds = New List(Of ObjectId)
    End Sub

    Public Sub Terminate() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Terminate

    End Sub

    <CommandMethod("gvSelectObject")> _
    Public Sub gvSelectObject()
        Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Dim ed As Editor = doc.Editor
        Dim db As Database = doc.Database

        ' Select Object
        Dim pso As New PromptSelectionOptions
        pso.SingleOnly = True
        pso.MessageForAdding = "Select Object"
        Dim psr As PromptSelectionResult = ed.GetSelection(pso, Nothing)
        If psr.Status <> PromptStatus.OK Then
            ed.WriteMessage(vbCrLf & "Nothing selected")
            Exit Sub
        End If

        ' Check, if global list exists
        If _lObjIds Is Nothing Then
            ed.WriteMessage(vbCrLf & "Global List is not available")
            Exit Sub
        Else
            _lObjIds.Add(psr.Value.GetObjectIds(0))
            ed.WriteMessage(vbCrLf & "Global List has {0} IDs", _lObjIds.Count)
        End If

    End Sub

End Class

Do I have to use any flag for my method?

Would be nice if anyone could provide me a solution.

 

Regards

Michael

12 REPLIES 12
Message 2 of 13
MS7000
in reply to: MS7000

OK, sorry for wasting your time. I found the solution by myself.

The thraed from yesterday from jmaeding took me on the right way.

He linked to a post from Kean (http://through-the-interface.typepad.com/through_the_interface/2006/10/perdocument_dat_1.html)

I just needed to declare my variable an my method as shared.

 

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput

Public Class Class1
    Implements IExtensionApplication

    Private Shared _lObjIds As List(Of ObjectId)

    Public Sub Initialize() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Initialize
        _lObjIds = New List(Of ObjectId)
    End Sub

    Public Sub Terminate() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Terminate

    End Sub

    <CommandMethod("gvSelectObject")> _
    Public Shared Sub gvSelectObject()
        Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Dim ed As Editor = doc.Editor
        Dim db As Database = doc.Database

        ' Select Object
        Dim pso As New PromptSelectionOptions
        pso.SingleOnly = True
        pso.MessageForAdding = "Select Object"
        Dim psr As PromptSelectionResult = ed.GetSelection(pso, Nothing)
        If psr.Status <> PromptStatus.OK Then
            ed.WriteMessage(vbCrLf & "Nothing selected")
            Exit Sub
        End If

        ' Check, if global list exists
        If _lObjIds Is Nothing Then
            ed.WriteMessage(vbCrLf & "Global List is not available")
            Exit Sub
        Else
            _lObjIds.Add(psr.Value.GetObjectIds(0))
            ed.WriteMessage(vbCrLf & "Global List has {0} IDs", _lObjIds.Count)
        End If

    End Sub

End Class

 

 

Message 3 of 13
_gile
in reply to: MS7000

Hi,

 

It looks like you didn't understand the per document vs per session behavior.

If you declare your collection as static (Shared) member of your class, this collection will be available in each document of the session.

If the field isn't declared static (as an instance member of the class), it will be available for this 'document instance' each time the command is called.

 

Try the 2 commands in the following code in a document, then in another one, the back in the first one...

 

    public class Class1
    {
        private static ObjectIdCollection ids = new ObjectIdCollection();

        [CommandMethod("PerSession")]
        public static void PerSessionList()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;

            PromptEntityResult result = ed.GetEntity("\nSelect object: ");
            if (result.Status != PromptStatus.OK)
                return;

            ids.Add(result.ObjectId);
            ed.WriteMessage("\nSession List has {0} IDs", ids.Count);
        }
    }

    public class Class2
    {
        private ObjectIdCollection ids = new ObjectIdCollection();

        [CommandMethod("PerDocument")]
        public void PerDocumentList()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;

            PromptEntityResult result = ed.GetEntity("\nSelect object: ");
            if (result.Status != PromptStatus.OK)
                return;

            ids.Add(result.ObjectId);
            ed.WriteMessage("\nDocument List has {0} IDs", ids.Count);
        }
    }

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 13
_gile
in reply to: _gile

The upper test can be done with a single command:

 

    public class Class1
    {
// static (Shared) field private static ObjectIdCollection globalIds = new ObjectIdCollection();

// instance field (related to each document calling the command) private ObjectIdCollection localIds = new ObjectIdCollection(); [CommandMethod("gvSelectObject")] public void gvSelectObject() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptEntityResult result = ed.GetEntity("\nSelect object: "); if (result.Status == PromptStatus.OK) { globalIds.Add(result.ObjectId); localIds.Add(result.ObjectId); } ed.WriteMessage( "\nGlobal List has {0} IDs\nLocal LIST has {1} IDs", globalIds.Count, localIds.Count); } }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 5 of 13
SENL1362
in reply to: _gile

Hi Gilles,
Nice clear explanation of the session versus document storage but for what purpose is a session storage of ObjectIds?

As far as i know ObjectIds are private to documents and valid up to the closure of the document or am i wrong?

Message 6 of 13
_gile
in reply to: SENL1362


SENL1362 a écrit :

Hi Gilles,
Nice clear explanation of the session versus document storage but for what purpose is a session storage of ObjectIds?

As far as i know ObjectIds are private to documents and valid up to the closure of the document or am i wrong?


By my side I do not see any purpose to store ObjectIdS at session level, even ObjectIdS are unic in a session whatever the document they own.

 

I just tried to explain to MS7000 he was going the wrong route using a static/Shared field to store ObjectIds and how he can use a "document level global variable" with an 'document instance' field.

 

When we create a command method in a class, we have to think as if each first calling of the command in  a document creates a new instance of this class which all non-static members are instance members of this 'document instance'.

 

Maybe using explicit constructors is more self explanatory:

 

    public class TestClass
    {
        // static fields (session scope) declaration
        private static DocumentCollection docs;
        private static int sesCnt;

        // instance fields (document scope) declaration
        private Document doc;
        private Database db;
        private Editor ed;
        private static int docCnt;

        // static constructor (static fields initialization)
        // run once in the session at first command calling
        static TestClass()
        {
            docs = Application.DocumentManager;
            sesCnt = 0;
            docs.MdiActiveDocument.Editor.WriteMessage(
                "\nSession field initialization\nSession Counter = {0}", sesCnt);
        }

        // instance constructor (instance fields initialization)
        // run once in each document at first command calling
        public TestClass()
        {
            doc = docs.MdiActiveDocument;
            db = doc.Database;
            ed = doc.Editor;
            docCnt = 0;
            ed.WriteMessage(
"\nDocument fields initialization\nDocument Counter = {0}", docCnt); }
// using of static and instance fields [CommandMethod("TESTCMD")] public void TestCommand() { ed.WriteMessage( "\n{0}\nSession Counter = {1}\nDocument Couter = {2}", db.Filename, ++sesCnt, ++docCnt); } }

 

 

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 7 of 13
SENL1362
in reply to: _gile

You're sample was the purpose of my question. I thought that if the level of the question is at the difference of static versus instance then he needs to be warned that ObjectId's are a bad sample for session storage. Something like configuration vars used for all opened documents should be stored static and document specific vars should be stored at the class level.

Thanks Gilles.
Message 8 of 13
MS7000
in reply to: SENL1362

OK, I understand the difference between perDocument and perSession.

But I have another problem with the context.

- The id collection should exist in document focus.

-The id collection should be initialized while the assembly is loaded.

- The ObjectModified reactor should monitor the changes.

- With the method 'SelLine' a selected line should be added to the id collection.

In my code below the id collection for the reactor and for the method is different. Why?

How could I achieve the mentioned requirements?

 

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput

Public Class iExApp
    Implements IExtensionApplication

    Public Sub Initialize() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Initialize
        Dim x As New Class1
    End Sub

    Public Sub Terminate() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Terminate

    End Sub
End Class

Public Class Class1
    Private ids As New ObjectIdCollection()

    Public Sub New()
        AddHandler Application.DocumentManager.MdiActiveDocument.Database.ObjectModified, AddressOf OnObjectModified
    End Sub

    Private Sub OnObjectModified(sender As Object, e As Autodesk.AutoCAD.DatabaseServices.ObjectEventArgs)
        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
        ed.WriteMessage(vbLf & "Modify - Document List has {0} IDs", ids.Count)
    End Sub

    <CommandMethod("SelLine")> _
    Public Sub SelLine()
        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor

        ' Select Line
        Dim pso As New PromptSelectionOptions
        pso.SingleOnly = True
        pso.MessageForAdding = "Select Line"
        Dim psr As PromptSelectionResult = ed.GetSelection(pso, New SelectionFilter(New TypedValue() {New TypedValue(0, "LINE")}))
        If psr.Status <> PromptStatus.OK Then
            ed.WriteMessage(vbCrLf & "No line selected")
            Exit Sub
        End If

        ' Add line to collection
        ids.Add(psr.Value.GetObjectIds(0))
        ed.WriteMessage(vbLf & "Document List has {0} IDs", ids.Count)
    End Sub
End Class

 

Message 9 of 13
_gile
in reply to: MS7000

Sorry, I do not understand your purpose.

As your SelLine command method is the only way to modify the ObjectId collection, it seems to me you do not need to create a new instance of Class1 at initialization (i.e. before the SelLine command is called), a new one will implicitly be created at the first call of the command (in each document).

 

The following code works for me (note the constructor can be used to initialize the fields).

 

    public class Class1
    {
        private ObjectIdCollection ids;
        private Document doc;
        private Editor ed;

        public Class1()
        {
            ids = new ObjectIdCollection();
            doc = Application.DocumentManager.MdiActiveDocument;
            doc.Database.ObjectModified += onObjectModified;
            ed = doc.Editor;
            ed.WriteMessage("\nNew instance of Class1");
        }

        void onObjectModified(object sender, ObjectEventArgs e)
        {
            ed.WriteMessage("\nModify - Document List has {0} IDs", ids.Count);
        }

        [CommandMethod("SelLine", CommandFlags.Modal)]
        public void SelLine()
        {
            PromptEntityOptions peo = new PromptEntityOptions("\nSelect Line: ");
            peo.SetRejectMessage("Only Line.");
            peo.AddAllowedClass(typeof(Line), true);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\nNo line selected");
                return;
            }
            ids.Add(per.ObjectId);
            ed.WriteMessage("\nDocument List has {0} IDs", ids.Count);
        }
    }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 10 of 13
MS7000
in reply to: _gile

My last code sample is very reduced to the minimum. I tried to reduce it as good as possible.

l In reality I store my data (it is more than a objectidcollection) in a dictionary.

The stored data should be available just after the assembly has loaded for some reactors (e.g. ObjectModified). But the same date should also be available for some commands. The reactors and the commands should have access to the same data.

@Gilles: In your example the Class1 instance is not initialized until the command is called, and the reactor is not working.

 

Message 11 of 13
_gile
in reply to: MS7000


MS7000 a écrit :

@Gilles: In your example the Class1 instance is not initialized until the command is called, and the reactor is not working.

 


I said in my reply, due to the little I knew about your purpose, that it seems to me the initialization was unusefull.

 


MS7000 a écrit :

My last code sample is very reduced to the minimum. I tried to reduce it as good as possible.

l In reality I store my data (it is more than a objectidcollection) in a dictionary.

The stored data should be available just after the assembly has loaded for some reactors (e.g. ObjectModified). But the same date should also be available for some commands. The reactors and the commands should have access to the same data.

 


If your objectIdS are stored in a Dictionary/Xrecord, why do you need to store them again in an instance member ?

You'd directly read/write the Xrecord when needed.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 12 of 13
MS7000
in reply to: _gile


@_gile wrote:

If your objectIdS are stored in a Dictionary/Xrecord, why do you need to store them again in an instance member ?

You'd directly read/write the Xrecord when needed.


I am not sure if I understand you right. I think it is not possible to read the dictionary directly. I think this would be a performance killer, as my reactors react e.g. for any object modification. If I would imagine that I e.g. move 1.000 entities the reactor would fire about 4.000 times for table and object modification.

I think I need to have my data in memory. I would like to read the data during initialization and write it during drawing save.

Message 13 of 13
_gile
in reply to: MS7000


MS7000 a écrit :

I am not sure if I understand you right. I think it is not possible to read the dictionary directly. I think this would be a performance killer, as my reactors react e.g. for any object modification. If I would imagine that I e.g. move 1.000 entities the reactor would fire about 4.000 times for table and object modification.

I think I need to have my data in memory. I would like to read the data during initialization and write it during drawing save.


IMO, the issue is more about handling events firing so often as ObjectModified than about reading a Dictionary...

 

Anyway, this works for me.

Initialization only calls the "initialize" command and handles the DocumentCreated event to do the same for each newly created document.

The "initialize" command does nothing. It's only called to implicitly create an instance of the Commands class for the active document.

The Commands class constructor does all the trick : initialization of instance members, ObjectModified event handling.

 

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace DataStorage
{
    public class Initilize : IExtensionApplication
    {
        DocumentCollection docs = AcAp.DocumentManager;

        public void Initialize()
        {
            docs.DocumentCreated += onDocumentCreated;
            AcAp.Idle += onIdle;
        }
public void Terminate() { }
void onIdle(object sender, EventArgs e) {
AcAp.Idle -= onIdle; docs.MdiActiveDocument.SendStringToExecute("initialize ", false, false, true); } void onDocumentCreated(object sender, DocumentCollectionEventArgs e) { AcAp.Idle += onIdle; } } public class Commands { ObjectIdCollection ids; Document doc; Database db; Editor ed; public Commands() { ids = new ObjectIdCollection(); doc = AcAp.DocumentManager.MdiActiveDocument; db = doc.Database; ed = doc.Editor; db.ObjectModified += onObjectModified; } [CommandMethod("initialize")] public void Intialize() { } [CommandMethod("SelLine")] public void SelLine() { PromptEntityOptions peo = new PromptEntityOptions("\nSelect Line: "); peo.SetRejectMessage("Only a Line."); peo.AddAllowedClass(typeof(Line), true); PromptEntityResult per = ed.GetEntity(peo); if (per.Status == PromptStatus.OK) ids.Add(per.ObjectId); ed.WriteMessage("\nDocument List has {0} IDs", ids.Count); } void onObjectModified(object sender, ObjectEventArgs e) { ed.WriteMessage("\nModify - Document List has {0} IDs", ids.Count); } } }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

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