Different ways to display balloon notifications in AutoCAD using .NET

In this previous post I showed some code to display balloon notifications via InfoCenter in AutoCAD 2009 (and, it seems, in AutoCAD 2008, albeit with a different look & feel).

In a comment on that post, I promised to take a look at another way to show user notification balloons in AutoCAD, by displaying balloons (or maybe it's "blowing bubbles"? :-) on the application status bar. It's really up to you to decide which style of balloon is more appropriate for your application notifications... I personally find the InfoCenter balloons less intrusive (and more modern-looking), but the choice is yours. The duration during which the balloons are displayed is also controlled differently: the status bar "tray settings" controls the status bar balloons, the duration of the InfoCenter balloons is decided more directly by the application.

From a programmatic perspective, status bar balloon notifications are also different in nature from their InfoCenter equivalents: we need to create a balloon window and have a status bar tray item display it.

In the below code we create a tray item on the fly, just for the purposes of displaying the balloon, but you may actually have your own tray icon that you provide for your users to access your application features & settings (which would be a great topic for a follow-up post, thinking about it).

Rather than me creating and providing a custom icon, this code takes the lazy option and picks up the first icon that is available in the current document's status bar (which happens to be the annotation scaling icon, on my system at least). The right way to do this is to create a custom icon file (or resource) and load it into a System.Drawing.Icon. This is left as an exercise for the reader or to be addressed in a future post (we'll see :-).

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.AcInfoCenterConn;

using Autodesk.AutoCAD.Windows;


namespace BalloonsAndBubbles

{

  public class Commands

  {

    const string title =

      "Custom Application Notification";

    const string hlink =

      "http://blogs.autodesk.com/through-the-interface";

    const string htext =

      "Link to Kean's blog";

    const string msg =

      "Kean has some information for you...";

    const string msg2 =

      "Some additional text";


    [CommandMethod("icb")]

    public void infoCenterBalloon()

    {

      InfoCenterManager icm =

        AcInfoCenterConn.InfoCenterManager;


      Autodesk.InfoCenter.PaletteMgr pm =

        icm.PaletteManager;


      pm.ShowBalloon(

        title,

        msg,

        null, // Don't provide an icon

        new System.Uri(hlink),

        5,    // Show the balloon for 5 seconds

        1    // Make it relatively slow to fade in

      );

    }


    [CommandMethod("sbb")]

    public void statusBarBalloon()

    {

      const string appName =

        "Kean's application";


      Document doc =

        Application.DocumentManager.MdiActiveDocument;


      TrayItem ti = new TrayItem();

      ti.ToolTipText = appName;

      ti.Icon =

        doc.StatusBar.TrayItems[0].Icon;


      Application.StatusBar.TrayItems.Add(ti);


      TrayItemBubbleWindow bw =

        new TrayItemBubbleWindow();

      bw.Title = title;

      bw.HyperText = htext;

      bw.HyperLink = hlink;

      bw.Text = msg;

      bw.Text2 = msg2;

      bw.IconType = IconType.Information;


      ti.ShowBubbleWindow(bw);

      Application.StatusBar.Update();


      bw.Closed +=

        delegate(

          object o,

          TrayItemBubbleWindowClosedEventArgs args

        )

        {

          // Use a try-catch block, as an exception

          // will occur when AutoCAD is closed with

          // one of our bubbles open


          try

          {

            Application.StatusBar.TrayItems.Remove(ti);

            Application.StatusBar.Update();

          }

          catch

          {}

        };

    }

  }

}

The ICB command is basically the same as the one shown in the previous post (with just the strings factored out into constants that are shared with the new command), and displays a notification like this from the top right of the screen (or wherever you've configured your InfoCenter to display):

Custom balloon notification

The SBB command shows a balloon notification at the bottom right of the application frame:

Status bar balloon notification

As a temporary tray item is created for each notification, if you call the SBB command repeatedly without closing the balloons, you'll see one tray item per notification. This may or may not be what you want, of course, and it's easy enough to adjust the behaviour.

Status bar balloon notifications

A note about the code in the SBB command: after watching the C# Whirlwind on Anonymous Methods, I implemented the "closed" event handler as an inline delegate. This code is compatible with Visual Studio 2005 and onwards, so users of Visual Studio .NET 2002 and 2003 will have to modify this. This callback removes the temporary tray item we added to the status bar.

A word of warning about a little quirk I found: if you run the SBB command twice in a row, without calling another command in-between, the notification that is displayed may lose its border:

Status bar balloon notification - no border

Now this doesn't happen if you run another command in-between, so I suspect this is a non-issue for two reasons: the balloon functions just fine (it just doesn't look as pretty), and it's very unlikely you'll want to spam your users with repeated balloon notifications from consecutive commands, so it's unlikely to occur in the first place. Which is probably why this hasn't been spotted before.

