TheSwamp
Code Red => .NET => Topic started by: BillZndl on September 23, 2009, 04:53:07 PM
-
Hello,
This is my first post here at the swamp.
Some of you already know me from the Autodesk discussion groups.
I have about 20 years AutoCAD experience and almost that long at AutoLisp and more lately, Vlisp.
Been playing around with C# for a couple of years but more seriously this last year.
I'm pretty much self taught and have become the head of our CAD dept mostly because I've outlasted everybody else.
My platform is pretty outdated as we are using AcadR16 and while I have WinXP there are still some on Win2K.
This limits me to VS2005 ex and net 2.0. This may change as time goes on.
Right now I have basically one Program in C#, it's a net loadable .dll that displays a modeless form.
It's loaded at startup and stays up all day as my close is overridden to just Visibility status.
When a drawing is opened, the programs fills a listview with the names of existing part groups in the drawing.
If we make a new group, consisiting of lines, circles & arcs, the program adds the current date into the description field.
The listview is single select and the highlighted item causes the program to look for a matching part file of same name.
If found it displays the last date modified of the file and displays that and the dwgpreview bitmap as part of the listview item.
There are other features I haven't mentioned related to finding groups, copying etc.
I would really like to make it so that when you open a drawing in Autocad, the program can find the last group made in the dwg
and show that as the selected item in the listview. We had another program like this that used blocks and that was easy
because blocks are in the order they're made in the block table, but groups seem to be sorted by name in the dictionary that owns them.
I've thought about p/invokeing acdbEntLast() but not sure if that would work.
Any ideas on how to find the last group created in a drawing?
-
...because blocks are in the order they're made in the block table...
Hmmm...that's news to me. Blocks appear in the block table according to name ie. alphabetical as far as I'm aware...
Having said that, unless you tag a GROUP with creation date/time, I know of no way to find the last one created.
-
...because blocks are in the order they're made in the block table...
Hmmm...that's news to me. Blocks appear in the block table according to name ie. alphabetical as far as I'm aware...
I shouldn't say "in order of creation" because I haven't checked that theory out completely
but when I iterate the blocktable collection,
the blocktablerecords certainly are not "sorted" (at least not by name) when I print them to the screen:
BlkNam: 10700
BlkNam: 10693
BlkNam: 10627-
BlkNam: 10765
BlkNam: 10766
BlkNam: 86172
BlkNam: 10608
BlkNam: 10088
BlkNam: 10759
BlkNam: 10761
BlkNam: 10760
BlkNam: 10758
BlkNam: 10776
BlkNam: 10777
BlkNam: 10762
BlkNam: 10751
BlkNam: 10736
BlkNam: 10626-
BlkNam: 10755-
BlkNam: 10833
BlkNam: 10832
BlkNam: uph
BlkNam: TAG
BlkNam: ASM
BlkNam: 10614-10
BlkNam: 10777-10
Having said that, unless you tag a GROUP with creation date/time, I know of no way to find the last one created.
Hmm, bummer.
Oh well, probably more work than it's worth,
the users wouldn't necessarily be working on the last group created anyway,
just an idea.
Thanks!
-
welcome to the swamp
-
You can always just watch for whenever the user creates a new group, then store that name somewhere, such as in the Named Object Dictionary.
-
You can always just watch for whenever the user creates a new group, then store that name somewhere, such as in the Named Object Dictionary.
That would probably be the easiest.
I already watch for new groups so the date created gets added to the description field.
I suppose If I added a "time" to that field..... be nice if there was another "Tag" field for GROUPS.
I'm up to my ears in alligators right now (no pun intended) so I'll have to look into this more when time permits.
Again, this is my "spare time" project but I'll be checking in on this post now & then for more input.
Thanks to all for the warm welcome! :-D
-
Another question, same app.
I have a button that, "onClick", opens the native AutoCAD "Group:" dialog box using a static method.
private void button6_Click(object sender, EventArgs e)
{
if (Autodesk.AutoCAD.ApplicationServices.Application.IsQuiescent)
{
base.Enabled = false;
OpenGroupCommandDialog.CallGroupCommand();
}
}
As you can see, I am using base.Enabled = false;
this is to prevent the user from interaction with the modeless form,
until they do whatever with the group command dialog.
In my CommandEnded, CommandCancelled and CommandFailed handlers,
I use base.Enabled = true; to bring the modeless form back to life.
void document_CommandEnded(object sender, CommandEventArgs e)
{
base.Enabled = true;
}
For some reason, if I open the group dialog, then just rename a group and hit okay,
the group dialog closes but the main modeless form remains dead.
Is there a better way to prevent working with the modeless form
while another dialog is up or if another command is running?
TIA
Bill
-
I uploaded an image of my app, thought it might help others visualize what I'm talking about.
(http://www.theswamp.org/screens/index.php?dir=&file=PartMonitor.png)
-
Hmm, well wouldn't you know,
This morning, everything works fine.
I am no longer getting the "lock up" behavior when I use rename inside the group dialog. 8-)
Also I added base.Enabled = true; to the drawing activated handlers in case it happens again,
opening another drawing or reopening the current drawing will reset the form.
I don't like that for a solution but it's better than being "locked up" for the session.
Still undecided on what/if to do for last group in drawing
but everything in this project is working about the way we originally set goals for it,
so we're good to go (until I think of something else :roll: ).
Thanks all.
-
Bill -
Did you catch Tony T's post on a.a.c.dotnet 9/30?
-
Bill -
Did you catch Tony T's post on a.a.c.dotnet 9/30?
Yes, my only trouble is, I am still on net 2.0 and don't have "using System.Linq;" in my resources,
so I can't test until something else happens around here. :(
-
Wait, I found System.Linq under the System core dll.
Stay turned.
-
Nope, need VS2008.
You can target 2.0 but you have to add an extension attribute for the compiler.
I could try using VS2008 with a 2.0 target and see if it will run on Win2000 machines.
I'll see if I get time. :|
-
It's just kind of a hack but it seems to work.
Just using the default comparer for the sort.
Needs more testing:
public string GetLastHandle (ObjectIdCollection ids)
{
// return the element with the numerically largest handle: ??
List<string> list = new List<string>();
foreach (ObjectId id in ids)
{
list.Add(id.Handle.ToString());
}
list.Sort();
return list[list.Count - 1];
}
-
It's just kind of a hack but it seems to work.
Just using the default comparer for the sort.
Needs more testing:
Nope, didn't work.
Was sorting alpha numerically.
Ended up using a sorted list with the handle Value as the key for sorting.
Limited testing shows success.
FWIW:
public string GetLastHandle (ObjectIdCollection ids)
{
// return the element with the numerically largest handle:
SortedList solst = new SortedList();
foreach (ObjectId id in ids)
{
solst.Add(id.Handle.Value, id.Handle);
}
IList Vals = solst.GetValueList();
return Vals[Vals.Count - 1].ToString();
}
-
For some reason, if I open the group dialog, then just rename a group and hit okay,
the group dialog closes but the main modeless form remains dead.
Is there a better way to prevent working with the modeless form
while another dialog is up or if another command is running?
The better way is to use the finally statement :
private void button6_Click(object sender, EventArgs e)
{
try
{
if (Autodesk.AutoCAD.ApplicationServices.Application.IsQuiescent)
{
base.Enabled = false;
OpenGroupCommandDialog.CallGroupCommand();
}
}
catch (SytemException exception)
{
Message.Show{ blah blah blah};
}
finally
{
base.Enabled = true;
}
}
The finally statement is executed no matter what.
:-)
-
try this and see if it works :laugh:
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using AcEd = Autodesk.AutoCAD.EditorInput;
[assembly: CommandClass(typeof(ExecMethod.Commands))]
namespace ExecMethod
{
public class Commands
{
[CommandMethod("lastgroup")]
static public void lastgroup()
{
try
{
AcAp.Document activeDocument = AcAp.Application.DocumentManager.MdiActiveDocument;
AcEd.Editor editor = activeDocument.Editor;
AcDb.Database database = HostApplicationServices.WorkingDatabase;
AcDb.TransactionManager manager = activeDocument.TransactionManager;
List<Int64> handleVals = new List<Int64>();
using(Transaction transaction = manager.StartTransaction())
{
AcDb.DBDictionary groupDictionary =
transaction.GetObject(database.GroupDictionaryId, OpenMode.ForRead)
as AcDb.DBDictionary;
foreach(DBDictionaryEntry item in groupDictionary)
{
AcDb.Group group =
transaction.GetObject(item.Value, OpenMode.ForRead)
as AcDb.Group;
if (group.NumEntities > 0)
handleVals.Add(item.Value.Handle.Value);
}
if(handleVals.Count > 0)
{
handleVals.Sort();
handleVals.Reverse();
AcDb.Group lastgroup =
transaction.GetObject(database.GetObjectId
(false, new Handle(handleVals[0]), 0), OpenMode.ForRead)
as AcDb.Group;
editor.WriteMessage(lastgroup.Name);
}
}
}
catch (System.Exception ex)
{
AcAp.Application.DocumentManager.
MdiActiveDocument.Editor.WriteMessage
("\n{0}\n{1}", ex.Message, ex.StackTrace);
}
}
}
}
-
Thanks, but no it doesn't work.
I keep getting "DBDictionaryEntry could not be found" error.
Probably because I'm using VS2005 ex and net 2.0?
-
try this and see if it works :laugh:
Okay, now that I take a better look at it, this does simplify what I was doing.
I never would've thought to sort the handle values directly, then get the objectId of the group from the value. :-o
This also eliminates the sorted list, which I understand is the same as using 2 arrays, so I'll assume for now that the List<int64> is a more efficient way to handle things, not to mention the reverse method which the sorted list doesn't have.
I chose to not check the group for number of entities as I list them even if empty
and to obtain the Object id using the GetAt method (don't know if it's better than opening for read or not, I'm thinking it is ^-^).
The DBDictionaryEntry = DictionaryEntry on my platform.
Oh, one question, why do I see some using:
Group grp = (Group)tr.GetObject(db.GetObjectId(false, new Handle(handleVals[0]), 0), OpenMode.ForRead) as Group;
When it works without using the "as Group":
Group grp = (Group)tr.GetObject(db.GetObjectId(false, new Handle(handleVals[0]), 0), OpenMode.ForRead);
Thanks for the tips!
Here's what I end up with, any critique appreciated, just don't get too carried away! 8-)
using System;
using System.Collections;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace PartGroupsMonitor
{
public class LastGroupInDrawing
{
public string FindLastGroup()
{
Document doc = AcadApp.DocumentManager.MdiActiveDocument;
Database db = HostApplicationServices.WorkingDatabase;
List<Int64> handleVals = new List<Int64>();
using (DocumentLock dloc = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary groups = (DBDictionary)tr.GetObject(db.GroupDictionaryId, OpenMode.ForRead);
ObjectIdCollection objIDs = new ObjectIdCollection();
foreach (DictionaryEntry DicEnt in groups)
{
ObjectId Did = groups.GetAt(DicEnt.Key.ToString());
handleVals.Add(Did.Handle.Value);
}
if (handleVals.Count > 0)
{
handleVals.Sort();
handleVals.Reverse();
Group grp = (Group)tr.GetObject(db.GetObjectId(false, new Handle(handleVals[0]), 0), OpenMode.ForRead);
return grp.Name;
}
else
{
return "No";
}
}
}
}
}
}
-
it's not a Visual Studio issue or a .NET issue, it's a managed library issue.
this should work with Acad05
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.LayerManager;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
using AcAp = Autodesk.AutoCAD.ApplicationServices;
using AcGe = Autodesk.AutoCAD.Geometry;
using AcRx = Autodesk.AutoCAD.Runtime;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
[assembly: CommandClass(typeof(ExecMethod.Commands))]
namespace ExecMethod
{
public class Commands
{
[CommandMethod("lastgroup")]
static public void lastgroup()
{
try
{
AcAp.Document activeDocument = AcAp.Application.DocumentManager.MdiActiveDocument;
AcDb.Database database = HostApplicationServices.WorkingDatabase;
AcDb.TransactionManager manager = activeDocument.TransactionManager;
List<Int64> handleVals = new List<Int64>();
using (Transaction transaction = manager.StartTransaction())
{
AcDb.DBDictionary groupDictionary =
transaction.GetObject(database.GroupDictionaryId, OpenMode.ForRead)
as AcDb.DBDictionary;
foreach (System.Collections.DictionaryEntry item in groupDictionary)
{
ObjectId id = (ObjectId)item.Value;
AcDb.Group group =
transaction.GetObject(id, OpenMode.ForRead)
as AcDb.Group;
if (group.NumEntities > 0)
handleVals.Add(id.Handle.Value);
}
if (handleVals.Count > 0)
{
handleVals.Sort();
handleVals.Reverse();
Handle hnd = new Handle();
hnd.Value = handleVals[0];
AcDb.Group lastgroup =
transaction.GetObject(database.GetObjectId
(false, hnd, 0), OpenMode.ForRead)
as AcDb.Group;
AcAp.CommandLinePrompts.Message(lastgroup.Name);
}
}
}
catch (System.Exception ex)
{
AcAp.CommandLinePrompts.Message
(String.Format("\n{0}\n{1}", ex.Message, ex.StackTrace));
}
}
}
}
-
oops, I see you got it :-)