cad.net 复制中断bug以及隐藏图元

说明

复制图元的时候多次按下ESC导致复制中断的bug,令REGEN,REGENALL更新图元无效.
浩辰没有这个bug.

注意例子要有足够多的图元(大概一万个图元),
才能很好展示这个bug,而且这个bug直到2019都会有,
我已经测试了Acad2008和Acad2019.

这个时候cad提交到数据库的操作是成功的,
但是显示的提交却是失效,导致用户暂时无法操作隐藏的那个部分图元.
用户可以关闭dwg,再打开,就能看见.

动图示意:
img

样图在: 桌子的论坛链接

解决方案

1,复制之前用键盘钩子禁止ESC,轻松解决.

2,自己实现一个copy命令,
因为c#实现的命令是不会接受ESC的.

主函数

下面只是遇到这个bug的一些测试代码,请不要将它作为修复方案.
它仅为刷新全局的参考

打开隐藏的方法,及判断数量的方法:

#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.Collections.Generic;
using System.Linq;
using System;
using System.Text.RegularExpressions;

namespace JoinBox
{
    public class CmdTest_SetEntityVisual
    {
        //代替cad原有的更新命令
        [CommandMethod("jj_rea", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public void JJ_rea()
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            ed.WriteMessage(Environment.NewLine + "****惊惊连盒-刷新所有图元" + Environment.NewLine);

            var reBugEntieys = new List<ObjectId>();      //可以编组的图元(bug图元
            var reVisibleEntieys = new List<ObjectId>();  //正常隐藏的图元

            doc.Action(() => {
                db.Action(tr => {
                    EntityVisibleHelper.SetEntityVisible(tr, db, ed,
                        reBugEntieys,
                        reVisibleEntieys,
                        false);
                });
            });
        }

        /*  bug: 复制中断
         *  操作: 复制的时候如果连续用esc键,那么会导致复制中断,然后图元出现隐藏,令re,rea更新图元无效.
         *       但是图元却是成功提交到数据库的!
         *  原因: 桌子内置的更新显示函数没有和数据库提交的时候一起锁交互,导致出错了.
         *  解决方法:  ent.Visible = true;
         *  但是if(!ent.Visible)不能够判断这样的情况,而直接设置可以成功更新到这个图元的状态.
         *  如果要判断图元数量可以通过 SelectAll 来进行,经显示函数的获取是获取不到的,然后与遍历块表的差集就可以得出.
         */

        //修改隐藏图元
        [CommandMethod("jj_yc", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public void JJ_yc()
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            ed.WriteMessage(Environment.NewLine + "****惊惊连盒-打开所有隐藏图元" + Environment.NewLine);

            var reBugEntieys = new List<ObjectId>();        //可以编组的图元(bug图元
            var reVisibleEntieys = new List<ObjectId>();    //正常隐藏的图元

            doc.Action(() => {
                db.Action(tr => {
                    EntityVisibleHelper.SetEntityVisible(tr, db, ed,
                        reBugEntieys,
                        reVisibleEntieys,
                        true,
                        true);
                });
            });

            if (reVisibleEntieys.Count > 0)
                ed.WriteMessage($"{Environment.NewLine}打开修改Visible图元隐藏的数量是: {reVisibleEntieys.Count}");
            if (reBugEntieys.Count > 0)
                ed.WriteMessage($"{Environment.NewLine}打开bug图元数量是: {reBugEntieys.Count}");
        }
    }

    public static class EntityVisibleHelper
    {
        /// <summary>
        /// 设置视图,并选择图元
        /// </summary>
        /// <param name="ac"></param>
        static void SetViewAndGetEntitys(Editor ed, Database db, Action<ObjectId[]> ac)
        {
            var viewOld = ed.GetCurrentView(); //旧视图
            db.UpdateExt(true);                //更新当前空间的范围,如果中断的bug的图元范围在(db.Extmin,db.Extmax)范围外,那么利用这个更新先

            bool tilemode = false;
            var msps = CadSystem.SpatialPoint();
            if (msps == CadSystem.SpatialPosition.Viewport)
            {
                //设置视图,如果当前在布局,那么这个(db.Extmin, db.Extmax)是模型的,所以要切换的模型
                CadSystem.Setvar("tilemode", "1");
                tilemode = true;
            }
            IFoxCAD.Cad.EditorEx.ZoomExtents(ed);//更新视图到全屏

            /*
               获取隐藏的数量
               选择集是通过显示函数获取的,复制中断引起的显示缺失图元是无法获取的
               var ssall = ed.SelectAll();                        //这种方式不能用,它和遍历全图一样
               var ssall = ed.SelectWindow(db.Extmin, db.Extmax); //Acad2008 第二+次调用会卡一会,原因不明
            */
            PromptSelectionResult ssget = null;
            if (msps == CadSystem.SpatialPosition.Model ||
                msps == CadSystem.SpatialPosition.Viewport
                )//鼠标状态在模型内
            {
                ssget = ed.SelectCrossingWindow(db.Extmin, db.Extmax);
                if (ssget.Status != PromptStatus.OK)
                    ssget = ed.SelectAll();
            }
            ed.SetCurrentView(viewOld);//变回原有视图

            if (ssget.Status == PromptStatus.OK)
                ac.Invoke(ssget.Value.GetObjectIds());//可见的选择

            if (tilemode)
                CadSystem.Setvar("tilemode", "0");
        }


        /// <summary>
        /// 打开隐藏图元
        /// </summary>
        /// <param name="tr">事务</param>
        /// <param name="db">数据库</param>
        /// <param name="ed">编辑器</param>
        /// <param name="reBugEntieys">bug隐藏的图元集合</param>
        /// <param name="reVisibleEntieys">正常隐藏的图元集合</param>
        /// <param name="openVisible">隐藏图元是否打开</param>
        /// <param name="bianzu">隐藏bug图元是否编组</param>
        /// <param name="openVisibleBug">隐藏bug图元是否打开</param>
        public static void SetEntityVisible(Transaction tr, Database db, Editor ed,
            List<ObjectId> reBugEntieys,
            List<ObjectId> reVisibleEntieys,
            bool bianzu,
            bool openVisible = false,
            bool openVisibleBug = true)
        {
            SetViewAndGetEntitys(ed, db, (idsSs) => {

                var idsAll = new List<ObjectId>();      //全图图元的id
                var entInvisible = new List<ObjectId>();//不可见,块外隐藏(故意)+块内隐藏(动态块)

                db.TraverseBlockTable(tr, btRec => {
                    foreach (var item2 in btRec)
                    {
                        if (!item2.IsOk())
                            continue;

                        using var ent = item2.ToEntity(tr);
                        if (ent.IslockLayer(tr))//选择非锁定
                            continue;

                        idsAll.Add(item2);
                        try
                        {
                            ent.UpgradeOpen();
                            if (!ent.Visible)           //这个时候并不会判断这个bug发生
                                entInvisible.Add(item2);//不可见,故意隐藏+动态块隐藏
                            ent.Visible = true;         //有无可见均打开,这样才可以成功更新显示.
                        }
                        catch (System.Exception e)
                        {
                            if (e.Message == "eNotAllowedForThisProxy")//代理会修改错误  //什么也不做哟
                            { }
                        }
                        finally
                        {
                            ent.DowngradeOpen();
                        }
                    }
                    return false;
                });

                //得出bug图元
                //块表所有-可见-(故意隐藏+动态块隐藏) == bug图元(得出复制中断产生的bug图元,用户不可选择的)
                var bugEntity = idsAll.Except(idsSs).Except(entInvisible);//差集

                //得出bug图元中可以编组的
                var bugEntieysGroup = new List<ObjectId>();//bug图元(编组
                var regex = new Regex("^ASSORTED_");       //正则
                foreach (var item in bugEntity)
                {
                    if (item.IsOk())
                    {
                        using var ent = item.ToEntity(tr);
                        if (ent.BlockName != "_ArchTick" && !regex.IsMatch(ent.BlockName))
                            bugEntieysGroup.Add(item);
                    }
                }

                //块外隐藏(故意)是否打开
                var blockVisibleNo = entInvisible.SpaceFilter(db, tr, EntityEdit.Space.Model | EntityEdit.Space.Paper);
                if (!openVisible)
                {
                    foreach (var item in blockVisibleNo)
                    {
                        using var ent = item.ToEntity(tr);
                        ent.UpgradeOpen();
                        ent.Visible = false;
                        ent.DowngradeOpen();
                    }
                }

                //块内隐藏(动态块)关闭他们
                var blockVisible = entInvisible.Except(blockVisibleNo); //为了效率,这里加速一下
                foreach (var item in blockVisible)                      //块内依然要隐藏
                {
                    using var ent = item.ToEntity(tr);
                    ent.UpgradeOpen();
                    ent.Visible = false;
                    ent.DowngradeOpen();
                }

                if (!openVisibleBug)
                {
                    foreach (var item in bugEntieysGroup)
                    {
                        using var ent = item.ToEntity(tr);
                        ent.UpgradeOpen();
                        ent.Visible = false;
                        ent.DowngradeOpen();
                    }
                }

                if (bugEntieysGroup.IsNullNo())
                {
                    //打开完全图的bug图元之后,这里只编组当前空间bug图元.
                    var bugEntityGroup = bugEntieysGroup.SpaceFilter(db, tr, EntityEdit.Space.CurrentSpace);
                    bugEntityGroup = bugEntityGroup.Except(blockVisible).Except(blockVisibleNo);//减去块隐藏和块内隐藏
                    if (bugEntityGroup.IsNullNo() && bianzu)
                    {
                        db.CreateGroup(tr, bugEntityGroup, out string gname);//新建组
                        ed.WriteMessage($"{Environment.NewLine}已经产生编组: {gname}");
                    }

                    //传出值修改
                    foreach (var item in bugEntityGroup)
                        reBugEntieys.Add(item);
                }

                //传出值修改
                foreach (var item in blockVisibleNo)
                    reVisibleEntieys.Add(item);
            });
        }
    }
}

子函数

其他缺省请利用本博客的搜索功能...如果缺少就按照文字理解一下,或者留言

db.TraverseBlockTable 来自本博客这里

IFoxCAD.Cad.EditorEx.ZoomExtents(ed); 来自这里IFoxCAD库

public static partial class CadSystem
{
    public enum SpatialPosition
    {
        Model,     //模型
        Viewport,  //布局视口内
        Layout,    //布局视口外
    }