April 28, 2008 in AutoCAD, AutoCAD .NET, User interface | Permalink | Comments (3) | TrackBack

The story of the (Office 2007) Ribbon

On the theme of the Ribbon, whose API in AutoCAD we introduced in this previous post...

The other day I came across this interesting post, which talks about the design history culminating in the Office 2007 ribbon. The post focuses on product design rather than programming, but it's very interesting to see the story behind this significant user-interface innovation, especially as Autodesk has adopted the ribbon paradigm in AutoCAD 2009.

April 18, 2008 in AutoCAD, User interface | Permalink | Comments (0) | TrackBack

Showing a balloon notification using the InfoCenter API in AutoCAD 2009

This post is the latest in the series of closer looks at the new APIs in AutoCAD 2009. It dips into the InfoCenter API, a .NET API allowing you to customize and drive the InfoCenter feature inside AutoCAD.

To make use of this API you need to add Project References to two managed assemblies from the AutoCAD 2009 root folder: AcInfoCenterConn.dll and AdInfoCenter.dll.

Here's some C# code that will display a balloon notification to your users:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.AcInfoCenterConn;


namespace InfoCenterApp

{

  public class Commands

  {

    [CommandMethod("icb")]

    public void infoCenterBalloon()

    {

      InfoCenterManager icm =

        AcInfoCenterConn.InfoCenterManager;


      Autodesk.InfoCenter.PaletteMgr pm =

        icm.PaletteManager;


      pm.ShowBalloon(

        "Custom Application Notification",

        "Kean has some information for you...",

        null, // Don't provide an icon

        new System.Uri(

          "http://blogs.autodesk.com/through-the-interface"

        ),

        5,   // Show the balloon for 5 seconds

        1    // Make it relatively slow to fade in

      );

    }

  }

}

Here's what you see when you run the ICB command:

Custom balloon notification

April 16, 2008 in AutoCAD, AutoCAD .NET, User interface | Permalink | Comments (5) | TrackBack

The New RibbonBar API in AutoCAD 2009

Thank you to Sreekar Devatha, from DevTech India, for writing this article for the recently published ADN Platform Technologies Customization Newsletter. This article talks about the new Ribbon API referenced in this overview of the new APIs in AutoCAD 2009. A complete sample demonstrating the use of this API is provided as part of the ObjectARX 2009 SDK, under samples/dotNet/Ribbon.

Introduction

Most of the AutoCAD® UI was redesigned in this release. Ribbon, Menu browser and Tooltips are some of the prominent UI features to list. As you might already know the UI enhancements are based on the new Windows® Presentation Foundation (WPF) programming model introduced by Microsoft. So, let’s start with a small introduction to WPF and then we'll move on to the finer points of customizing the Ribbon bar.

What is WPF?

Windows Presentation Foundation (WPF) is a programming model introduced by Microsoft to build rich Windows client applications.

This graphical subsystem introduced in .NET Framework 3.0 provides a clear separation between appearance and behavior of applications. You generally use eXtensible Application Markup Language (XAML) to implement the appearance of an application while using managed programming languages (code-behind) to implement its behavior. XAML is the new XML-based UI definition language from Microsoft, and as such is a core part of WPF.

Without wasting too much time on WPF let us move quickly on to the Ribbon APIs. If you are new to WPF then you could go through the basics of WPF using the links below before starting with the Ribbon APIs.

Windows Presentation Foundation - MSDN

Windows Presentation Foundation - CodeProject

Microsoft WindowsClient.NET

AutoCAD Ribbon

Before diving into the Ribbon APIs it's necessary to understand the Ribbon layout and its terminology which are covered in this and the following section.

The AutoCAD Ribbon provides a single, compact placement for operations that are relevant to the current workspace. It overcomes the need to display multiple toolbars, reducing clutter in the application window. The Ribbon maximizes the area available for work using a single compact interface.

