In this previous post we looked at creating a simple modal dialog and using it to display object properties. This post looks at the structural changes you need to make to your application for the same dialog to be used modelessly. In a later post we'll look at the benefits you get from leveraging the Palette system for modeless interaction inside AutoCAD.
Firstly, let's think about the interaction paradigm needed by a modeless dialog. A few things come to mind:
- There is no longer a need to hide and show the dialog around selection
- Rather than asking the user to select an entity, it's neater to respond to standard selection events
- We no longer need a "browse" button
- We now need to be more careful about document access
- Our command automatically locked the document (and had sole access) in the modal example
- We should now lock it "manually" when we access it
So we can already simplify our dialog class - here's some modified C# code, with the browse button removed and document-locking in place:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace CustomDialogs
{
public partial class TypeViewerForm : Form
{
public TypeViewerForm()
{
InitializeComponent();
}
public void SetObjectText(string text)
{
typeTextBox.Text = text;
}
public void SetObjectId(ObjectId id)
{
if (id == ObjectId.Null)
{
SetObjectText("");
}
else
{
Document doc =
Autodesk.AutoCAD.ApplicationServices.
Application.DocumentManager.MdiActiveDocument;
DocumentLock loc =
doc.LockDocument();
using (loc)
{
Transaction tr =
doc.TransactionManager.StartTransaction();
using (tr)
{
DBObject obj = tr.GetObject(id, OpenMode.ForRead);
SetObjectText(obj.GetType().ToString());
tr.Commit();
}
}
}
}
private void closeButton_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
So which event should we respond to, to find out when objects are selected? In this case I chose a PointMonitor - this class tells you a lot of really useful information about the current selection process. It also has the advantage of picking up the act of hovering over objects - no need for selection to actually happen. One other fun option would have been to use a Database event (ObjectAppended) to display information about objects as they are added to the drawing.
A few other comments about the code:
- Predictably enough we now use ShowModelessDialog rather than ShowModalDialog()
- We have our form as a member variable of the class, as its lifespan goes beyond the command we use to show it
- I've also removed the selection code; we're no longer asking for objects to be selected
Here's the updated command implementation:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System;
using CustomDialogs;
namespace CustomDialogs
{
public class Commands
{
TypeViewerForm tvf;
public Commands()
{
tvf = new TypeViewerForm();
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
ed.PointMonitor +=
new PointMonitorEventHandler(OnMonitorPoint);
}
~Commands()
{
try
{
tvf.Dispose();
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
ed.PointMonitor -=
new PointMonitorEventHandler(OnMonitorPoint);
}
catch(System.Exception)
{
// The editor may no longer
// be available on unload
}
}
private void OnMonitorPoint(
object sender,
PointMonitorEventArgs e
)
{
FullSubentityPath[] paths =
e.Context.GetPickedEntities();
if (paths.Length <= 0)
{
tvf.SetObjectId(ObjectId.Null);
return;
};
ObjectId[] objs = paths[0].GetObjectIds();
if (objs.Length <= 0)
{
tvf.SetObjectId(ObjectId.Null);
return;
};
// Set the "selected" object to be the last in the list
tvf.SetObjectId(objs[objs.Length - 1]);
}
[CommandMethod("vt",CommandFlags.UsePickSet)]
public void ViewType()
{
Application.ShowModelessDialog(null, tvf, false);
}
}
}
And here's the source project for this version of the application. When you run the application you may experience issues with the dialog getting/retaining focus - this is generally a problem with modeless dialogs that has been addressed automatically by the Palette class, something we'll take a look at in a future post.