cad.net 复制中断bug以及隐藏图元
说明
复制图元的时候多次按下ESC导致复制中断的bug,令REGEN,REGENALL更新图元无效.
浩辰没有这个bug.
注意例子要有足够多的图元(大概一万个图元),
才能很好展示这个bug,而且这个bug直到2019都会有,
我已经测试了Acad2008和Acad2019.
这个时候cad提交到数据库的操作是成功的,
但是显示的提交却是失效,导致用户暂时无法操作隐藏的那个部分图元.
用户可以关闭dwg,再打开,就能看见.
动图示意:
样图在: 桌子的论坛链接
解决方案
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;
}
}
(完)