The Ribbon was built using WPF (part of .NET Framework 3.0) and a comprehensive set of APIs have been provided by Autodesk to help external developers customize it.

Ribbon Layout

The different components of the AutoCAD Ribbon are shown below. Also, depicted in the snapshot are the classes corresponding to each component.

Ribbon layout

Figure: the AutoCAD Ribbon and its layout

The Ribbon control is the top level control which contains everything in the Ribbon. It is composed of a series of panels, which are organized into tabs labeled by task.

Ribbon tabs control the display and order of Ribbon panels on the Ribbon. You add Ribbon tabs to a workspace to control which Ribbon tabs are displayed on the Ribbon. Ribbon tabs do not contain any commands or controls like a Ribbon panel does; instead, they manage the display of Ribbon panels on the Ribbon. Once a Ribbon tab is created, a panel can then be added to it. Ribbon tabs are of two types, standard and contextual. Standard tabs are always displayed while contextual tabs are displayed based on a particular context: for instance a Block Editor Tab is displayed while editing a Block.

Contextual tabs can appear in two modes:

Replace mode: In this mode the contextual tab gets added to the standard tabs as a regular tab. When the contextual tab is clicked it becomes active and the panels in the contextual tabs replace the panels in the previously active tab.

Append mode: In this mode contextual tabs form another tab set similar to the standard tab set and is displayed side-by-side with the standard tabs and panels. There are two tabs active any time and activating a tab in one tab set does not affect active tab in the other tab set.

Ribbon panels are organized by rows, sub-panels, and panel separators. Rows and sub-panels are used to organize how commands and controls are displayed on the Ribbon panel. A row, similar to a toolbar, determines the order and position that commands and controls appear on the Ribbon panel. Rows run horizontally on a Ribbon panel. If all the commands and controls cannot be displayed on the Ribbon panel, a gray down arrow is displayed for expanding the Ribbon panel. Rows can be divided using a sub-panel which, holds rows to order and position commands and controls. Commands and controls can be added to rows and sub-panels, you can remove the commands and controls that you use infrequently, and rearrange the order of commands and controls. Along with commands and controls, you can also create flyouts that contain multiple commands and only take up the space of a single command.

Prerequisites
Modules, Namespaces & Classes

The core UI framework for AutoCAD is present in AdWindows and AcRibbon contains the Ribbon specific implementation. These are managed UI class libraries developed using .NET 3.0 and WPF. Only .NET APIs are available and no C++ wrappers are provided.

AdWindows.dll

This library implements the framework for the following Autodesk UI features.

  • Ribbon classes
  • Autodesk controls
  • Tooltips
  • Menu browser
  • Task dialog, etc.

These are the Ribbon-specific classes under the Autodesk.Windows namespace of this DLL.

  • RibbonControl
  • RibbonTab
  • RibbonPanel
  • RibbonPanelSource
  • RibbonRow
  • RibbonItem
  • RibbonButton
  • RibbonDropDownButton
  • RibbonSeperator
  • RibbonForm
  • RibbonHwnd
  • RibbonRowPanel, etc.

For more details regarding these classes refer the ObjectARX® Managed Reference guide available in the ObjectARX 2009 SDK.

AcRibbon.dll

This library was actually meant to be an internal-only DLL except for the very few APIs which are  discussed in this article below. All other APIs included in the DLL should be considered as internal-only.

Classes

  • Palette that hosts the AutoCAD Ribbon control
    • Autodesk.AutoCAD.Ribbon.RibbonPaletteSet

  • AutoCAD Ribbon control
    • Autodesk.AutoCAD.Ribbon.RibbonServices. RibbonPaletteSet.RibbonControl

Properties

  • Property to access the default Ribbon host window which is a palette
    • Autodesk.AutoCAD.Ribbon.RibbonServices. RibbonPaletteSet

Note: You are advised not to use any of the internal APIs as they are unsupported and could be changed or dropped without prior notice.

Custom Ribbon Tab

As discussed above in the Ribbon Layout section, we need to create panels with Ribbon items placed on them. Then, these panels should be categorized based on their usage and hosted on your application-specific Ribbon tabs. To demonstrate this we'll now look into the finer points of the API by adding a simple button to a panel and then host the panel on a tab (Custom Tab).

Button

