cad.net 图层
影响图层显示的主要有:
关闭 isOff
冻结 IsFrozen
图层隐藏 isHidden
视口冻结 FreezeLayersInViewport
图层解锁褪色度
想跟acad的锁定/解锁一样拥有褪色度,那么你需要阅读此篇
代码
#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
namespace JoinBox {
public class TestLayer {
[CommandMethod(nameof(JJCmd_LayerLock))]
public void JJCmd_LayerLock()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
var pr = ed.GetEntity("\n请选择图元:锁定它的图层");
if (pr.Status != PromptStatus.OK)
return;
using DBTrans tr = new();
var ent = (entity)tr.GetObject(pr.ObjectId);
SetLayerLock(ent.LayerId, true);
}
[CommandMethod(nameof(JJCmd_LayerUnLock))]
public void JJCmd_LayerUnLock()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
var peo1 = new PromptEntityOptions("\n请选择图元:解锁它的图层")
{
AllowObjectOnLockedLayer = true,
AllowNone = true
};
var pr = ed.GetEntity(peo1);
if (pr.Status != PromptStatus.OK)
return;
using DBTrans tr = new();
var ent = (entity)tr.GetObject(pr.ObjectId);
SetLayerLock(ent.LayerId, false);
}
}
}
namespace JoinBox
{
public static partial class LayerHelper
{
static _isOff = false;
/// <summary>
/// 按层名锁定或解锁图层
/// </summary>
/// <param name="layerId">图层id</param>
/// <param name="locked"><see langword="true"/>锁定,<see langword="false"/>解锁</param>
/// <returns>图层原本锁定的状态</returns>
public static bool SetLayerLock(ObjectId layerId, bool locked = true)
{
if (layerId.IsOk()) return;
var tr = DBTrans.Top;
if (locked)
{
// 锁定图层:
// 先遍历这个图层的图元,把它放入刷新队列,
// 因为图层锁了的话图元就无法更改了
SetLayLockFadectl(layerId, true);
}
// 更新图层褪色度的显示
bool oldSetting = false;
var ltr = (LayerTableRecord)tr.GetObject(layerId, OpenMode.ForRead);
if (ltr != null)
{
ltr.UpgradeOpen();
oldSetting = ltr.IsLocked;
ltr.IsLocked = locked;
// 确保不会被编译器优化
Volatile.Write(ref _isOff, ltr.IsOff);
// 必须自己设置自己才能更新显示
ltr.IsOff = Volatile.Read(ref _isOff);
ltr.DowngradeOpen();
ltr.Dispose();
}
if (!locked)
{
// 解锁图层:
// 上面先解锁,再处理褪色度显示,
// 因为图层锁了的话图元就无法更改了
SetLayLockFadectl(layerId, false);
}
return oldSetting;
}
/// <summary>
/// 设置图层褪色度显示
/// </summary>
/// <param name="layerId">图层id</param>
/// <param name="locked"><see langword="true"/>锁定,<see langword="false"/>解锁</param>
static void SetLayLockFadectl(ObjectId layerId, bool locked = true)
{
var tr = DBTrans.Top;
const string str = "LayLockFadectl";
var value = int.Parse(CadSystem.Getvar(str));
// 锁定-绝对值褪色...设置了会自动刷新屏幕显示
value = Math.Abs(value);
if (!locked)
value *= -1;//解锁-负数.设置完之后要手动刷新
CadSystem.Setvar(str, value.ToString());
// 方案一:获取屏幕内的图元.缺少的函数见文章相关引用
// 成功是成功,但是不在屏幕内的褪色度没有变化
// var ids = ed.SelectViewportObjectId();
// ids.ToEntity(tr, ent => {
// ent.EntityRedraw(BrightEntity.RecordGraphicsModified);
// });
// 方案二:这个刷新不成功,见文章相关引用的"动画"
// ed.UpdateScreenEx();
// 方案三:遍历全图,加入刷新队列.缺少的函数见文章相关引用
#if true
db.TraverseEntitys(tr, entItem => {
if (entItem.LayerId == layerId)
entItem.EntityRedraw(BrightEntity.RecordGraphicsModified);
return false;
});
#else
// 以下代码仅用于测试:
// 请不要并行遍历块表,否则将导致块表迭代器失效!!!
// 查询图元不涉及数据库更改,改为并行查询.
// 并且应该限制当前空间,不过可能是布局视口内的模型,所以还是遍历全图吧.
// 限制并行操作为CPU核心数
int degreeOfParallelism = System.Environment.ProcessorCount;
// 获取块表
var table = (BlockTable)tr.GetObject(tr.Database.BlockTableId);
// 并行查询
var entitys = table.Cast<ObjectId>()
.AsParallel() //.AsEnumerable() 顺序执行
.WithDegreeOfParallelism(degreeOfParallelism)
.Where(btRecordId => btRecordId.IsOk())
.Select(btRecordId => (BlockTableRecord)tr.GetObject(btRecordId, OpenMode.ForRead))
.SelectMany(btr => btr.Cast<ObjectId>().Where(eid => eid.IsOk())) // 扁平化时候过滤条件,减少合并大小.
.Select(eid => (Entity)tr.GetObject(eid, OpenMode.ForRead))
.Where(ent => ent.LayerId == layerId); // 得到相同图层的图元
// 顺序加入刷新队列
entitys.AsSequential().ForEach(ent => {
ent.EntityRedraw(BrightEntity.RecordGraphicsModified);
});
#endif
//解锁图层之后,有其他图层是锁定的,那么此时需要还原褪色度
if (!locked)
CadSystem.Setvar(str, (value * -1).ToString());
}
}
}
public static class EditorHelper
{
/// <summary>
/// 获取当前屏幕的所有图元
/// </summary>
/// <param name="ed"></param>
/// <param name="tr"></param>
/// <returns></returns>
public static ObjectId[] SelectViewportObjectId(this Editor ed)
{
var view = ed.GetCurrentView();
var cpt = view.CenterPoint;
var w = view.Width / 2;
var h = view.Height / 2;
var min = new Point3d(cpt.X - w, cpt.Y - h, 0);
var max = new Point3d(cpt.X + w, cpt.Y + h, 0);
var ssget = ed.SelectCrossingWindow(min, max);
if (ssget.Status != PromptStatus.OK)
return new();
return ssget.Value.GetObjectIds();
}
}
特别说明
0x01
e大告诉我这个神奇的地方,(我也在Adn博客看见了)
估计是触发了某些桌子内部机制,就好像WPF的数据绑定触发?
可以尝试注释掉这句再运行,会发现锁定褪色都不成功.acad08测试.
ltr.IsOff = ltr.IsOff;
这种狗屎设计,会被编译器优化杀掉的!!所以我们要用volatile读写.
0x02
解锁是必须遍历全图的,
若只遍历当前屏幕的图元,会呈现这样的效果,屏幕外没有退出淡显:
相关引用
BrightEntity.RecordGraphicsModified
高版本
https://www.cnblogs.com/d1742647821/p/17995075
下面的操作,Autodesk.AutoCAD.Internal命名空间是COM接口.
Acad08只能引用COM的DLL或者反射,但反射无函数提示,
并且引用COM的DLL需要程序域卸载.
原作者只从Acad2015开测:
Acad2015/2016是LayerUtilities类.
Acad2017(R21.0)版本及以上是CoreLayerUtilities类,
public static void RegenLayers(ObjectId[] layerIds) {
var type = Acap.Version.Major >= 21
? Assembly.Load("accoremgd")?.GetType("Autodesk.AutoCAD.Internal.CoreLayerUtilities")
: Assembly.Load("acmgd")?.GetType("Autodesk.AutoCAD.Internal.LayerUtilities");
var mi = type?.GetMethods().FirstOrDefault(e => e.Name == "RegenLayers");
var pi = type?.GetProperties().FirstOrDefault(e => e.Name == "RegenPending");
var regenPending = (int)(pi?.GetValue(null) ?? 0);
mi?.Invoke(null, new object[] { layerIds, regenPending });
}
隐藏图层
今天小博发现了一件事情,
无论怎么用 IsHidden
都会出现报错 eDuplicateRecordName
有问题的代码
public class Commands {
[CommandMethod(nameof(TestLayer))]
public void TestLayer() {
using DBTrans tr = new();
var table = (LayerTable)tr.GetObject(tr.Database.LayerTableId);
const string layerName = "test";
if (!table.Has(ss)) {
var layer = new LayerTableRecord() {
Name = layerName,
IsHidden = false
};
table.UpgradeOpen();
table.Add(layer);
tr.AddNewlyCreatedDBObject(layer, true);
table.DowngradeOpen();
table.Dispose();
return;
}
foreach (var item in table) {
var layer = (LayerTableRecord)tr.GetObject(item);
if (layer.Name == layerName) {
layer.UpgradeOpen();
layer.IsHidden = true;
layer.DowngradeOpen();
layer.Dispose();
break;
}
}
}
}
acad2008和acad2019测试都是如下报错:
第一次遍历,生成一个
test
图层,
第二次遍历,修改隐藏(相当于删除了的感觉)
第三次遍历,又生成了一个test
图层
第四次遍历,修改隐藏报错了
原因
后来得到e大的帮助,说了一个很重要的问题:遍历器上面没有!!
那么我就去看了层表,层表上面有个参数是IncludingHidden
,
所以代码要写这句,层表才会显示隐藏的图层:(几乎所有表都有这个隐藏?😏)
var layerTable = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForWrite);
layerTable = layerTable.IncludingHidden;
之后,我就发现了,报错的原因:
第一次遍历,生成一个
test
图层,
第二次遍历,修改隐藏(相当于把图层改成了*test
的名字)
第三次遍历,又生成了一个test
图层
第四次遍历,修改隐藏,相当于把test
改成*test
,
因为第二遍的时候隐藏的已经有这个名字了,就报错了!
修改好的代码
写了个例子:
尝试注释掉打开隐藏那个,反复执行命令.
public class Command {
[CommandMethod(nameof(TestLayer))]
public void TestLayer()
{
using DBTrans tr = new();
var table = (LayerTable)tr.GetObject(tr.Database.LayerTableId, OpenMode.ForWrite);
table = table.IncludingHidden; // 隐藏的全部显示出来
string layerName = "test";
LayerTableRecord ltr = null;
if (!table.Has(layerName))
{
ltr = new { Name = layerName };
table.Add(ltr);
tr.AddNewlyCreatedDBObject(ltr, true);
}
switch (SetLayerHidden(layerName))
{
case LayerHiddenError.OK:
ed.WriteMessage("\n更改隐藏成功");
break;
case LayerHiddenError.No:
ed.WriteMessage("\n不含有图层,所以没得更改隐藏");
break;
case LayerHiddenError.ErrorHaveStarsLayer:
ed.WriteMessage("\n已有带*图层,不能隐藏");
break;
case LayerHiddenError.ErrorNotStarsLayer:
ed.WriteMessage("\n已有不带*图层,不能取消隐藏");//这句始终不会执行
break;
}
switch (SetLayerHidden(layerName,true))
{
case LayerHiddenError.OK:
ed.WriteMessage("\n更改隐藏成功");
break;
case LayerHiddenError.No:
ed.WriteMessage("\n不含有图层,所以没得更改隐藏");
break;
case LayerHiddenError.ErrorHaveStarsLayer:
ed.WriteMessage("\n已有带*图层,不能隐藏");//这句始终不会执行
break;
case LayerHiddenError.ErrorNotStarsLayer:
ed.WriteMessage("\n已有不带*图层,不能取消隐藏");
break;
}
}
}
public enum LayerHiddenState {
/// <summary>
/// 成功更改隐藏
/// </summary>
OK,
/// <summary>
/// 不含有图层,所以没得更改隐藏
/// </summary>
No,
/// <summary>
/// 已有带*图层,不能隐藏
/// </summary>
ErrorHaveStarsLayer,
/// <summary>
/// 已有不带*图层,不能取消隐藏
/// </summary>
ErrorNotStarsLayer,
}
/// <summary>
/// 设置隐藏图层
/// </summary>
/// <param name="layerName">不带*的图层名称</param>
/// <param name="isHidden">隐藏true,显示false</param>
/// <returns></returns>
public static LayerHiddenState SetLayerHidden(string layerName, bool isHidden=false)
{
var tr = DBTrans.Top;
var table = (LayerTable)tr.GetObject(tr.Database.LayerTableId, OpenMode.ForWrite);
table = table.IncludingHidden; // 隐藏的全部显示出来
if (table.Has(layerName))
return LayerHiddenState.ErrorNotStarsLayer;
foreach (var item in table){
if (!item.IsOk()) continue;
var ltr = (LayerTableRecord)tr.GetObject(item, OpenMode.ForWrite);
if (ltr.Name == ("*" + layerName)) {
ltr.IsHidden = isHidden;
return LayerHiddenState.OK;
}
}
return LayerHiddenState.No;
}
因为*号
是保留关键字
,普通用户不给用的,
大家要判断层名的时候就要打开IncludingHidden
遍历的时候
同时判断"*"+layerName
和 layerName
普通cad用户只能干着急,嘻嘻
e大还说了,把dwg存成dxf,然后用对比文件看,也能看到,并改...
这貌似是给普通用户的简便修改方式?
(完)