promptselectionresult with change-able keywords

promptselectionresult with change-able keywords

stefanveurink68AXD
Advocate Advocate
1,107 Views
18 Replies
Message 1 of 19

promptselectionresult with change-able keywords

stefanveurink68AXD
Advocate
Advocate

Hey, I want to get a selection from user, while giving the possibility to change certain 'variables' for the code. 

 

With promptentityoptions i do this (simplified) like: 

string S_Scale = "Scale=1:" + D_Scale; 

PromptEntityOptions opt = new PromptEntityOptions("\nSelect");
string S_Scale = "Scale=1:" + D_Scale;
opt.Keywords.Add(S_Scale);

 PromptEntityResult res = ed.GetEntity(opt);
 if (res.Status == PromptStatus.OK)
 {}
 else if (res.Status == PromptStatus.Keyword)
 {
    if (res.StringResult == S_Scale)
    {
       PromptStringOptions pso = new PromptStringOptions("\nScale 1 op:");
       PromptResult res1 = ed.GetString(pso);
      DPSchaal = Convert.ToDouble(res1.StringResult)
    }
}

 

However, with promptselectionoptions, I don't seem to get into the "prompstatus.keyword when clicking/entering the keyword. Tried it these ways: 

https://www.keanw.com/2010/05/adding-keyword-handling-to-autocad-nets-getselection.html

https://help.autodesk.com/view/OARX/2024/ENU/?guid=GUID-94A4E3DE-0066-4D6A-8558-0252BE8CB85E 

but these don't seem to work either. 

 

Is it possible to do what I want, or isn't this available? And if yes, how?

0 Likes
1,108 Views
18 Replies
Replies (18)
Message 2 of 19

Jeff_M
Consultant
Consultant

@stefanveurink68AXD Show the code you tried based on Kean's example. It should work, as that is the way all of my selections with Keywords are handled. 

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 3 of 19

stefanveurink68AXD
Advocate
Advocate
double Puntafstand = 0.5;

var PSopties = new PromptSelectionOptions();
string S_puntafstand = "MaxAfstand=" + Puntafstand.ToString();
PSopties.Keywords.Add(S_puntafstand);
           
string kws = PSopties.Keywords.GetDisplayString(true);
PSopties.MessageForAdding = "selecteer de punten en lijnen" + kws ;

PSopties.KeywordInput += delegate (object sender, SelectionTextInputEventArgs e)
            {
               string key = e.Input;
                if (key == S_puntafstand)
                {
                    PromptStringOptions pso = new PromptStringOptions("\nGeef maximale afstand:");
                    pso.AllowSpaces = false;
                    PromptResult res1 = edi.GetString(pso);

                    bool inputakkoord = true;
                    try
                    {
                        Puntafstand = Convert.ToDouble(res1.StringResult);
                        S_puntafstand = "MaxAfstand=" + Puntafstand.ToString();
                    }
                    catch
                    {
                        inputakkoord = false; 
                    }

                    if (inputakkoord == false)
                    {
                        MessageBox.Show("Dit is geen getal!"); 
                    }
                }

            };

See above, I see the keyword, but it doesn't seem to get into the 'delegate code', I can't reach the 'string key = e.input" in my debugger.

0 Likes
Message 4 of 19

Jeff_M
Consultant
Consultant

@stefanveurink68AXD you are confusing different selection types. GetEntity does not utilize the PromptSelectionOptions. Going back to the original code you posted, something like this is more of what you will be needing.

        [CommandMethod("LetsTestThis")]
        public void letstestthis()
        {
            var D_Scale = 1.0;
            var DPSchaal = double.NaN;
            var doc = Application.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;
            PromptEntityOptions opt = new PromptEntityOptions("\nSelect");
            string S_Scale = "Scale=1:" + D_Scale.ToString("F0"); ;
            opt.Keywords.Add(S_Scale);
            opt.AppendKeywordsToMessage = true;
            PromptEntityResult res = ed.GetEntity(opt);
            while (res.Status == PromptStatus.Keyword)
            {
                if (res.StringResult == S_Scale)
                {
                    PromptDoubleOptions pso = new PromptDoubleOptions("\nScale 1 op:");
                    var res1 = ed.GetDouble(pso);
                    DPSchaal = res1.Value;
                    S_Scale = "Scale=1:" + res1.Value.ToString("F0");
                    opt.Keywords.Clear();
                    opt.Keywords.Add(S_Scale);
                    res = ed.GetEntity(opt);
                }
            }
            if (res.Status != PromptStatus.OK)
                return;
        }