In this section we'll add a button to the ribbon bar. If we take a look at the classes listed above we have the RibbonButton class which can be used to create a button to be placed on the Ribbon. So, let’s start with the creation of a RibbonButton instance as below:

RibbonButton button = new RibbonButton();

button.Text = "Click Me";

// resourceDictionary

// A XAML resource dictionary that defines a ButtonImage

button.LargeImage =

  resourceDictionary["ButtonImage"] as BitmapImage;

button.Orientation = Orientation.Vertical;

button.Size = RibbonItemSize.Large;

button.ShowText = true;

button.ShowImage = true;

button.Id = "ClickMe_1";

Now, this button instance should be placed on a panel that can then be hosted by a tab. But before actually creating the panel we need a row in which to place the button, as discussed earlier.

// Create a Row to add the RibbonButton

RibbonRow row = new RibbonRow();

row.Items.Add(button);

// Create a Ribbon panel source in which to

// place ribbon items

RibbonPanelSource panelSource =

  new RibbonPanelSource();

panelSource.Title = "Custom Panel";

panelSource.Rows.Add(row);

// Create a panel for holding the panel

// source content

RibbonPanel panel = new RibbonPanel();

panel.Source = panelSource;

The panel should be hosted on the tab which in turn should be added to the Ribbon control and the equivalent code to achieve this is below:

// Create a tab to manage the above panel

RibbonTab tab = new RibbonTab();

tab.Title = "Custom Tab";

tab.Id = "CustomTab";

tab.IsContextualTab = false;

tab.Panels.Add(panel);

// Now add the tab to AutoCAD Ribbon bar...

RibbonControl ribbonControl =

  Autodesk.AutoCAD.Ribbon.RibbonServices.

    RibbonPaletteSet.RibbonControl;

ribbonControl.Tabs.Add(tab);

// ... and activate the tab

ribbonControl.ActiveTab = tab;

The below snapshot shows the button added to AutoCAD's Ribbon.

Custom ribbon panel inside AutoCAD 2009

Figure: the button added to the Ribbon bar

One more item that was missing in the above code was an event to identify the click of the button. The following code implements the click event.

button.Click += new RoutedEventHandler(button_Click);

private static void button_Click(

  object sender, RoutedEventArgs e)

{

  RibbonButton button = sender as RibbonButton;

  if (button != null && (button.Id == "ClickMe_1")

  {

    MessageBox.Show("Click Me clicked ", "Click Me");

    e.Handled = true;

  }

}

The above code might also be implemented using a combination of XAML and C# code-behind as shown below.

XAML that defines the RibbonTab

  <adw:RibbonTab

    x:Key="TabXaml" Title="Custom Tab XAML" Id="CustomTabXaml">

    <adw:RibbonPanel >

      <adw:RibbonPanelSource Title="Custom Panel XAML" >

        <!--Add a ribbon row-->

        <!--Note: You could add only rows

                  to the panel source content-->

        <adw:RibbonRow x:Uid="adw:RibbonRow_1">

          <!--Add Ribbon Items here-->

          <!--The items could be any RibbonItem derived classes-->

          <!--Like RibbonButton

              RibbonDropDownButton,

              RibbonForm,

              RibbonHwnd,

              RibbonLabel

              RibbonMenuButton,

              RibbonRowPanel,

              RibbonSeperator,

              RibbonToggleButton

              or any RibbonItem derived custom controls-->

          <adw:RibbonButton Id="ClickMe_2" ShowText="true">

            <adw:RibbonButton.Orientation>

              <Orientation>

                Vertical

              </Orientation>

            </adw:RibbonButton.Orientation>

            <adw:RibbonButton.Image>

              <BitmapImage

                UriSource="Images/bitmap1.bmp"/>

            </adw:RibbonButton.Image>

            <adw:RibbonButton.LargeImage>

              <BitmapImage

                UriSource="Images/bitmap1.bmp"/>

            </adw:RibbonButton.LargeImage>

            <adw:RibbonButton.Size>

              <adw:RibbonItemSize>

                Large

              </adw:RibbonItemSize>

            </adw:RibbonButton.Size>

            <adw:RibbonButton.Text>

              Click Me

            </adw:RibbonButton.Text>

            <adw:RibbonButton.ToolTip>

              <src:RibbonToolTip

                BasicText = "Click Me basic help"

                CommandName = "ClickMe"

                ExtendedURISource =

