LINQ and the AutoCAD .NET API (Part 4)
From tables to dictionaries
This is the fourth in a series of posts on LINQ an the AutoCAD .NET API. Here's a complete list of posts in this series.
Introduction
In part three of our series on LINQ and AutoCAD we created a neat way to access all tables of the drawing database. In this post we'll have a look at how to add AutoCAD dictionaries to our GeneralHelper
class.
Dictionaries
Which dictionaries of interest do we have in the drawing database? Figuring this out is almost the same like for tables: the database object has some properties that end with DictionaryId
. And again, like for tables, there is one element type for each dictionary. Let's bring it all together in tabular form:
ID property | Dictionary entry type |
---|---|
ColorDictionaryId | DBObject |
DataLinkDictionaryId | DataLink |
DetailViewStyleDictionaryId | DetailViewStyle |
GroupDictionaryId | Group |
LayoutDictionaryId | Layout |
MaterialDictionaryId | Material |
MLeaderStyleDictionaryId | MLeaderStyle |
MLStyleDictionaryId | MlineStyle |
NamedObjectsDictionaryId | DBObject |
PlotSettingsDictionaryId | PlotSettings |
PlotStyleNameDictionaryId | PlaceHolder |
SectionViewStyleDictionaryId | SectionViewStyle |
TableStyleDictionaryId | TableStyle |
VisualStyleDictionaryId | DBVisualStyle |
A few comments:
- I couldn't figure out the data type of entries of the color dictionary. Let's stick with
DbObject
for the moment. - The named objects dictionary is kind of a "meta" container. It holds all dictionaries of this list and some other objects. So from a Add-In developer's perspective, this dictionary is not really of interest, so it's safe to leave it out.
- The plot style names dictionary simply contains the plot style names, so there's no need to use
GetDictItems
, but rather we return the dictionary keys, which are the plot style names (we'll see that in listing 3).
Implementation
Alright, on to the implementation! First let's recap how we get records from AutoCAD tables:
private IEnumerable<T> GetItems<T>(ObjectId tableID) where T : SymbolTableRecord
{
var table = (IEnumerable)tr.GetObject(tableID, OpenMode.ForRead);
foreach (ObjectId id in table)
{
yield return (T)tr.GetObject(id, OpenMode.ForRead);
}
}
Listing 1: GetItems
That was easy. Getting dictionary entries is quite similar:
private IEnumerable<T> GetDictItems<T>(ObjectId dictID) where T : DBObject
{
if (dictID.IsValid)
{
var dict = (IEnumerable)tr.GetObject(dictID, OpenMode.ForRead);
foreach (DBDictionaryEntry entry in dict)
{
yield return (T)tr.GetObject((ObjectId)entry.Value, OpenMode.ForRead);
}
}
else
{
return new T[0];
}
}
Listing 2: GetDictItems
At first we get the IEnumerable
to iterate the IDs. But here's the difference between dictionaries and tables: each entry of the dictionary's iterator is a DBDictionaryEntry
, which holds the object's name as the key, and the ObjectId
as the value. So we have to cast the value to ObjectId
. And to be on the safe side we first check wheather the dictionary ID is valid at all (line 4).
Now we can add the GetDictItems()
method to our GeneralHelper
class. To not confuse the two getter methods, we rename GetItems()
- which returns records from a table - to GetTableItems()
. Finally, for each dictionary in our list above (except the named objects dictionary), we add a property that calls GetDictItems()
to the GeneralHelper
.
And so our GeneralHelper
finally looks like this:
public class GeneralHelper : IDisposable
{
private readonly Database db;
private readonly Transaction tr;
public GeneralHelper()
: this(Application.DocumentManager.MdiActiveDocument.Database,
db.TransactionManager.StartTransaction())
{
}
public GeneralHelper(Database db, Transaction tr)
{
this.db = db;
this.tr = tr;
}
public void Dispose()
{
if (tr != null && !tr.IsDisposed)
{
tr.Dispose();
}
}
private IEnumerable<T> GetTableItems<T>(ObjectId tableID) where T : SymbolTableRecord
{
if (tableID.IsValid)
{
var table = (IEnumerable)tr.GetObject(tableID, OpenMode.ForRead);
foreach (ObjectId id in table)
{
yield return (T)tr.GetObject(id, OpenMode.ForRead);
}
}
else
{
yield break;
}
}
private IEnumerable<T> GetDictItems<T>(ObjectId dictID) where T : DBObject
{
if (dictID.IsValid)
{
var dict = (IEnumerable)tr.GetObject(dictID, OpenMode.ForRead);
foreach (DBDictionaryEntry entry in dict)
{
yield return (T)tr.GetObject((ObjectId)entry.Value, OpenMode.ForRead);
}
}
else
{
yield break;
}
}
#region Tables
public IEnumerable<BlockTableRecord> Blocks
{
get { return GetTableItems<BlockTableRecord>(db.BlockTableId); }
}
public IEnumerable<DimStyleTableRecord> DimStyles
{
get { return GetTableItems<DimStyleTableRecord>(db.DimStyleTableId); }
}
public IEnumerable<LayerTableRecord> Layers
{
get { return GetTableItems<LayerTableRecord>(db.LayerTableId); }
}
public IEnumerable<LinetypeTableRecord> Linetypes
{
get { return GetTableItems<LinetypeTableRecord>(db.LinetypeTableId); }
}
public IEnumerable<RegAppTableRecord> RegApps
{
get { return GetTableItems<RegAppTableRecord>(db.RegAppTableId); }
}
public IEnumerable<TextStyleTableRecord> TextStyles
{
get { return GetTableItems<TextStyleTableRecord>(db.TextStyleTableId); }
}
public IEnumerable<UcsTableRecord> Ucss
{
get { return GetTableItems<UcsTableRecord>(db.UcsTableId); }
}
public IEnumerable<ViewportTableRecord> Viewports
{
get { return GetTableItems<ViewportTableRecord>(db.ViewportTableId); }
}
public IEnumerable<ViewTableRecord> Views
{
get { return GetTableItems<ViewTableRecord>(db.ViewTableId); }
}
#endregion
#region Dictionaries
public IEnumerable<DBObject> Colors
{
get { return GetDictItems<DBObject>(db.ColorDictionaryId); }
}
public IEnumerable<DataLink> DataLinks
{
get { return GetDictItems<DataLink>(db.DataLinkDictionaryId); }
}
public IEnumerable<DetailViewStyle> DetailViewStyles
{
get { return GetDictItems<DetailViewStyle>(db.DetailViewStyleDictionaryId); }
}
public IEnumerable<Group> Groups
{
get { return GetDictItems<Group>(db.GroupDictionaryId); }
}
public IEnumerable<Layout> Layouts
{
get { return GetDictItems<Layout>(db.LayoutDictionaryId); }
}
public IEnumerable<Material> Materials
{
get { return GetDictItems<Material>(db.MaterialDictionaryId); }
}
public IEnumerable<MLeaderStyle> MLeaderStyles
{
get { return GetDictItems<MLeaderStyle>(db.MLeaderStyleDictionary); }
}
public IEnumerable<MLeaderStyle> MLeaderStyles
{
get { return GetDictItems<MLeaderStyle>(db.MLeaderStyleDictionaryId); }
}
public IEnumerable<MlineStyle> MlineStyles
{
get { return GetDictItems<MlineStyle>(db.MLStyleDictionaryId); }
}
public IEnumerable<PlotSettings> PlotSettings
{
get { return GetDictItems<PlotSettings>(db.PlotSettingsDictionaryId); }
}
public IEnumerable<string> PlotStyleNames
{
get
{
var dict = (IEnumerable)tr.GetObject(db.PlotStyleNameDictionaryId, OpenMode.ForRead);
foreach (DBDictionaryEntry entry in dict)
{
yield return (string)entry.Key;
}
}
}
public IEnumerable<SectionViewStyle> SectionViewStyles
{
get { return GetDictItems<SectionViewStyle>(db.SectionViewStyleDictionaryId); }
}
public IEnumerable<TableStyle> TableStyles
{
get { return GetDictItems<TableStyle>(db.TableStyleDictionaryId); }
}
public IEnumerable<DBVisualStyle> VisualStyles
{
get { return GetDictItems<DBVisualStyle>(db.VisualStyleDictionaryId); }
}
#endregion
}
Listing 3: GeneralHelper
The power of abstraction
The great thing about the GeneralHelper
class is that it abstracts away all details about how and where items are stored in the drawing database. The class provides one uniform way to access tables and dictionaries, no knowledge about details necessary. From the client's perspective, each "container" is of the most general container type available in .NET: IEnumerable<T>
. And this gives us all the benefits of LINQ out of the box.
In the next post we'll have a look at some special object of the AutoCAD API, like the model space and paper space. We integrate these objects into the GeneralHelper
, to make them easily querable using LINQ.