Jeff_M, also a frequent Swamper
EESignature
Message 5 of 19

stefanveurink68AXD
Advocate
Advocate

Yes I understand that it works with getEntity, but then I can only select one entity. I want to select multiple, including filtering. If I understand you correct you say this is not possible? Didn't expect it to be such a difference. 

 

0 Likes
Message 6 of 19

ActivistInvestor
Mentor
Mentor

Try testing your code with a relatively- simple test keyword rather than the one that you are using (that contains an equals sign). And, make sure that the keyword doesn't conflict with any of the keywords that are available at the select objects prompt. And see if that keyword works and if your Handler is being entered with it.

 

 

0 Likes
Message 7 of 19

stefanveurink68AXD
Advocate
Advocate

Okay, got it kinda working now (including the "="-sign, also tried it without).

Only thing is I can't get the keywords to change dynamically. Like with the PromptEntityOptions. 

private SelectionSet bochten(string message)
{ 
            var PSopties = new PromptSelectionOptions();
            string S_Tol = D_tolerance.ToString();
            string S_Tolerance = "Tolerance=" + S_Tol;
            PSopties.Keywords.Add(S_Tolerance)
            
            string kws = PSopties.Keywords.GetDisplayString(true);
            PSopties.MessageForAdding = "Do selection or: " + kws;

            PSopties.KeywordInput += delegate (object sender, SelectionTextInputEventArgs e)
            {
                string key = e.Input;
                                           
                if (key == S_tolerance)
                {

                PromptStringOptions pso = new PromptStringOptions("\n set tolerance: ");
                    pso.AllowSpaces = false;
                    PromptResult res1 = edi.GetString(pso);

                    try
                    {
                        double b = Convert.ToDouble(res1.StringResult);
                        D_tolerance = b;
                        S_Tol = D_tolerance.ToString();
                        S_Tolerance = "Tolerance=" + S_Tol;
                        PSopties.Keywords.Clear();
                        PSopties.Keywords.Add(S_Tolerantie);
                        kws = PSopties.Keywords.GetDisplayString(true);
                        PSopties.MessageForAdding = "Do selection or: " + kws;
                    }
                    catch 
                    {
                        MessageBox.Show("input error");                     
                    }  
                 }
            }
}

Tried different options. The keyword is changed, because when i break out of it (by pressing "esc" in autocad) and start it again, the keyword is updated.

But I can't get it working to break out of it within the code after changing the keyword. This would be not perfect, but acceptable. 

Most perfect would be the keyword changing while still in the same loop. 

 

Any ideas?

0 Likes
Message 8 of 19

ActivistInvestor
Mentor
Mentor

@stefanveurink68AXD wrote:

Okay, got it kinda working now (including the "="-sign, also tried it without).

Only thing is I can't get the keywords to change dynamically. Like with the PromptEntityOptions. 

Tried different options. The keyword is changed, because when i break out of it (by pressing "esc" in autocad) and start it again, the keyword is updated.

But I can't get it working to break out of it within the code after changing the keyword. This would be not perfect, but acceptable. 

Most perfect would be the keyword changing while still in the same loop

 

Any ideas?


I don't see any loop in the code you show above. If that code is being executed within a loop at an outer scope that's not shown, there is a new PromptSelectionOptions instance created on each loop iteration, and would explain why the keyword isn't changing on each prompt.

 

If you're calling GetSelection() in a loop, you have the pass the same instance of the PromptSelectionOptions on each iteration, rather than creating a instance on each iteration.

0 Likes
Message 9 of 19

stefanveurink68AXD
Advocate
Advocate

yeah sorry 'loop' was not the right word used, there is no loop. I meant to say 'command'. 