                  "/MyRibbon;component/Dictionary1.xaml"

                ExtendedURISourceKey = "ClickMe_ToolTip"

                HelpSource = "./Help/readme.chm"

                HelpTopic =

                  "WS1a9193826455f5ff1dbc298511635bea8752e2f"/>

            </adw:RibbonButton.ToolTip>

          </adw:RibbonButton>

        </adw:RibbonRow>

      </adw:RibbonPanelSource>

    </adw:RibbonPanel>

  </adw:RibbonTab>

C# code-behind to add a button to the ribbon bar using the tab defined in XAML

[CommandMethod("AddButtonXAML")]

public static void AddButtonXAML()

{

  // Create a RibbonTab using the resourceDictionary

  RibbonTab tab =

    resourceDictionary["TabXaml"] as RibbonTab;

  // Find the ribbon button and add the event

  RibbonRow row = tab.Panels[0].Source.Rows[0];

  RibbonItemCollection coll = row.Items;

  foreach (RibbonItem item in coll)

  {

    if (item is RibbonButton)

    {

      RibbonButton button = (RibbonButton)item;

      if (button.Id == "ClickMe_2")

      {

        button.Click +=

          new RoutedEventHandler(button_Click);

      }

    }

  }

  // Now add the tab to AutoCAD Ribbon bar and activate it

  ribbonControl.Tabs.Add(tab);

  ribbonControl.ActiveTab = tab;

}

ToolTip

The next thing you would want to do once you add your objects to the Ribbon bar is to display a tooltip for these objects.

The ToolTip property of the RibbonItem class accepts an object so, we could assign a control object to it to display the control’s content as a tooltip. In this example here we define a Grid control. The control intern uses the Autodesk.Windows.ProgressivePanel class to implement the extended tooltip feature that is available with the AutoCAD tooltips.

XAML

<Grid x:Key="ClickMe_ToolTip">

  <StackPanel>

    <!--Header Part-->

    <StackPanel Orientation="Horizontal" Margin="5,5,5,5">

      <TextBlock Text="ClickMe">

        <TextBlock.FontWeight>

          <FontWeight>

            Bold

          </FontWeight>

        </TextBlock.FontWeight>

      </TextBlock>

    </StackPanel>

    <!--Basic help information -->

    <StackPanel Margin="5,5,5,5">

      <TextBlock

        Text="This is basic help of click me command">

        <TextBlock.TextWrapping>

          <TextWrapping>

            Wrap

          </TextWrapping>

        </TextBlock.TextWrapping>

      </TextBlock>

    </StackPanel>

    <!--Extended help information -->

    <adw:ProgressivePanel Margin="5,5,5,5">

      <StackPanel/>

      <!--Click Me Extended Tooltip-->

      <Grid>

        <StackPanel Orientation="Vertical" Margin="0,0,0,0">

          <TextBlock>

            Click Me extended ToolTip

          </TextBlock>

          <Image Margin="40,10,0,0" 

            Width="150" Height="150" 

            Source="/MyRibbon;component/Images/Smiley.png" />

        </StackPanel>

      </Grid>

    </adw:ProgressivePanel>

    <!-- Footer Part -->

    <Line Stroke="Black" StrokeThickness="2" X2="250"/>

    <StackPanel Orientation="Horizontal" Margin="5,5,5,5">

      <Grid VerticalAlignment="Center"

            HorizontalAlignment="Left">

        <Grid.ColumnDefinitions>

          <ColumnDefinition Width="21" />

          <ColumnDefinition Width="179" />

        </Grid.ColumnDefinitions>

        <Image HorizontalAlignment="Left" Grid.Column="0"

              Width="16" Height="16">

          <Image.Source>

            /MyRibbon;component/Images/Help.gif

          </Image.Source>

        </Image>

        <TextBlock HorizontalAlignment="Left" Grid.Column="1"

                  FontWeight="Bold">

          Press F1 for more help

        </TextBlock>

      </Grid>

    </StackPanel>

  </StackPanel>

</Grid>

button.ToolTip = resourceDictionary["ClickMe_ToolTip"];

We can do away with this statement above if we define the button in the XAML file by adding the tooltip to RibbonButton in the XAML as below:

<adw:RibbonButton.ToolTip>

  <!-- Define tooltip here, above XAML without

