Monday, September 7, 2026

Using Keyword With Editor.GetSelection()

When Editor.GetSelection() is called, AutoCAD behaves exactly as the execution of built-in command "SELECT": user can either directly click entities one at a time to get them selected; or can click anywhere in the editor and drag a selecting window, or a selecting fence...; or user can enter one of the built-in keywords, such as "All", "P", "L"...; if a non-keyword is entered, AutoCAD display an available keyword options:

These built-in keywords are used to change how the selecting behaves. 

Editor.GetSelection() in our code can be used with PromptSelectionOption class, which is not derived from PromptOption base class, as other PromptXXXXXOptions classes, has a Keywords property. We can add custom keywords (make sure to avoid built-in keywords, shown in the picture above, of course). However, there are a few things we need to be aware of:

1. The keywords are not automatically displayed at command line (or at dynamic input context menu). If we add our own custom keywords, they should be shown at command line, so that users are aware of them. So, we need to use Keywords.GetDisplayString() to append the keyword list to the adding and/or removal message of the PromptSelection object.

2. the Boolean argument of the GetDisplayString(bool showNoDefault) should be True, because, unlike in some other PropmptXXXXXOptions that have an AllowNone property, pressing Enter key during Editor.GetSelection() call will end the selecting operation, returning either PromptStatus.OK (when entities are selected), or PromptStatus.Error (no entities selected). That is, press Enter would always ends the selecting operation. Therefore, there is no point to have a default keyword.

3. When user enters a valid custom keyword, the GetSelection() continues its selecting process (i.e. waiting to make valid entity selection until Enter/Esc key is hit). Therefore, if the keyword entering is somehow not associated with how the selecting behaves, the custom keywords are useless. 

Here is the code example of showing custom keywords in the GetSelection() call:

[CommandMethod("SelWithKwd1")]
public static void GetSelectionWithKeyword1()
{
    var dwg = CadApp.DocumentManager.MdiActiveDocument;
    var ed = dwg.Editor;
 
    GetSelectionWithKeywords(ed);
    ed.PostCommandPrompt();
}
 
private static void GetSelectionWithKeywords(Editor ed)
{
    var opt = new PromptSelectionOptions();
    opt.Keywords.Add("All");
    opt.Keywords.Add("LIne");
    opt.Keywords.Add("CIrcle");
    opt.Keywords.Default = "LIne";
    opt.MessageForAdding = $"Select entities, or " + opt.Keywords.GetDisplayString(true) + ": ";
    opt.MessageForRemoval = $"Select entities, or " + opt.Keywords.GetDisplayString(true) + ": ";
 
    var res = ed.GetSelection(opt);
    if (res.Status == PromptStatus.OK)
    {
        ed.WriteMessage($"\nSelected: {res.Value.Count}");
    }
    else if (res.Status == PromptStatus.Keyword)
    {
        // This line will never be hit, because the
        // PromptSelectionResult.Status will never be PromptStatus.Keyword
        ed.WriteMessage($"\nKeywork inputted");
    }
    else
    {
        ed.WriteMessage("\nNothing selected.");
    }
}

Obviously, if adding custom keyworks does not change how Editor.GetSelection() behaves, then adding custom keyword is meaningless. Here is where the KeywordInput and UnknownInput events of the PromptSelectionOptions class come into play.

If one wants to append custom keywords to the built-in keywords, which would change the way how entities get selected. However, this behavior change could be quite difficult to implement. In reality, I have never felt the need to have other way or ways to select entities beyond the built-in approach (clicking on entities, window/fence selecting...). If one really needs to do this, I'd imagine that there is probably need to incorporate KetwordInput and/orUnknownInput events handling with Editor.SelectionAdded/Removed, or even other Editor events handling together. 

In real AutoCAD .NET programming, I often wish that when calling GetSelection(), I can use keywords in the similar way as the keywords in other PromptOption based classes that when a keyword is entered, the GetSelection() is ended in a desired matter (i.e. neither the selection is completed or aborted). For example, the GetSelection() can begin with no filter, or a certain filter; then depending on what have been selected, I may want to give user option to change/add different filter. Or, the selecting is in middle of a longer custom command execution, I'd like give an option to restart the selecting/or loop the selecting with keyword, rather than only allow users to cancel the selecting, which in turn may stop the lengthy custom execution for only starting it all over again. To achieve this goal, we can simply handle the KeyworkInput event properly and stop the selecting operation gracefully. The trick of doing it is to raise an exception in the event handler, and wrap the Editor.GetSelection() call with try...catch... to specifically catch the exception.