0 Likes
Message 10 of 19

ActivistInvestor
Mentor
Mentor

After looking at your code again the problem is that when the user responds with a keyword, the call to GetSelection() returns. That means there must be a loop that calls GetSelection() on each iteration. 

0 Likes
Message 11 of 19

stefanveurink68AXD
Advocate
Advocate

Yes okay, I see what you mean, but problem is, in contrary to SelectEntity, i have to put the delegate before the GetSelection, so you never enter the 'else' part other then from breaking out of the 'command'. 

 

private SelectionSet bochten(string message)
{ 
  while(true)
  {
            //you only reach this place one time, not looping. 

            var PSopties = new PromptSelectionOptions();
            string S_Tol = D_tolerance.ToString();
            string S_Tolerance = "Tolerance=" + S_Tol;
            PSopties.Keywords.Add(S_Tolerance)
            
            string kws = PSopties.Keywords.GetDisplayString(true);
            PSopties.MessageForAdding = "Do selection or: " + kws;

            PSopties.KeywordInput += delegate (object sender, SelectionTextInputEventArgs e)
            {
                string key = e.Input;
                                           
                if (key == S_tolerance)
                {

                PromptStringOptions pso = new PromptStringOptions("\n set tolerance: ");
                    pso.AllowSpaces = false;
                    PromptResult res1 = edi.GetString(pso);

                    try
                    {
                        double b = Convert.ToDouble(res1.StringResult);
                        D_tolerance = b;
                        S_Tol = D_tolerance.ToString();
                        S_Tolerance = "Tolerance=" + S_Tol;
                        PSopties.Keywords.Clear();
                        PSopties.Keywords.Add(S_Tolerantie);
                        kws = PSopties.Keywords.GetDisplayString(true);
                        PSopties.MessageForAdding = "Do selection or: " + kws;
                    }
                    catch 
                    {
                        MessageBox.Show("input error");                     
                    }  
                 }
            }

                PromptSelectionResult res = edi.GetSelection(PSopties, filter);
                if (res.Status == PromptStatus.OK)
                {
                    SelectionSet set = res.Value;
                    return set;
                }
                else 
                {
                
                
                
                }
               

  }
}
0 Likes
Message 12 of 19

ActivistInvestor
Mentor
Mentor

After looking at the disassembled code for GetSelection() it doesn't appear that there is any way to change the keywords after GetSelection() is called. The keyword event handler is called from native code, and that code will continue to issue prompts for selection as long as a keyword is entered.

 

Also, what I wrote above is not true. GetSelection() doesn't return when you enter a keyword, it calls the keyword event handler and then reissues the prompt for input, and the keywords cannot be changed. The only way to exit the call to GetSelection() is to supply another type of response (select objects, or cancel/esc).  I would call that a deficiency in the design as changing keywords and optionally exiting the internal loop are legitimate requirements in certain use cases.

 

The only way I can find to exit the call to GetSelection() when a Keyword is entered, is to throw an exception from the keyword input event handler, and catch it in the code that calls GetSelection().

 

Here's an example:

public static class PromptSelectionWithKeyword
{
   public static PromptSelectionResult GetSelectionWithKeywords(this Editor editor, params string[] keywords)
   {
      string keyword = null;
      int i = 0;
      PromptSelectionOptions pso = new PromptSelectionOptions();
      pso.KeywordInput += OnKeywordInput;
      foreach(string s in keywords)
         pso.Keywords.Add(s);
      pso.Keywords.Add("Quit");
      pso.Keywords.Add("Change");

      void OnKeywordInput(object sender, SelectionTextInputEventArgs e)
      {
         keyword = e.Input;
         if(e.Input == "Quit" || e.Input == "Change")
            throw new KeywordException(e.Input); // break out of loop

         // Otherwise, prompt is repeated with the same keywords.
      }

      PromptSelectionResult result = null;

      while(true)
      {
         try
         {
            result = editor.GetSelection(pso);
         }
         catch(KeywordException ex)
         {
            editor.WriteMessage($"\nKeyword input: {ex.Keyword}");
            if(ex.Keyword == "Quit")
            {
               return null; // Can't return PromptSelectionResult
            }
            else if(ex.Keyword == "Change")
            { 
               /// Modify/add/remove keywords here
               pso.Keywords.Add("NewKeyword");
            }
            continue;
         }
         return result;
      }
   }

