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
解锁是必须遍历全图的,
若只遍历当前屏幕的图元,会呈现这样的效果,屏幕外没有退出淡显:

相关引用

cad.net 图元动画效果+图元刷新

BrightEntity.RecordGraphicsModified

TraverseEntitys

高版本

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,然后用对比文件看,也能看到,并改...
这貌似是给普通用户的简便修改方式?

(完)

posted @ 2022-03-11 20:21  惊惊  阅读(744)  评论(0编辑  收藏  举报