Base class to simplify working with blocks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I thought I would share a base class I've been working with and refining that I think simplifies working with Blocks in AutoCAD.
Here is the code, and I'll kind of go over some parts
public abstract class AppBlock<T> where T : AppBlock<T>, new()
{
public abstract string Name { get; }
private Dictionary<string, AttributeReference> AttributeDictionary { get; } = new();
public BlockReference? Block { get; set; }
public Transaction? Transaction { get; set; }
public Point3d? Position => Block?.Position;
protected string? GetAttributeValue(string tag)
{
string? value = null;
if(AttributeDictionary.TryGetValue(tag, out var attRef))
{
return attRef?.TextString;
}
else if(Block is not null)
{
foreach (ObjectId id in Block.AttributeCollection)
{
AttributeReference? att = Transaction?.GetObject(id, OpenMode.ForRead) as AttributeReference;
if(att is not null) AttributeDictionary.TryAdd(att.Tag, att);
if (att.Tag.Equals(tag))
{
value = att.TextString;
break;
}
}
}
return value;
}
protected bool SetAttributeValue(string tag, string? value)
{
if (value is null) return false;
if (AttributeDictionary.TryGetValue(tag, out var attRef))
{
if(!attRef.IsWriteEnabled) attRef.UpgradeOpen();
attRef.TextString = value;
return true;
}
else if (Block is not null)
{
foreach (ObjectId id in Block.AttributeCollection)
{
AttributeReference? att = Transaction?.GetObject(id, OpenMode.ForRead) as AttributeReference;
if (att is not null) AttributeDictionary.TryAdd(att.Tag, att);
if (att.Tag.Equals(tag))
{
if(!att.IsWriteEnabled) att.UpgradeOpen();
att.TextString = value;
return true;
}
}
}
return false;
}
protected object? GetPropertyValue(string propName)
{
if (!Block.IsDynamicBlock) return null;
foreach (DynamicBlockReferenceProperty property in Block.DynamicBlockReferencePropertyCollection)
{
if(!property.PropertyName.Equals(propName, StringComparison.CurrentCultureIgnoreCase)) continue;
return property.Value;
}
return null;
}
protected bool SetPropertyValue(string propName, object value)
{
if (!Block.IsDynamicBlock) return false;
foreach (DynamicBlockReferenceProperty property in Block.DynamicBlockReferencePropertyCollection)
{
if (!property.PropertyName.Equals(propName, StringComparison.CurrentCultureIgnoreCase)) continue;
if(!Block.IsWriteEnabled) Block.UpgradeOpen();
property.Value = value;
return true;
}
return false;
}
protected string? GetAttributeDefinitionValue(string tag)
{
string? value = null;
if (Block is not null)
{
BlockTableRecord? btr = Transaction.GetObject(Block.DynamicBlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
if (btr is null) return null;
if (!btr.HasAttributeDefinitions) return null;
btr.ForEach<AttributeDefinition>(Transaction, attDef =>
{
if(!attDef.Tag.Equals(tag)) return;
value = attDef.TextString;
});
}
return value;
}
protected bool SetAttributeDefinitionValue(string tag, string? value)
{
if (value is null) return false;
bool success = false;
if (Block is not null)
{
BlockTableRecord? btr = Transaction.GetObject(Block.DynamicBlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
if (btr is null) return success;
if (!btr.HasAttributeDefinitions) return success;
btr.ForEach<AttributeDefinition>(Transaction, attDef =>
{
if (!attDef.Tag.Equals(tag)) return;
value = attDef.TextString;
success = true;
});
}
return success;
}
protected Point3d? GetVisiblePointProperty(string name)
{
var pointProp = Block
.DynamicBlockReferencePropertyCollection
.Cast<DynamicBlockReferenceProperty>()
.Where(p => p.VisibleInCurrentVisibilityState)
.Where(p => p.PropertyTypeCode == 1)
.Where(p => p.PropertyName.StartsWith(name))
.ToList();
if (pointProp.Count > 2) return null;
var xProb = pointProp
.FirstOrDefault(p => p.PropertyName.EndsWith('X'))
?.Value;
var yProb = pointProp
.FirstOrDefault(p => p.PropertyName.EndsWith('Y'))
?.Value;
if (xProb is not null && yProb is not null)
return new Point3d((double)xProb, (double)yProb, 0);
return null;
}
public Dictionary<string,Point3d> GetAllVisiblePointProperties()
{
var pointProps = Block?
.DynamicBlockReferencePropertyCollection
.Cast<DynamicBlockReferenceProperty>()
.Where(p => p.VisibleInCurrentVisibilityState)
.Where(p => p.PropertyTypeCode == 1)
.GroupBy(p => p.PropertyName.TrimEnd('X', 'Y'))
.Where(g => g.Count() == 2)
.ToList();
if (pointProps is null) return new();
Dictionary<string,Point3d> points = new Dictionary<string,Point3d>();
foreach (var group in pointProps)
{
var xProb = group
.FirstOrDefault(p => p.PropertyName.EndsWith('X'))
?.Value;
var yProb = group
.FirstOrDefault(p => p.PropertyName.EndsWith('Y'))
?.Value;
if (xProb is not null && yProb is not null)
points.Add(group.Key, new Point3d((double)xProb, (double)yProb, 0));
}
return points;
}
public static T? Get(BlockReference? block, Transaction tr)
{
T temp = new T();
if (block?.IsInstanceOf(tr, temp.Name) is not null)
{
temp.Block = block;
temp.Transaction = tr;
return temp;
}
return null;
}
public static List<T> GetAll(BlockTableRecord btr, Transaction tr)
{
T newBlock = new T();
var list = new List<T>();
btr.ForEach<BlockReference>(tr, block =>
{
if (block.IsInstanceOf(tr, newBlock.Name) is not null)
{
T toAdd = new T();
toAdd.Block = block;
toAdd.Transaction = tr;
list.Add(toAdd);
}
});
return list;
}
public static List<T> GetAllModel(Database db, Transaction tr)
{
T newBlock = new T();
var list = new List<T>();
db.ForEachModelSpace<BlockReference>(tr, block =>
{
if (block.IsInstanceOf(tr, newBlock.Name) is not null)
{
T thisBlock = new T
{
Block = block,
Transaction = tr
};
list.Add(thisBlock);
}
});
return list;
}
}
this is the base class that is then implemented for your specific blocks. This class has functionality for working with a blocks Attributes and properties like visibility.
It also has functions for retrieving instances from either a provided BlockReference, finding instances in a BlockTableRecord, or within the model space BlockTableRecord of a provided Database. I've so created retrievers for paperspace along the same lines for blocks I expect to be in paper space.
Here is an example of an implementing class
public class ThermostatBlock : AppBlock<ThermostatBlock>
{
public override string Name => "THERMOSTAT_COMBO";
public string? Zone
{
get => GetAttributeValue("SINGLE");
set => SetAttributeValue("SINGLE", $"T{value}");
}
public Configuration? Visibility
{
get
{
var val = GetPropertyValue("Visibility1");
return val switch
{
"Type1" => Configuration.Type1,
"Type2" => Configuration.Type2,
_ => null
};
}
set
{
string? vis = value switch
{
Configuration.Type1 => "Type1",
Configuration.Type2 => "Type2",
_ => null
};
if(vis is not null)
SetPropertyValue("Visibility1", vis);
}
}
public enum Configuration
{
Type1,
Type2
}
}
This class uses the "Zone" property to set and get the "Single" attribute from the Block instance. It also uses an enum in this case for the different visibility states that the block can take on.
I just thought I would share something I've found incredibly helpful incase anyone else could find some use for it.