   public class KeywordException : System.Exception
   {
      string keyword;
      public KeywordException(string keyword)
      {
         this.keyword = keyword;
      }

      public string Keyword => keyword;
   }

   [CommandMethod("SELECTWITHKEYWORDS")]
   public static void Test()
   {
      Document doc = Application.DocumentManager.MdiActiveDocument;
      Editor editor = doc.Editor;
      var psr = editor.GetSelectionWithKeywords("FIrst", "Second", "Third");
      if(psr == null)
         editor.WriteMessage("\nUser chose Quit");
      else
         editor.WriteMessage($"\nresult.Status = {psr.Status}");
   }

}
0 Likes
Message 13 of 19

stefanveurink68AXD
Advocate
Advocate

Yeah, since delegate has to be defined before promptselection, and promptselection has to be defined before loop, I tried to change the delegate in the

if (res.Status == PromptStatus.Keyword)
  {
    //here       
  }

but isn't working either. Probably because delegate is not changing if PromptSelection isn't finished yet. Little weird but okay. 

 

Did you test your code? Because when I do I don't see any keywords appearing at all... to be honest nothing really happens (seems to). You just can do a selection, but nothing with keywords? 

 

 

0 Likes
Message 14 of 19

ActivistInvestor
Mentor
Mentor

Perhaps I didn't explain clearly enough. The code I posted works, but you may have been expecting it to do something it wasn't intended to do. It only shows how to call GetSelection() in a loop, and exit the loop when the user enters a keyword, by using an exception, and also shows how to add another keyword to the prompt. To change the keywords, you must exit the inner loop by throwing the exception. When it's caught you can then modify the keywords. 

 

It might also help to point out that once GetSelection() is called, and up to the point when it returns, the PromptSelectionOptions you pass to it, is effectively read only, and any changes made to it from within a keyword input event handler will be ignored until the PromptSelectonOptions is again passed into a subsequent call to GetSelection().

 

In other words there are two loops. One is the loop within GetSelection() that iterates each time the user responds with a keyword. The only way to exit that internal loop is by throwing an exception that is caught within the outer loop. The other "outer" loop repeatedly calls GetSelection() with the same PromptSelectionOptions, which it can modify before each call to GetSelection() (e.g., to add/change/remove keywords).

 

Here is a slightly-more verbose version of the same code posted above, that displays a message each time you enter a keyword. Two of the keywords will exit the inner loop within GetSelection(), the "Change" keyword when entered, will add another keyword to the input prompt. The "Quit" keyword will exit both the inner and outer loops and return null.

 

public static class PromptSelectionWithKeyword
{
   public static PromptSelectionResult GetSelectionWithKeywords(this Editor editor, params string[] keywords)
   {
      string keyword = null;
      int i = 0;
      PromptSelectionOptions pso = new PromptSelectionOptions();
      pso.KeywordInput += OnKeywordInput;
      foreach(string s in keywords)
         pso.Keywords.Add(s);
      pso.Keywords.Add("Quit");
      pso.Keywords.Add("Change");

      void OnKeywordInput(object sender, SelectionTextInputEventArgs e)
      {
         keyword = e.Input;
         editor.WriteMessage($"User entered keyword {e.Input}");
         if(e.Input == "Quit" || e.Input == "Change")
            throw new KeywordException(e.Input); // break out of inner loop

         // Otherwise, Select Objects: prompt is re-issued
         // with the same keywords.
      }

      PromptSelectionResult result = null;

      while(true)
      {
         try
         {
            string suffix = pso.Keywords.GetDisplayString(true);
            pso.MessageForAdding = $"\nSelect objects or {suffix}";
            result = editor.GetSelection(pso);
         }
         catch(KeywordException ex)
         {
            editor.WriteMessage($"\nGetSelection() exited, keyword = {ex.Keyword}");
            if(ex.Keyword == "Quit")
            {
               return null; // Can't return PromptSelectionResult
            }
            else if(ex.Keyword == "Change")
            { 
               /// Modify/add/remove keywords here
               pso.Keywords.Add("NewKeyword");
            }
            continue;
         }
         return result;
      }
   }