For example, in the middle of lengthy custom code execution, I let user to get a selection of either LINE entities, or CIRCLE entities. I could call Editor.GetKeyword() to let user to make the choice before calling Editor.GetSelection() with corresponding filter. But we can eliminate the need to use GetKeyword() entirely by using keyword in Editor.GetSelection(). In this case, user can either go ahead with default filter (say, LINE filter), or enter the keyword to change to another filter and re-run Editor.GetSelection(). Without the keyword option, if wanting to use the other filter, user can only either complete the selection (not desired, obviously), or cancel the selection (then you do not know if user really wants to cancel, or just wants different filter).

The code example showed below is for the scenario where I want user to select either LINE, or CIRCLE by using custom keyword in Editor.GetSelection() operation, so that user can freely switch the selection filter without having to either complete or cancel the selecting process (well, the GetSelection() is indeed cancelled behind the scene, but from the user's point view, the selecting process continues until either the Enter or Esc key is hit to make it complete or cancelled.

[CommandMethod("SelWithKwd2")]
public static void GetSelectionWithKeyword2()
{
    var dwg = CadApp.DocumentManager.MdiActiveDocument;
    var ed = dwg.Editor;
 
    var ids = GetSelectionWithKeywordInputHandler(ed);
    ed.WriteMessage($"\nSelected entities: {ids.Count()}");
    ed.PostCommandPrompt();
}
 
private static IEnumerable<ObjectIdGetSelectionWithKeywordInputHandler(Editor ed)
{
    var selectedIds=new List<ObjectId>();
 
    var selectLine = true;
    while (true)
    {
        SelectionFilter filter;
        if (selectLine)
        {
            filter = new SelectionFilter(new[] { new TypedValue((int)DxfCode.Start, "LINE") });
        }
        else
        {
            filter = new SelectionFilter(new[] { new TypedValue((int)DxfCode.Start, "CIRCLE") });
        }
 
        string addingMsg;
        string removalMsg;
 
        var opt = new PromptSelectionOptions();
        if (selectLine)
        {
            addingMsg = "Select LINE entities:";
            removalMsg = "Remove LINE entities:";
            opt.Keywords.Add("CIrcle");
        }
        else
        {
            addingMsg = "Select CIRCLE entities:";
            removalMsg = "Remove CIRCLE entities:";
            opt.Keywords.Add("LIne");
        }
 
        opt.MessageForAdding = $"{addingMsg}, or {opt.Keywords.GetDisplayString(true)}: ";
        opt.MessageForRemoval = $"{removalMsg}, or {opt.Keywords.GetDisplayString(true)}: ";
 
        opt.KeywordInput += (oe) =>
        {
            if (e.Input.Equals("LIne") || e.Input.Equals("CIrcle"))
            {
                throw new ApplicationException(
                    $"GetSelection() custom keyword: {e.Input}");
            }
        };
 
        try
        {
            var res = ed.GetSelection(optfilter);
            if (res.Status == PromptStatus.OK)
            {
                ed.WriteMessage($"\nSelected: {res.Value.Count}");
                selectedIds.AddRange(res.Value.GetObjectIds());
            }
            else
            {
                ed.WriteMessage("\nSelection cancelled.");
            }
            ed.Regen();
            break;
        }
        catch(ApplicationException ex)
        {
            if (ex.Message.Contains("GetSelection()"))
            {
                selectLine = ex.Message.EndsWith("LIne");
            }
        }
ed.Regen();     }

Noticed: each time when the throwing an ApplicationException disrupts the selection operation, I called Editor.Regen() in order to clear the highlight the Editor.GetSelection() applied to selected entities. Editor.Regen() also called when the selecting is completed/cancelled, which is not needed normally. But because the selecting could be disrupted by throwing exception in the KeywordInput event handler, which would lead to AutoCAD losing tracks of which entities were highlighted, thus the need of calling Editor.Regen(). It might be an issue: if the drawing's current view is very crowded, regen would take time to complete. But since we do selecting on screen with Editor.GetSelection(), it is likely we have already zoomed in enough for easy screen selection, thus hopefully regen would not be a big deal in most cases.

Here is the video clip shows the code in action:




No comments:

Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.