      x:Key value could be used -->

</adw:RibbonButton.ToolTip>

Here's a snapshot of the extended tooltip:

Custom extended tooltip in AutoCAD 2009

Figure: Ribbon object tooltip

Although we can display tooltip using a control as done above, we will not be able to implement the F1 event-handling mechanism using this approach. The ToolTip UI controls like Autodesk.Windows.ToolTip or System.Windows.Controls.ToolTip with F1 event handlers will not help us here because the Ribbon bar does not accept them similar to the way we could not use the Button class to add a button to the Ribbon bar. This particular feature could easily run into an article in itself, so we'll stop at this point to continue in a future article.

April 9, 2008 in AutoCAD, AutoCAD .NET, User interface | Permalink | Comments (1) | TrackBack

Embedding AutoCAD 2009 in a standalone dialog

This post takes a look at another topic outlined in this overview of the new API features in AutoCAD 2009.

AutoCAD 2009 introduces the ability to embed the application in a standalone dialog or form via an ActiveX control. This capability has been around for a number of releases of AutoCAD OEM, but this feature has now been made available in the main AutoCAD product.

The way the control works is to launch an instance of AutoCAD in the background (it should go without saying that AutoCAD needs to be installed on the system, but I've said it, anyway :-) and it then pipes the graphics generated by AutoCAD into the area specified by the bounds of the control. It also then pipes back any mouse movements or keystrokes, to allow the embedded AutoCAD to be controlled. It's pretty neat: you'll see the standard cursor, be able to enter commands via dynamic input, and more-or-less do whatever can be done inside the full product.

The control is especially handy if you want to present a reduced user-interface to the people using the product (which is really what AutoCAD OEM is for, in a nutshell, although the development effort involved in creating a full AutoCAD OEM application makes it inappropriate for quick & easy UI streamlining).

Let's start our look at this control by creating a new C# Windows Application project in Visual Studio 2005 (you can use whatever ActiveX container you like, though - it should even work from a web-page or an Office document):

New application project

Once Visual Studio has created the new project, we need to add our control to the toolbox. If you right-click on the toolbox, you'll be able to select "Choose Items...".

Add to toolbox

From here, there should be an item "AcCtrl" in the list of COM Components. Otherwise you can browse to it in c:\Program Files\Common Files\Autodesk Shared\AcCtrl.dll.

Add control to toolbox

Then you simply need to place the control on your form.

Add control to form

Once we've done that, we're going to add a few more controls - for the drawing path, and a text string for commands we want to try "posting" to the embedded AutoCAD application.

Design our form

Here's the C# code we'll use to drive the embedded control from the form. You should be able to work out what the various controls have been called in the project by looking at the code.

using System;

using System.Windows.Forms;


namespace EmbedAutoCAD

{

  public partial class MainForm : Form

  {

    public MainForm()

    {

      InitializeComponent();

    }


    private void browseButton_Click(

      object sender, EventArgs e)

    {

      OpenFileDialog dlg =

        new OpenFileDialog();

      dlg.InitialDirectory =

        System.Environment.CurrentDirectory;


      dlg.Filter =

        "DWG files (*.dwg)|*.dwg|All files (*.*)|*.*";


      Cursor oc = Cursor;


      String fn = "";


      if (dlg.ShowDialog() ==

        DialogResult.OK)

      {

        Cursor = Cursors.WaitCursor;

        fn = dlg.FileName;

        Refresh();

      }

      if (fn != "")

        this.drawingPath.Text = fn;


      Cursor = oc;

    }

    private void loadButton_Click(

      object sender, EventArgs e)

    {

      if (System.IO.File.Exists(drawingPath.Text))

        axAcCtrl1.Src = drawingPath.Text;

      else

        MessageBox.Show("File does not exist");

    }


    private void postButton_Click(

      object sender, EventArgs e)

    {

      axAcCtrl1.PostCommand(cmdString.Text);

    }

  }

}

Finally, when we run the application and load a drawing via the browse/load buttons, the real fun starts. :-)

Embedded AutoCAD 2009

Try entering commands via dynamic input, or via the "Post a command" textbox. You might feel a little disorientated due to the lack of a command-line (I do love my command-line ;-), but dynamic input allows you to at least see what you're typing.

Here's the C# project for you to download.

March 27, 2008 in AutoCAD, AutoCAD .NET, User interface | Permalink | Comments (0) | TrackBack

Implementing your own AutoCAD color combobox control using .NET

A big thanks to Scott McFarlane, from Geotropix, Inc., for sharing the code in this post. Here's an email I received from Scott:

I was reading this blog entry on “Through the Interface” and some folks were asking about how to implement .NET combo box versions of the color and linetype ActiveX controls that are available. I just wanted to share a simple .NET implementation of a color combo box. The color combo is quite easy, really. The linetype one would be more difficult.

Attached is the source code. This is just a generic color combo, that loads up with the 255 ACI colors. It has no dependency on AutoCAD – I was actually using this in an external program. It would be easy, however, to modify this to provide a list item to launch the built-in AutoCAD color dialog if used inside AutoCAD.

Here is the C# code Scott provided (which was in a source file named AcColorComboBox.cs):

using System;

using System.Collections;

using System.Drawing;

using System.Windows.Forms;


public class AcColorComboBox : ComboBox

{

