Creating an AutoCAD palette dynamically to launch commands and methods tagged using .NET

I needed to run some code from a modeless dialog, the other day, and found it was a bit of a pain to generate something quickly to do so. So I thought it might be a good idea to populate a palette dynamically with buttons that call commands and methods that were somehow tagged in the current project.

The first step was to work out how to tag them: the obvious choice being some kind of method-level designation (much as we have with the CommandMethod() attribute). I created a command – named PC, for PaletteCommands – that uses reflection on the main module of the executing assembly to extract the various commands and methods of interest. The commands get called using DocumentCollection.ExecuteInCommandContextAsync() – as we saw in this previous post, this is the way to call a command from the session context – and the methods get executed via MethodInfo.Invoke(). I've just shown one possible approach, and it's certainly open for extension, but it's pretty nice to see the dialog get populated based on the various methods flagged with the [PaletteMethod] attribute.

Dynamically created palette of commands and methods

Here's the C# code implementing the PC command (which we've also flagged, even if it's a little silly to do so) as well as the various other commands and methods populating the above palette:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Windows;

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Reflection;

using WinForms = System.Windows.Forms;

 

namespace ModelessDialogs

{

  // The basic attribute we'll use to tag the methods/commands to place

  // on the palette

 

  [AttributeUsage(AttributeTargets.Method)]

  public class PaletteMethod : Attribute { }

 

  public class Commands

  {

    private static PaletteSet _ps = null;

 

    // Our main command to display a palette. This is flagged to be included

    // on the palette, too, which is a bit silly, but harmless (as the

    // command doesn't do anything if the palette is already available)

 

    [PaletteMethod]

    [CommandMethod("PC")]

    public void PaletteCcommands()

    {

      if (_ps == null)

      {

        var doc = Application.DocumentManager.MdiActiveDocument;

        if (doc == null) return;

        var ed = doc.Editor;

 

        // We're going to take a look at the various methods in this module

 

        var asm = Assembly.GetExecutingAssembly();

        var type = asm.GetType("ModelessDialogs.Commands");

        if (type == null)

        {

          ed.WriteMessage("\nCould not find the command class.");

          return;

        }

 

        // We'll create a sequence of buttons for each callable method

 

        var bs = new List<WinForms.Button>();

        var i = 1;

 

        // Loop over each method

 

        foreach (var m in type.GetMethods())

        {

          var cmdName = "";

          var palette = false;

 

          // And then all of its attributes

 

          foreach (var a in m.CustomAttributes)

          {

            // Check whether we have a command and/or a "palette" attb

 

            if (a.AttributeType.Name == "CommandMethodAttribute")

            {

              cmdName = (string)a.ConstructorArguments[0].Value;

            }

            else if (a.AttributeType.Name == "PaletteMethod")

            {

              palette = true;

            }

          }

 

          // If we have a palette attb, then one way or another it'll be

          // added to the palette

 

          if (palette)

          {

            // Create our button and give it a position

 

            var b = new WinForms.Button();

            b.SetBounds(50, 40 * i, 100, 30);

 

            // If no command name was found, use the method name and call the

            // function directly in the session context

 

            if (String.IsNullOrEmpty(cmdName))

            {

              b.Text = m.Name;

              b.Click +=

                (s, e) =>

                {

                  var b2 = (WinForms.Button)s;

                  var mi = type.GetMethod(b2.Text);

                  if (mi != null)

                  {

                    // Use reflection to call the method with no arguments

 

                    mi.Invoke(this, null);

                  }

                };

            }

            else

            {

              // Otherwise we use the command name as the button text and

              // execute the command in the command context asynchronously

 

              b.Text = cmdName;

              b.Click +=

                async (s, e) =>

                {

                  var dm = Application.DocumentManager;

                  var doc2 = dm.MdiActiveDocument;

                  if (doc2 == null) return;

 

                  // We could also use SendStringToExecute for older versions

 

                  // doc2.SendStringToExecute(

                  //   "_." + cmdName + " ", false, false, true

                  // );

 

                  var ed2 = doc2.Editor;

 

                  await dm.ExecuteInCommandContextAsync(

                    async (obj) =>

                    {

                      await ed2.CommandAsync("_." + cmdName);

                    },

                    null

                  );

                };

            }

            bs.Add(b);

            i++;

          }

        }

 

        // Create a user control and add all our buttons to it

 

        var uc = new WinForms.UserControl();

        uc.Controls.AddRange(bs.ToArray());

 

        // Create a palette set and add a palette containing our control

 

        _ps = new PaletteSet("PC", new Guid("87374E16-C0DB-4F3F-9271-7A71ED921566"));

        _ps.Add("CMDPAL", uc);

        _ps.MinimumSize = new Size(200, (i + 1) * 40);

        _ps.DockEnabled = (DockSides)(DockSides.Left | DockSides.Right);

      }

 

      _ps.Visible = true;

    }

 