   public class KeywordException : System.Exception
   {
      string keyword;
      public KeywordException(string keyword)
      {
         this.keyword = keyword;
      }

      public string Keyword => keyword;
   }

   [CommandMethod("SELECTWITHKEYWORDS")]
   public static void Test()
   {
      Document doc = Application.DocumentManager.MdiActiveDocument;
      Editor editor = doc.Editor;
      var psr = editor.GetSelectionWithKeywords("FIrst", "Second", "Third");
      if(psr == null)
         editor.WriteMessage("\nUser chose Quit");
      else
         editor.WriteMessage($"\nresult.Status = {psr.Status}");
   }

}

And here's what happens when I run it:

Command: SELECTWITHKEYWORDS

Select objects or [FIrst/Second/Third/Quit/Change]: First
User entered keyword FIrst

Select objects or [FIrst/Second/Third/Quit/Change]: Second
User entered keyword Second

Select objects or [FIrst/Second/Third/Quit/Change]: Change
User entered keyword Change
GetSelection() exited, keyword = Change

Select objects or [FIrst/Second/Third/Quit/Change/NewKeyword]: NewKeyword
User entered keyword NewKeyword

Select objects or [FIrst/Second/Third/Quit/Change/NewKeyword]: quit
User entered keyword Quit
GetSelection() exited, keyword = Quit
User chose Quit
Command:

 

Message 15 of 19

stefanveurink68AXD
Advocate
Advocate

Well, I see what you are trying to do, but when I run it it doesn't work.... it does everything you say, only when I choose 'change'  it just gives me the option to select, nothing else happens. Others are the same as you posted above. 

 

Only difference might be I'm calling it from another class (toolpallete). But I don't see how that should matter. 

 

I'll work on it, any ideas are welcome.. 

0 Likes
Message 16 of 19

ActivistInvestor
Mentor
Mentor

The calling context most certainly does matter. If you are calling it from the application context such as from The Click Handler of a button on a tool pallet or something like that then it may not behave the way it will when called from the Handler of a registered command.

 

I generally avoid executing code from the application context and instead place the code in a registered command Handler and then just execute the command from the application context.

0 Likes
Message 17 of 19

ActivistInvestor
Mentor
Mentor

@stefanveurink68AXD wrote:

 

Only difference might be I'm calling it from another class (toolpallete). But I don't see how that should matter. 

 I'm not sure if you copied the code verbatim or not, but I just checked and the code I posted works from any calling context. E.g., from the click handler of a button on a Palette (application context), or from the registered command handler (document context).

 

From the click handler of a button (application context):

Select objects or [FIrst/Second/Third/Quit/Change]: first
User entered keyword 'FIrst'
Select objects or [FIrst/Second/Third/Quit/Change]: second
User entered keyword 'Second'
Select objects or [FIrst/Second/Third/Quit/Change]: change
User entered keyword 'Change'
GetSelection() exited, keyword = 'Change'
Select objects or [FIrst/Second/Third/Quit/Change/NewKeyword]: newkeyword
User entered keyword 'NewKeyword'
Select objects or [FIrst/Second/Third/Quit/Change/NewKeyword]: quit
User entered keyword 'Quit'
GetSelection() exited, keyword = 'Quit'
User chose Quit

So, unless there's some difference in the code you're using, then the only other possibility is the AutoCAD release you're running on. There have been subtle changes over the last 3 or 4 major releases.

Message 18 of 19

stefanveurink68AXD
Advocate
Advocate

Im running it at autocad 2024, .net-framework is 4.8. 

 

Will try some things when I got time, but i litteraly just pasted your code into mine, so i'm 100% it's exactly the same

0 Likes
Message 19 of 19

ActivistInvestor
Mentor
Mentor

Have you tried running the command that's included in the code that I posted?

0 Likes