TheSwamp
Code Red => .NET => Topic started by: Viktor on October 21, 2009, 01:56:00 PM
-
I have a need in one of my apps to let the user pick layouts out of a drawing. Normally, I have a listbox with a list of files in a selected directory, and if a user checks a mutli-tab checkbox, then I want to update the list with all the filesnames and layouts.
Anyway, I've written a simple GetLayouts function to go through the btrs and check if its a layout then retrieve the name and return an array of names. This works great if there's 5 files, but if I have 50 this may take upto 20 seconds... The most consuming part of the code of course is this:
database.ReadDwgFile(filepath, FileOpenMode.OpenForReadAndAllShare, False, "")
So is there any other quicker way to get layouts?
Thanks,
Viktor.
-
Hi,
You can iterate the "ACAD_LAYOUT" dictionary.
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary NOD = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
DBDictionary layouts = (DBDictionary)tr.GetObject(NOD.GetAt("ACAD_LAYOUT"), OpenMode.ForRead);
foreach (DBDictionaryEntry entry in layouts)
{
string name = entry.Key;
...
}
}
-
Viktor,
You need to specify at the least, the following information for more targeted help from members:
1. AutoCAD version
2. .NET framework version
3. Operating system and service packs
4. Visual Studio version
5. 32 or 64 bit for all the above for good measure
Also, a small example project that members can test with would be very beneficial as well.
-
Viktor,
You need to specify at the least, the following information for more targeted help from members:
1. AutoCAD version
2. .NET framework version
3. Operating system and service packs
4. Visual Studio version
5. 32 or 64 bit for all the above for good measure
Also, a small example project that members can test with would be very beneficial as well.
My apology Glenn, it was more of a general question, here's more info:
1. AutoCad 2010
2. .net 3.5
4. XP
4. VS2008 express
5. 32bit
6. VB.net
Here's the function I use to get the layouts:
Public Function GetLayouts(ByVal filepath As String) As ArrayList
Dim database As New Database(False, True)
Dim list As New ArrayList
Dim transaction As Transaction = database.TransactionManager.StartTransaction
Using database
Try
database.ReadDwgFile(filepath, FileOpenMode.OpenForReadAndAllShare, True, Nothing)
Dim enumerator As SymbolTableEnumerator = DirectCast(database.BlockTableId.GetObject(OpenMode.ForRead), BlockTable).GetEnumerator
Do While enumerator.MoveNext
Dim record As BlockTableRecord = DirectCast(enumerator.Current.GetObject(OpenMode.ForRead), BlockTableRecord)
If record.IsLayout Then
Dim layout As Layout = DirectCast(record.LayoutId.GetObject(OpenMode.ForRead), Layout)
If (layout.LayoutName <> "Model") Then
list.Add((layout.LayoutName))
End If
End If
Loop
Catch exception1 As Exception
transaction.Abort()
Return Nothing
Finally
transaction.Dispose()
End Try
End Using
Return list
End Function
This works, but the fact that I have to read the database of each drawing in my list takes quiet a bit of time. For example, if i have 30 drawings that are 2-5mb each with 3-5 layouts in each, this can take upto 30 seconds to scan for layouts. Like I said, the most consuming part of this code is opening the database.
I'd guess there's no other way without using objectarx, but thought i'd ask either way.
-
heh...forgot to add 6. Language - thanks.
In ObjectARX, you would still have to 'read' the dwg dbase as well, so that point is moot.
You could also do this:
[CommandMethod("MyCommandLocal", CommandFlags.Modal)]
public void MyCommand() // This method can have any name
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary layoutDict = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead, false) as DBDictionary;
foreach (DBDictionaryEntry dictEnt in layoutDict)
{
//Layout layout = tr.GetObject(dictEnt.Value, OpenMode.ForRead, false) as Layout;
//ed.WriteMessage("{0}Layout name: {1}", Environment.NewLine, layout.LayoutName);
ed.WriteMessage("{0}Layout name: {1}", Environment.NewLine, dictEnt.Key);
}
}
}
Also, why are you using GetEnumerator?
-
heh...forgot to add 6. Language - thanks.
In ObjectARX, you would still have to 'read' the dwg dbase as well, so that point is moot.
You could also do this:
[CommandMethod("MyCommandLocal", CommandFlags.Modal)]
public void MyCommand() // This method can have any name
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary layoutDict = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead, false) as DBDictionary;
foreach (DBDictionaryEntry dictEnt in layoutDict)
{
//Layout layout = tr.GetObject(dictEnt.Value, OpenMode.ForRead, false) as Layout;
//ed.WriteMessage("{0}Layout name: {1}", Environment.NewLine, layout.LayoutName);
ed.WriteMessage("{0}Layout name: {1}", Environment.NewLine, dictEnt.Key);
}
}
}
Also, why are you using GetEnumerator?
Thankgs Glenn, yea i was feeling hopeless either way but thought of asking. In the sample you provided you're getting the layouts of the current drawing if i'm not mistaken, I don't want to open drawings cause that would take another 20 minutes.
As far as enumerator goes, i actually copied this from a vb.net book i use, but to be honest with you i rarely use the enumerator other than where I copied it. I may not be doing it any better way though, i just loop through the id's in a for each loop once i find what i need i exit for.
What's the best way?
I also signed up for the linq class at AU this year, i love linq for everything outside of autocad db, so it'd be interesting to see how i can use it in autocad db. Do you linq?
-
In the sample you provided you're getting the layouts of the current drawing if i'm not mistaken, I don't want to open drawings cause that would take another 20 minutes.
Yes I am, but it's a quick example. You will see that I'm getting the layouts from the DATABASE of the current document...it's still a databse, the same as you're constructing and using in ReadDwgFile.
Also, you're iterating the whole block table to find the layouts, which in dwgs of 2-5mb could be large, whereas I'm going straight to the layouts dictionary, similar to what gile posted above, which, all things being equal, should be faster.
Try it.
-
Something like this ?
public ArrayList GetLayouts(string filename)
{
ArrayList lst = new ArrayList();
using (Database db = new Database(false, true))
{
try
{
db.ReadDwgFile(filename, System.IO.FileShare.None, false, string.Empty);
using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary NOD = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
DBDictionary layouts = (DBDictionary)tr.GetObject(NOD.GetAt("ACAD_LAYOUT"), OpenMode.ForRead);
foreach (DBDictionaryEntry entry in layouts)
{
string name = entry.Key;
if (name == "Model") continue;
lst.Add(name);
}
tr.Commit();
}
return lst;
}
catch
{
return null;
}
}
}
-
I don't think FileShare.None is correct; from memory this will block anybody from using the file...it's the opposite to what I always think it is ;)
Also gile, you're going for the NOD, whereas what I posted, bypasses that, but other than that, yes.
-
In the sample you provided you're getting the layouts of the current drawing if i'm not mistaken, I don't want to open drawings cause that would take another 20 minutes.
Yes I am, but it's a quick example. You will see that I'm getting the layouts from the DATABASE of the current document...it's still a databse, the same as you're constructing and using in ReadDwgFile.
Also, you're iterating the whole block table to find the layouts, which in dwgs of 2-5mb could be large, whereas I'm going straight to the layouts dictionary, similar to what gile posted above, which, all things being equal, should be faster.
Try it.
No doubt its faster the way you have it, will be using your approach there. But to eliminate the reading of the database I am changing the architecture of the app. Instead of reloading the list with filenames/layouts I instead load on demand when the user actually clicks on the file, then I get all the data from all the layouts and allow the user to shuffle between sheets at that point. So i'm moving the delay to a place where its less noticable.
Anyway, thanks everyone for their input.
-
Thanks for the corrections.
I didn't saw "db.LayoutDictionaryId" before (so many things to learn...)
-
Viktor,
I was going to suggest a lazy loading approach, but try what I suggested first and then make the decision.
Gile,
You're wlecome.
-
[.........?
I also signed up for the linq class at AU this year, i love linq for everything outside of autocad db, so it'd be interesting to see how i can use it in autocad db. Do you linq?
Who's teaching the class Viktor ??
-
[.........?
I also signed up for the linq class at AU this year, i love linq for everything outside of autocad db, so it'd be interesting to see how i can use it in autocad db. Do you linq?
Who's teaching the class Viktor ??
Albert Szilvasy
Using .NET 4.0 with AutoCADŽ
http://au.autodesk.com/?nd=e_class&session_id=5164
-
How many of you guys going to AU .net classes?
btw, who's using linq in autocad db so far?