    // Helper function to display a message and post a command prompt

    // (if there's an active document available)

 

    private static void DisplayMessage(string str, bool postPrompt = true)

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      doc.Editor.WriteMessage("\n{0}\n", str);

      if (postPrompt)

        doc.Editor.PostCommandPrompt();

    }

 

    // A bunch
of test methods and a single proper AutoCAD command

    // (it's a bit misleading to think of the

    [PaletteMethod]

    public void Method1()

    {

      DisplayMessage("Method 1");

    }

 

    [PaletteMethod]

    public void Method2()

    {

      DisplayMessage("Method 2");

    }

 

    [PaletteMethod]

    public void Method3()

    {

      DisplayMessage("Method 3");

    }

 

    [PaletteMethod]

    public void Method4()

    {

      DisplayMessage("Method 4");

    }

 

    [PaletteMethod]

    [CommandMethod("TEST")]

    public static void CommandTest()

    {

      DisplayMessage("This is a command!", false);

    }

  }

}

 

I'm really not sure if this will be of use to other people, but I certainly expect to use it from time to time to test problems that specifically need to be called from the session context to reproduce and diagnose. It'll certainly save the effort of generating a UI behind which to place the code.

13 responses to “Creating an AutoCAD palette dynamically to launch commands and methods tagged using .NET”

  1. Hi Kean. You mention a "previous post" but forgot to link to it. Thanks.

    1. Fixed, thanks!

      Kean

  2. Hi Kean, more good stuff as usual, thx for your continuing helpful blog. I did notice at the top when you said "this post", its not a blue link. I only thought to mention in case the editor you are using is not showing them as I guess it was composed as a link. thx

    1. Fixed, thx!

      Kean

  3. Hello Kean Walmsley,

    I have used palette. I need when my plugin palette load it should be a by default Left and side dock. I have try to assigned many property but none of them is working.

    Do you have way to achive this functionality?

    Parthiv

    1. Hi Parthiv,

      Please post your support requests to the AutoCAD .NET Discussion Forum.

      Regards,

      Kean

      1. Thanks I have posted in Forum..

  4. HELLO KEAN THIS WORKER HAS A FILE CALLED I COULD HAVE ANY BUSINESS INTERRUPTION IF THIS I HAVE TOO MUCH LESS THAN YOU CAN cihanbak2177@gmail.com

  5. I tried to code the error you could mail it to me cihanbaki2147@gmail.com ffile

  6. EL AHMADI ISSAM Avatar
    EL AHMADI ISSAM

    Hi Kean. How to Create an AutoCAD "Panel" just next to the " BIM360 , Express Tools .. etc "
    Thank You

    1. As mentioned via LinkedIn, please use other support channels (such as the AutoCAD .NET forum).

      1. EL AHMADI ISSAM Avatar
        EL AHMADI ISSAM

        Absolutely, Got it, thanks for your time

Leave a Reply to EL AHMADI ISSAM Cancel reply

Your email address will not be published. Required fields are marked *