cad.net 选择集问题

保留关键字

无论是cad.arx还是cad.net的开发人员都需要注意交互的保留关键字.
N S W E是东南西北四个方位,s是南方270度,在设置关键字的时候必须避开这四个关键字.
这个设置在早期的R14也有:

在使用选择集的时候,设置关键字不可以用a,它会是all的首字母,更多的参考一下飞诗的lisp选择集屏蔽关键字.

选择集预选

动图演示

img

本次测试

测试环境: Acad2008
这个bug是这样的,在预选状态时候执行命令,会导致该命令去重设一次选择集,
而没有预选则十分正常

错误代码

[CommandMethod("tta", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
public static void tta()
{
    Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;    
    var psr = ed.GetSelection();
    if (psr.Status != PromptStatus.OK)
        return;
    //重构了一个选择集,为什么预选的时候,要等到再次点鼠标才能成功显示呢?
    ed.SetImpliedSelection(psr.Value.GetObjectIds()); 
    //ed.Regen();
}

如果最后加一句:ed.Regen();是可以成功的,但是,这不应该啊.

因为其中的奥妙在于,预选的获取不应该利用 ed.GetSelection(pso, filter); 而是应该利用 ed.SelectImplied();

正确代码

[CommandMethod("tta", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
public static void tta()
{
    Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;
    var psr = ed.SelectImplied();//预选
    if (psr.Status != PromptStatus.OK) //数量0也是不ok
        psr = ed.GetSelection(pso, filter);//手选
    if (psr.Status != PromptStatus.OK)
        return;
    ed.SetImpliedSelection(psr.Value.GetObjectIds()); //重构了一个选择集
}

原因为什么会这样呢?
因为手选的 ed.GetSelection() 直接读取了选择集缓冲区,没有很好的处理预选状态..
导致了引起了选择的内部错误,是函数单一职责的封装问题.

相关阅读

acedSSSetFirst选择集夹点亮显实例

com选择集的例子

    /// <summary>
    /// 输出WMF<br/>
    /// </summary>
    /// <param name="editor">命令行对象</param>
    /// <param name="saveFile">保存文件</param>
    /// <param name="ids">选择集的对象,为null时候手选</param>
    /// <exception cref="ArgumentNullException"></exception>
    public static void ExportWMF(this Editor editor, string saveFile, ObjectId[]? ids = null)
    {
        if (string.IsNullOrEmpty(saveFile))
            throw new ArgumentNullException(nameof(saveFile));
        if (File.Exists(saveFile))
            throw new FileFormatException("文件重复:" + saveFile);

        var dm = Acap.DocumentManager;
        if (dm.Count == 0)
            return;

        // 剔除后缀
        int dot = saveFile.LastIndexOf('.');
        if (dot != -1)
        {
            // 因为文件名可以有.所以后缀点必须是最后\的后面
            int s = saveFile.LastIndexOf('\\');
            if (s < dot)
                saveFile = saveFile.Substring(0, dot);
        }

        // ActiveSelectionSet:
        // 第一次执行会触发选择,
        // 再次重复命令执行的时候,它会无法再选择
        // 因此此处选择一次,此时必然有当前选择集,它就直接获取当前选择集
        if (ids == null || ids.Length == 0)
        {
            var psr = editor.SelectImplied();// 预选
            if (psr.Status != PromptStatus.OK)
                psr = editor.GetSelection();// 手选
            if (psr.Status != PromptStatus.OK)
                return;
        }
#if zcad
        var com = Acap.ZcadApplication;
#else
        var com = Acap.AcadApplication;
#endif
        var doc = com.GetProperty("ActiveDocument");
        var wmfSet = doc.GetProperty("ActiveSelectionSet");
        doc.Invoke("Export", saveFile, "wmf", wmfSet); // JPGOUT,PNGOUT
        wmfSet.Invoke("Delete");
    }

关键字回调

kean博客ssget关键字回调

关键字回调时候会重新进入选择集,此时需要再空格才能退出.
如何立即退出?
arx在关键字上面发送cad命令\003,

net可以引发异常退出,自己构造一个异常.
net发送命令它会经过命令模式,进行重入,不好,还是触发异常好

循环选择

1,选择状态有夹点显示,此时缓冲区有id集合且不为0.
2,亮显没有夹点显示.
在Acad.net提供的手选函数,无法在缓冲区不为0时再选择,如果此时循环再手选,不会发生阻塞,而是直接就是ok的...

问题代码展示:

[CommandMethod(nameof(LoopBug))]
public void LoopBug() {
    Document doc = Acap.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;
    for (int i = 0; i < 5; i++) {
        // 第1次会阻塞后执行手选,2+就不会了.
        var opts = new PromptSelectionOptions() { MessageForAdding = "\n请选择图元:" };
        var ss = ed.GetSelection(opts);
        if (ss.Status != PromptStatus.OK) {
            ed.WriteMessage("\n未选择任何图元.");
            continue;
        }
        var ids = ss.Value.GetObjectIds();
        ed.WriteMessage("\n选择了图元数:" + ids.Length);
        ed.SetImpliedSelection(ids);
    }
}

我们很容易想到一个解决方案,
如果不设置缓冲区,那么是不是只能亮显了?
但是亮显图元无法用shift剔除.
重点:
此时需要通过键盘钩子进行拦截shift键入,然后发送消息结束ssget.把全部图元亮显,求出鼠标矩形在哪些图元上面,暗显并ids也减去.
shift抬起时候,恢复亮显.

[CommandMethod(nameof(Pick), CommandFlags.UsePickSet)]//记得加这个flag
public void Pick() {
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;

    var opts = new PromptSelectionOptions("\n请选择对象:");
    opts.AllowNone = true;
    opts.AllowArbitraryInput = true; // 允许任意输入
    // opts.SingleOnly = true;//只一个对象不回车
    opts.SelectEverythingInAperture = true;// 点击模式

    // 过滤器
    SelectionFilter fr = new (new TypedValue[] {
        new TypedValue((int)DxfCode.Start, "LINE"), 
    });

    var ss = ed.SelectImplied();//预选
     if (ss.Status == PromptStatus.OK) {
         foreach(var id in ss.Value.GetObjectIds()) {
             // 这里是伪代码,没有过滤条件就注释掉
             if(!fr.Contains(id)) { _ids.Add(id); }
         }
      // 清空选择集
      ed.SetImpliedSelection(new ObjectId[] { });
    }

    // 循环选择,重新手选
    while(true) {
        _ids.ForEach=>{ent.Highlight();}//亮显
        ss = ed.GetSelection(opts,fr);
        if (ss.Status == PromptStatus.OK) {
            _ids.ForEach=>{ent.Unhighlight();}//暗显
            _ids.AddRange(ss.Value.GetObjectIds());
            ed.SetImpliedSelection(new ObjectId[] { });
            continue;
      }
      
      if(ss.Status == PromptStatus.Cancel) {
      /* 键盘钩子发送Esc消息才到这个状态
          方案一:
          先选中ids,再开启新选择集,然后减选.
          我怀疑不行,因为选中状态(缓冲区有对象)会直接跳过手选.
          shift按着也可能不切换到新选择集的反选模式.
          释放shift也要结束这个选择集
        _ids.ForEach=>{ent.Unhighlight();}//暗显
        ed.SetImpliedSelection(_ids.ToArray());//重设选择集
        var s2 = ed.GetSelection();
        if(s2.Status == PromptStatus.OK) {
            _ids.RemoveAll(s2.Value.GetObjectIds());
        }
        方案二:
        不重设选择集,获取选择集矩形两个点ss.PickedPoint,然后直接剔除集合
         没有代码
         */
      }
      // 结束循环
      _ids.ForEach=>{ent.Unhighlight();}//暗显
      break;  
    }
}

HashSet<ObjectId> _ids = new();

void 键盘钩子(s,e) {
  if(e.Key == "shift".KeyCode()){
    Acad.Handle.Foucs().发送异步Esc消息();
    //这里_ids.事务跨线程!无法操作图元.
  }
}

选择集的本质:
啊?其实可以自己实现一个两点矩形,然后通过选框范围,再触发四叉树选择.
选框还可以利用透明winform画一个颜色,并且才用滤镜算法...很麻烦吗?
(完)

posted @ 2019-01-05 15:13  惊惊  阅读(883)  评论(0)    收藏  举报