    /// <summary>
    /// 获取当前位置
    /// </summary>
    /// <returns></returns>
    public static SpatialPosition SpatialPoint()
    {
        if (Getvar("tilemode") == "1")
            return SpatialPosition.Model;//模型

        //布局的第一个视口就是没有进其他视口的
        if (Getvar("cvport") == "1")
            return SpatialPosition.Layout;//视口外
        else
            return SpatialPosition.Viewport;//视口内
    }

    /// <summary>
    /// 当前布局名称(视口内为模型)
    /// </summary>
    /// <returns>模型或布局名称</returns>
    public static string GetMouseSpace()
    {
        //获得当前布局名称
        string layoutName = LayoutManager.Current.CurrentLayout;//切换当前布局要锁文档
        if (SpatialPoint() == CadSystem_ToleranceHelper.SpatialPosition.Viewport)
            layoutName = "Model";
        return layoutName;
    }
}

public static partial class EntityEdit
{
    /// <summary>
    /// 过滤剩下本空间的图元
    /// </summary>
    /// <param name="ids_old">要过滤的图元集合</param>
    /// <param name="db">数据库</param>
    /// <param name="tr">事务</param>
    /// <param name="space">这里的空间是指此空间最外层的块,块内的记录不属于本空间的</param>
    /// <param name="ReverseSelection">逆反选择</param>
    /// <returns></returns>
    public static IEnumerable<ObjectId> SpaceFilter(this IEnumerable<ObjectId> ids_old,
        Database db, Transaction tr, Space space = Space.CurrentSpace, bool ReverseSelection = false)
    {
        using var bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;

        var spaceIds = new List<ObjectId>();
        if ((space & Space.CurrentSpace) == Space.CurrentSpace)
            spaceIds.Add(db.CurrentSpaceId);
        if ((space & Space.Model) == Space.Model)
            spaceIds.Add(bt[BlockTableRecord.ModelSpace]);
        if ((space & Space.Paper) == Space.Paper)
            spaceIds.Add(bt[BlockTableRecord.PaperSpace]);
        //过滤剩下相同空间的图元
        var ve = ids_old;
        var idn = new List<ObjectId>();
        foreach (var spaceId in spaceIds)
        {
            using var btRec = tr.GetObject(spaceId, OpenMode.ForRead) as BlockTableRecord;
            if (idn.Count > 0)
                ve = idn;
            foreach (var item in ve)
            {
                using var ent = item.ToEntity(tr);
                if (ent.BlockId == spaceId) // if (ent.BlockName == btRec.Name) 等价的
                    idn.Add(item);
            }
            if (idn.Count == 0)
                break;
        }
        if (ReverseSelection)
            idn = ids_old.Except(idn).ToList();//减去故意隐藏的
        return idn;
    }
 
    /// <summary>
    /// id有效,未被删除
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public static bool IsOk(this ObjectId id)
    {
        return !id.IsNull && id.IsValid && !id.IsErased && !id.IsEffectivelyErased && id.IsResident);
    }

    /// <summary>
    /// id转实体
    /// </summary>
    /// <param name="id">图元ID</param>
    /// <param name="tr">事务</param>
    /// <returns></returns>
    public static Entity ToEntity(this ObjectId id, Transaction tr)
    {  
        return tr.GetObject(id, OpenMode.ForRead) as Entity;
    }
}

(完)

posted @ 2019-09-05 18:18  惊惊  阅读(982)  评论(0编辑  收藏  举报