  public class ColorItem

  {

    private short _colorIndex;

    private Color _color;


    public ColorItem(short colorIndex, Color color)

    {

      _colorIndex = colorIndex;

      _color = color;

    }


    public short ColorIndex

    {

      get { return _colorIndex; }

    }


    public Color Color

    {

      get { return _color; }

    }


    public override string ToString()

    {

      return AcColorComboBox.ColorNameOf(_colorIndex);

    }


  }


  public class ColorItemSorter : IComparer

  {

    public int Compare(object x, object y)

    {

      return ((ColorItem)x).ColorIndex - ((ColorItem)y).ColorIndex;

    }

  }


  private short _colorIndex;


  #region " Windows Form Designer generated code "


  public AcColorComboBox()

    : base()

  {

    // This call is required by the Windows Form Designer.

    InitializeComponent();


    // Add any initialization after the InitializeComponent() call

    DrawMode = DrawMode.OwnerDrawFixed;

    DropDownStyle = ComboBoxStyle.DropDownList;

  }


  // Override dispose to clean up the component list.

  protected override void Dispose(bool disposing)

  {

    if (disposing)

    {

      if ((components != null))

      {

        components.Dispose();

      }

    }

    base.Dispose(disposing);

  }


  // Required by the Windows Form Designer

  private System.ComponentModel.IContainer components;


  // NOTE: The following procedure is required by the Windows Form Designer

  // It can be modified using the Windows Form Designer.

  // Do not modify it using the code editor.

  [System.Diagnostics.DebuggerStepThrough()]

  private void InitializeComponent()

  {

    components = new System.ComponentModel.Container();

  }


  #endregion


  protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)

  {

    if (e.Index >= 0)

    {

      e.DrawBackground();

      e.DrawFocusRectangle();

      Rectangle r = e.Bounds;

      r.Inflate(-1, -1);

      r.Width = 20;

      r.Offset(1, 0);

      ColorItem objColor = (ColorItem)Items[e.Index];

      e.Graphics.FillRectangle(new System.Drawing.SolidBrush(objColor.Color), r);

      e.Graphics.DrawRectangle(new System.Drawing.Pen(Color.Black), r);

      e.Graphics.DrawString(objColor.ToString(), e.Font, new SolidBrush(e.ForeColor), e.Bounds.X + r.Width + 4, e.Bounds.Y);

    }

  }


  protected override void OnCreateControl()

  {

    Items.Clear();

    for (short i = 1; i < 256; i++)

      Items.Add(new ColorItem(i, ColorOf(i)));


    base.OnCreateControl();

  }


  // ColorValue represents colorId

  public short ColorIndex

  {

    get { return _colorIndex; }

    set

    {

      _colorIndex = value;

      foreach (ColorItem objColor in Items)

      {

        if (objColor.ColorIndex == value)

        {

          SelectedItem = objColor;

          break;

        }

      }

    }

  }


  protected override void OnSelectedIndexChanged(System.EventArgs e)

  {

    _colorIndex = ((ColorItem)Items[SelectedIndex]).ColorIndex;

    base.OnSelectedIndexChanged(e);

  }


  public static string ColorNameOf(short colorIndex)

  {

    switch (colorIndex)

    {

      case 1:

        return "1 - Red";

      case 2:

        return "2 - Yellow";

      case 3:

        return "3 - Green";

      case 4:

        return "4 - Cyan";

      case 5:

        return "5 - Blue";

      case 6: