明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
12
返回列表 发新帖

[Kean专集] Kean专题(10)—Notification_Events

   关闭 [复制链接]
 楼主| 发表于 2009-6-13 16:56 | 显示全部楼层
November 21, 2008
Adding a custom tab to AutoCAD's options dialog using .NET - Part 2
This post extends the approach shown in this previous post to implement a realistic editing and storage mechanism for application settings. It uses the .NET PropertyGrid control to display a custom class, allowing editing of a number of properties. This class is also serializable, which means we can use the .NET Framework to save it out to an XML file on disk. Some readers may have their own approaches to saving custom application settings, whether in the Registry or elsewhere: this post is primarily about displaying properties rather than providing a definitive "how to" for storing custom application settings. I chose a path of relatively low resistance, which will hopefuly prove interesting to some of the people reading the post.
MSDN contains a useful page on implementing the PropertyGrid in your project, but there are lots of other helpful pages you'll find on The Code Project and other sites.
Here's a project containing the code from this post, in case you'd prefer not to create it yourself.
As in the first part of the series, we need to add a User Control to our project. Within this control we'll add a single PropertyGrid, drawn to the full extents of the control (I found that drawing it to fill the container and then setting "Anchor" to "Top, Bottom, Left, Right" worked better than setting "Dock" to "Fill"). Thinking about it, it would probably work just to create a PropertyGrid in code and pass that into the constructor of the TabbedDialogExtension object, but doing it this way allows us to make use of the designer to play around with the control's properties at design-time, rather than making the settings dynamically at runtime.
Here's an idea of what the design should look like of our user control containing the property grid (nothing very impressive or exciting, at this stage - I'm basically just including it for completeness :-):

I customized the layout of the PropertyGrid somewhat - modifying the font and the background colour of the categories - but you will see that from the below snapshots or from the sample project.
Next we need to add some code. Here's the code behind this control, where we use the "value changed" event to signal that our tab's data is "dirty" and may require saving:
  1. using System.Windows.Forms;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. namespace OptionsDlg
  4. {
  5.   public partial class OptionsTabControl : UserControl
  6.   {
  7.     public OptionsTabControl()
  8.     {
  9.       InitializeComponent();
  10.     }
  11.     private void propertyGrid_PropertyValueChanged(
  12.       object sender,
  13.       System.Windows.Forms.PropertyValueChangedEventArgs e
  14.     )
  15.     {
  16.       TabbedDialogExtension.SetDirty(this, true);
  17.     }
  18.   }
  19. }
Here's the code for the rest of our application's implementation (stored in a separate .cs file - I called mine Application.cs, although you might prefer to split it into AppSettings.cs and Initialization.cs):
  1. using System;
  2. using System.Web.UI;
  3. using System.IO;
  4. using System.Xml.Serialization;
  5. using System.ComponentModel;
  6. using Autodesk.AutoCAD.ApplicationServices;
  7. using Autodesk.AutoCAD.EditorInput;
  8. using Autodesk.AutoCAD.Runtime;
  9. [assembly:
  10.   ExtensionApplication(
  11.     typeof(OneNeedsOptions.Initialization)
  12.   )
  13. ]
  14. namespace OneNeedsOptions
  15. {
  16.   public enum Fruit
  17.   {
  18.     Orange,
  19.     Banana,
  20.     Strawberry,
  21.     Apple
  22.   }
  23.   [Serializable(),
  24.    DefaultProperty("Name")
  25.   ]
  26.   public class AppSettings
  27.   {
  28.     // Our internal properties
  29.     private string _name = "Kean Walmsley";
  30.     private string _url =
  31.       "http://blogs.autodesk.com/through-the-interface";
  32.     private DateTime _birthday = new DateTime(1912, 7, 14);
  33.     private Fruit _fruit = Fruit.Strawberry;
  34.     // Their external exposure and categorization/description
  35.     [Description("The person's name"),
  36.      Category("Identity")
  37.     ]
  38.     public string Name
  39.     {
  40.       set { _name = value; }
  41.       get { return _name; }
  42.     }
  43.     [Description("The blog written by this person"),
  44.     Category("Stuff I do"),
  45.     UrlProperty()
  46.     ]
  47.     public string Blog
  48.     {
  49.       set { _url = value; }
  50.       get { return _url; }
  51.     }
  52.     [Description("The day this person was born"),
  53.      Category("Identity")
  54.     ]
  55.     public DateTime Birthday
  56.     {
  57.       set { _birthday = value; }
  58.       get { return _birthday; }
  59.     }
  60.     [Description("The person's age"),
  61.      Category("Identity"),
  62.      ReadOnly(true)
  63.     ]
  64.     public int Age
  65.     {
  66.       get
  67.       {
  68.         return
  69.           (int)((DateTime.Now - _birthday).Days / 365.25);
  70.       }
  71.     }
  72.     [Description("The person's favourite fruit"),
  73.      Category("Stuff I like")
  74.     ]
  75.     public Fruit FavouriteFruit
  76.     {
  77.       set { _fruit = value; }
  78.       get { return _fruit; }
  79.     }
  80.     const string filename = "AppSettings.xml";
  81.     // Our methods for loading and saving the settings
  82.     // Load needs to be static, as we don't yet have
  83.     // an instance
  84.     public static AppSettings Load()
  85.     {
  86.       AppSettings ret = null;
  87.       XmlSerializer xs = null;
  88.       StreamReader sr = null;
  89.       try
  90.       {
  91.         xs = new XmlSerializer(typeof(AppSettings));
  92.         sr = new StreamReader(filename);
  93.       }
  94.       catch
  95.       {
  96.         // File not found: create default settings
  97.         return new AppSettings();
  98.       }
  99.       if (sr != null)
  100.       {
  101.         ret = (AppSettings)xs.Deserialize(sr);
  102.         sr.Close();
  103.       }
  104.       return ret;
  105.     }
  106.     // Save will be called on a specific instance
  107.     public void Save()
  108.     {
  109.       try
  110.       {
  111.         XmlSerializer xs =
  112.           new XmlSerializer(typeof(AppSettings));
  113.         StreamWriter sw =
  114.           new StreamWriter(filename, false);
  115.         xs.Serialize(sw, this);
  116.         sw.Close();
  117.       }
  118.       catch (System.Exception ex)
  119.       {
  120.         Editor ed =
  121.           Application.DocumentManager.MdiActiveDocument.Editor;
  122.         ed.WriteMessage(
  123.           "\nUnable to save the application settings: {0}",
  124.           ex
  125.         );
  126.       }
  127.     }
  128.   }
  129.   class Initialization : IExtensionApplication
  130.   {
  131.     static AppSettings _settings = null;
  132.     public void Initialize()
  133.     {
  134.       Application.DisplayingOptionDialog +=
  135.         new TabbedDialogEventHandler(
  136.           Application_DisplayingOptionDialog
  137.         );
  138.     }
  139.     public void Terminate()
  140.     {
  141.       Application.DisplayingOptionDialog -=
  142.         new TabbedDialogEventHandler(
  143.           Application_DisplayingOptionDialog
  144.         );
  145.     }
  146.     private static void OnOK()
  147.     {
  148.       _settings.Save();
  149.     }
  150.     private static void OnCancel()
  151.     {
  152.       _settings = AppSettings.Load();
  153.     }
  154.     private static void OnHelp()
  155.     {
  156.       // Not currently doing anything here
  157.     }
  158.     private static void OnApply()
  159.     {
  160.       _settings.Save();
  161.     }
  162.     private static void Application_DisplayingOptionDialog(
  163.       object sender,
  164.       TabbedDialogEventArgs e
  165.     )
  166.     {
  167.       if (_settings == null)
  168.         _settings = AppSettings.Load();
  169.       if (_settings != null)
  170.       {
  171.         OptionsDlg.OptionsTabControl otc =
  172.           new OptionsDlg.OptionsTabControl();
  173.         otc.propertyGrid.SelectedObject = _settings;
  174.         otc.propertyGrid.Update();
  175.         TabbedDialogExtension tde =
  176.           new TabbedDialogExtension(
  177.             otc,
  178.             new TabbedDialogAction(OnOK),
  179.             new TabbedDialogAction(OnCancel),
  180.             new TabbedDialogAction(OnHelp),
  181.             new TabbedDialogAction(OnApply)
  182.           );
  183.         e.AddTab("My Application Settings", tde);
  184.       }
  185.     }
  186.   }
  187. }
The interesting stuff is in the AppSettings class: it defines a number of properties (for which I've set default values as they're declared - you could also put them in a constructor, should you so wish), which are then exposed externally. It's these public properties that are interesting, as we've used attributes to indicate how the properties should be categorized, described and whether they're editable. The rest of the class contains the protocol to load and save the settings: we use the .NET Framework to do the heavy lifting of saving the contents to a file (which we've simply called AppSettings.xml, without specifying the location, which means it will be stored wherever your module is located), and loading them back in again.
Here's what the XML content looks like for the default settings, in case you're interested, although you should never really need to worry about it, unless you're interested in allowing more direct modification of the file contents:
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <AppSettings
  3.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  5.   <Name>Kean Walmsley</Name>
  6.   <Blog>http://blogs.autodesk.com/through-the-interface</Blog>
  7.   <Birthday>1912-07-14T00:00:00</Birthday>
  8.   <FavouriteFruit>Strawberry</FavouriteFruit>
  9. </AppSettings>
复制代码
A couple of more comments on the code...
We haven't bothered implementing the Help callback, but I've left it in their for your convenience (we could also have passed a null value into the construction of the TabbedDialogExtension object). From the callbacks for OK and Apply we call through to the AppSettings class to save the data; from Cancel we reload the last saved state, effectively cancelling any unsaved changes.
The AppSettings class will need to be accessible from elsewhere in your code (that's the point, really - settings aren't much use unless they're accessed), but I haven't actually shown this. It should simply be a matter of setting the _settings object to be public (or internal), or of exposing the data you care about via properties on the Initialization class.
Here's what tab looks like, once we've built the application, loaded it and launched the OPTIONS command inside AutoCAD:

As you edit the properties you'll see the controls available suit the property in question: there's a date picker for "Birthday" (which isn't actually my date of birth, by the way: it seems safer not to publish your birthday on the web, these days) and a combo-box for "FavouriteFruit". I wish there were better display of URLs in the grid, but that appears to be a standard complaint, and beyond the scope of this post. You will notice an "Age" property which has been made read-only as it's calculated from the date field. You'll also notice that the "Name" property is selected by default, because we indicated it as such using the DefaultProperty() attribute of the class.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-6-14 20:48 | 显示全部楼层
八、AutoCad2010中新的Api February 13, 2009
The new APIs in AutoCAD 2010 - Part 1
This is the first post in a series looking at the new APIs in AutoCAD 2010, and follows on from this post looking at the migration steps required. I've copied the information in this post from the recently-published Platform Technologies Customization Newsletter, a quarterly newsletter available to ADN members. A big thank you to Stephen Preston, Fenton Webb and Gopinath Taget for putting the material together.
AutoCAD 2010 New API Overview
AutoCAD 2010 has some really cool APIs. Please download the ObjectARX 2010 Beta SDK and review the Migration Guide for a complete list of changes and additions. [This is currently available to ADN members on the ADN extranet.]
Here are the highlights:
Overrule API
One of the most powerful ObjectARX APIs is the custom objects API. The custom object API allows you to create your own entities in a drawing that behave in the same way as standard AutoCAD entities. So, for example, where AutoCAD has a line, you might develop a custom entity that looks like a ‘pipe’. You can define how your pipe displays itself, the pipes grip- and snap- points, how the pipe behaves when moves or copied, etc.
However, with great power comes great responsibility. Custom objects are saved to a drawing. Without your Object Enabler, your custom object is loaded into AutoCAD as a dumb proxy object. So when you are considering creating a custom object, you need to consider whether you’re prepared to make a commitment to your application users that you will continue to support your custom object through multiple AutoCAD releases. If you’re not prepared to make that commitment, then you really shouldn’t be creating custom objects.
And because your custom object is responsible for filing itself when a drawing is saved or opened, you also have an extremely powerful mechanism for corrupting all your customers drawings if you make a mistake in your implementation.
To provide you with an alternative to custom objects – an alternative that requires less long term support commitment from you – AutoCAD 2010 introduces the new Overrule API. Think of Overrule as customized objects, rather than custom objects. It’s essentially a mechanism for AutoCAD to call your implementation of certain object functions instead of immediately calling the functions for that object. Your implementation can then choose whether to refer the call back to the native object. Unlike custom objects, the overrule definitions are not filed to the DWG file, so it’s a lot harder to corrupt your drawing. Instead, the Overrule API will only customize an entity when your application is loaded. (Although, you can save data used by your Overrule as Xdata or in Xrecords).
As a simple example, you can overrule an entity’s worldDraw function and draw your own graphical representation instead. (In the simple sample we demonstrated at Developer Days, we took a Line and turned it into a Thermometer (see image).

Image: Two Lines – Can you tell which one has been Overruled? ;-).


The Overrule API is available in ObjectARX (C++) and .NET. Here’s a simple VB.NET example of how you’d create an overrule…
First, create your custom Overrule class, inheriting from one of the available Overrules, and overriding the functions you want to overrule. In this case, we’re overruling an entity’s WorldDraw function. WorldDraw is part of the DrawableOverrule.
  1. Imports Autodesk.AutoCAD.GraphicsInterface
  2. Public Class MyDrawOverrule
  3.     Inherits DrawableOverrule
  4.     'This is the function that gets called to add/replace
  5.     'an entity's WorldDraw graphics
  6.     Public Overrides Function WorldDraw( _
  7.       ByVal drawable As Drawable, _
  8.       ByVal wd As WorldDraw) As Boolean
  9.         'Draw my own graphics here ...
  10.         'Call the object's own worldDraw function (if you want to)
  11.         Return MyBase.WorldDraw(drawable, wd)
  12.     End Function
  13. End Class
Next, instantiate your Overrule, add it to the entity you want to overrule, and turn Overruling on. (You can also specify how the overrule is applied – you can apply it to every object of that type, apply it depending on Xdata or Xrecords, maintain a list of ObjectIds of entities to be overruled, or define your own custom filter).
[Note that you will need to have Imported Autodesk,AutoCAD.Runtime and DatabaseServices for the below code to build.]
  1. 'mDrawOverrule is a class member variable
  2. 'we declared elsewhere
  3. mDrawOverrule = New MyDrawOverrule
  4. 'Add the Overrule to the entity class - in this case Line
  5. Overrule.AddOverrule( _
  6.     RXObject.GetClass(GetType(Line)), _
  7.     mDrawOverrule, False)
  8. 'Optional - specify filter
  9. '(In this case we only apply overrule to Lines with entry
  10. ' named "RDS_MyData" in Extension Dictionary)
  11. mDrawOverrule.SetExtensionDictionaryEntryFilter("RDS_MyData")
  12. 'Turn overruling on
  13. Overrule.Overruling = True
复制代码
And that’s all there is to it.
You can find a (simple) working Overrule sample with the Developer Days material posted on the ADN website. [I will post my own C# sample to this blog over the coming weeks, as I play around with the API myself - Kean] We’ll be extending that sample soon and using it as the basis of a webcast after AutoCAD 2010 has shipped. And look at the ‘Behavior Overrules’ section of the ObjectARX Developers Guide for information on the ObjectARX implementation of this API, and for details of methods affected by this API.
Freeform Modeling API
3D modeling in AutoCAD tends to be a bit ‘blocky’. It’s hard to create a shape that looks really organic. That’s where Freeform modeling comes in. It’s hard to describe succinctly the power of this feature, so I’d encourage you to review Heidi’s product demonstration [Once again, this link is to the ADN site - I will post more about the freeform modelling capabilities of AutoCAD 2010, in due course - Kean]. The basic idea is to take a solid or mesh, twist it around a bit by pushing and pulling at its edges, vertices and faces, and then smooth it and crease it. The smoothing is performed using  Subdivision – we use the Catmull-Clark algorithm that is already being used by other Autodesk products.

The API centers on the Sub-division mesh object – AcDbSubDMesh in ObjectARX, DatabaseServices.SubDMesh in .NET, and AcadSubDMesh in ActiveX. The API allows you to do essentially everything a user can through the UI. Here’s a simple VB.NET sample showing how to generate a SubDMesh from a Solid3d and then apply level 1 smoothing to it.
  1. Imports Autodesk.AutoCAD.ApplicationServices
  2. Imports Autodesk.AutoCAD.DatabaseServices
  3. Imports Autodesk.AutoCAD.EditorInput
  4. Imports Autodesk.AutoCAD.Geometry
  5. Imports Autodesk.AutoCAD.Runtime
  6. Public Class FreeFormSample
  7.   <CommandMethod("CREATEMESH")> _
  8.   Public Sub MySub()
  9.     'Select a solid.
  10.     Dim ed As Editor = _
  11.       Application.DocumentManager.MdiActiveDocument.Editor
  12.     Dim opts As _
  13.       New PromptEntityOptions(vbCrLf + "Select Solid:")
  14.     opts.SetRejectMessage(vbCrLf & "That's not a solid!")
  15.     opts.AddAllowedClass(GetType(Solid3d), False)
  16.     Dim res As PromptEntityResult = ed.GetEntity(opts)
  17.     'Exit sub if user cancelled selection.
  18.     If res.Status <> PromptStatus.OK Then Exit Sub
  19.     'Usual transaction stuff
  20.     Dim db As Database = _
  21.       Application.DocumentManager.MdiActiveDocument.Database
  22.     Using tr As Transaction = _
  23.       db.TransactionManager.StartTransaction
  24.       Dim mySolid As Solid3d = _
  25.         tr.GetObject( _
  26.           res.ObjectId, _
  27.           OpenMode.ForRead, False)
  28.       Dim ext As Extents3d = mySolid.Bounds
  29.       Dim vec As Vector3d = (ext.MaxPoint - ext.MinPoint)
  30.       'Define params governing mesh generation algorithm
  31.       '(See ObjectARX helpfiles for explanation of params –
  32.       ' you may need to change them depending on the scale
  33.       ' of the solid)
  34.       Dim myFaceterData As _
  35.         New MeshFaceterData( _
  36.           0.01 * vec.Length, _
  37.           40 * Math.PI / 180, _
  38.           2, 2, 15, 5, 5, 0)
  39.       'Create new mesh from solid (smoothing level 1)
  40.       Dim meshData As MeshDataCollection = _
  41.         SubDMesh.GetObjectMesh(mySolid, myFaceterData)
  42.       Dim myMesh As New SubDMesh
  43.       myMesh.SetSubDMesh( _
  44.         meshData.VertexArray, meshData.FaceArray, 1)
  45.       'Add mesh to database. (Don't remove solid).
  46.       myMesh.SetDatabaseDefaults()
  47.       Dim btr As BlockTableRecord = _
  48.         tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite)
  49.       btr.AppendEntity(myMesh)
  50.       tr.AddNewlyCreatedDBObject(myMesh, True)
  51.       'Our work here is done
  52.       tr.Commit()
  53.     End Using
  54.   End Sub
  55. End Class
In the next post we'll look at the Parametric Drawing API, CUI API Enhancements, RibbonBar Controls, PDF Underlays and the new AutoCAD .NET Developer's Guide.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-9-6 19:54 | 显示全部楼层
九、
July 27, 2009
Providing information on AutoCAD objects in a tooltip using .NET
One of the responses to my last post on the “Plugin of the Month” asked about showing information on an AutoCAD drawing object via a tooltip. Other than using the standard rollover tooltip properties mechanism, as shown in this previous post, the best way to achieve this is via a PointMonitor.
In the below C# code we check which object ore objects are being hovered over, get information about those objects and add them to the tooltip.
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. public class PointMonitorTooltips
  7. {
  8.   [CommandMethod("SM")]
  9.   public static void StartMonitor()
  10.   {
  11.     Editor ed =
  12.       Application.DocumentManager.MdiActiveDocument.Editor;
  13.     ed.PointMonitor +=
  14.       new PointMonitorEventHandler(ed_PointMonitor);
  15.   }
  16.   [CommandMethod("XM")]
  17.   public static void StopMonitor()
  18.   {
  19.     Editor ed =
  20.       Application.DocumentManager.MdiActiveDocument.Editor;
  21.     ed.PointMonitor -=
  22.       new PointMonitorEventHandler(ed_PointMonitor);
  23.   }
  24.   static void ed_PointMonitor(object sender, PointMonitorEventArgs e)
  25.   {
  26.     Editor ed = (Editor)sender;
  27.     Document doc = ed.Document;
  28.     if (!e.Context.PointComputed)
  29.       return;
  30.     try
  31.     {
  32.       // Determine the size of the selection aperture
  33.       short pickbox =
  34.         (short)Application.GetSystemVariable("PICKBOX");
  35.       Point2d extents =
  36.         e.Context.DrawContext.Viewport.GetNumPixelsInUnitSquare(
  37.           e.Context.ComputedPoint
  38.         );
  39.       double boxWidth = pickbox / extents.X;
  40.       Vector3d vec =
  41.         new Vector3d(boxWidth / 2, boxWidth / 2, 0.0);
  42.       // Do a crossing selection using a centred box the
  43.       // size of the aperture
  44.       PromptSelectionResult pse =
  45.         ed.SelectCrossingWindow(
  46.           e.Context.ComputedPoint - vec,
  47.           e.Context.ComputedPoint + vec
  48.         );
  49.       if (pse.Status != PromptStatus.OK ||
  50.           pse.Value.Count <= 0)
  51.         return;
  52.       // Go through the results of the selection
  53.       // and detect the curves
  54.       string curveInfo = "";
  55.       Transaction tr =
  56.         doc.TransactionManager.StartTransaction();
  57.       using (tr)
  58.       {
  59.         // Open each object, one by one
  60.         ObjectId[] ids = pse.Value.GetObjectIds();
  61.         foreach (ObjectId id in ids)
  62.         {
  63.           DBObject obj =
  64.             tr.GetObject(id, OpenMode.ForRead);
  65.           if (obj != null)
  66.           {
  67.             // If it's a curve, get its length
  68.             Curve cv = obj as Curve;
  69.             if (cv != null)
  70.             {
  71.               double length =
  72.                 cv.GetDistanceAtParameter(cv.EndParam);
  73.               // Then add a message including the object
  74.               // type and its length
  75.               curveInfo +=
  76.                 obj.GetType().Name + "'s length: " +
  77.                 string.Format("{0:F}", length) + "\n";
  78.             }
  79.           }
  80.         }
  81.         // Cheaper than aborting
  82.         tr.Commit();
  83.       }
  84.       // Add the tooltip of the lengths of the curves detected
  85.       if (curveInfo != "")
  86.         e.AppendToolTipText(curveInfo);
  87.     }
  88.     catch
  89.     {
  90.       // Not sure what we might get here, but not real action
  91.       // needed (worth adding an Exception parameter and a
  92.       // breakpoint, in case things need investigating).
  93.     }
  94.   }
  95. }
During the command we perform a selection, using Editor.SelectCrossingWindow() specifying a window based on the selected point, using a window that’s about the size of the selection aperture (at least that’s what I’m trying to do: let me know if you’ve got a better solution for this). Having some amount of “fuzz” in the selection allows us to not have to hover very precisely over the object, which may or may not be what you’re looking for.
We then iterate through the objects, gathering information about the objects we care about. In this case we’re looking for curves: when we find one, we add its length (along with its object type) to the string we eventually add to the tooltip via AppendToolTipText().
There are two commands: SM adds the monitor, and XM removes it. Bear in mind that the event fires for all sorts of input events - including keystrokes – so you can actually cause problems unless you’re careful. As an example, I wasn’t previously checking the PointComputed status of the PointManager’s context, which is false if we’re looking at keyboard input. The code would go ahead and try to select geometry, which – for some reason unknown to me – cause the character in the keystroke buffer to be lost. Which meant that entering any command would only result in the last command being executed (via the return being processed – that one got through, OK :-). All this to point out that care should be taken when relying on events that are so integrated so deeply into AutoCAD’s user input mechanism.
PointMonitor tooltip over a single line So let’s see what we get after NETLOADing the module, running the SM command and hovering over a curve, in this case a line. We see a tooltip appear containing the line’s length.
And if we do the same with a lot more intersecting objects, we see that we also get the PointMonitor tooltip over lots of different curvesopportunity to provide information on those. It’s ultimately our choice how we manage the selection process and what we do with the results (although please don’t assume you can attempt everything from this kind of very frequently fired event: there are a number of areas where you may find things failing, especially if there’s any interactive component).

Update:
Thanks to Tony Tanzillo for keeping me honest (see his comments, below). The below code addresses a few issues with the version shown above, and should be used instead.
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. public class PointMonitorTooltips
  7. {
  8.   [CommandMethod("SM")]
  9.   public static void StartMonitor()
  10.   {
  11.     Editor ed =
  12.       Application.DocumentManager.MdiActiveDocument.Editor;
  13.     ed.PointMonitor +=
  14.       new PointMonitorEventHandler(ed_PointMonitor);
  15.   }
  16.   [CommandMethod("XM")]
  17.   public static void StopMonitor()
  18.   {
  19.     Editor ed =
  20.       Application.DocumentManager.MdiActiveDocument.Editor;
  21.     ed.TurnForcedPickOn();
  22.     ed.PointMonitor -=
  23.       new PointMonitorEventHandler(ed_PointMonitor);
  24.   }
  25.   static void ed_PointMonitor(object sender, PointMonitorEventArgs e)
  26.   {
  27.     Editor ed = (Editor)sender;
  28.     Document doc = ed.Document;
  29.     try
  30.     {
  31.       FullSubentityPath[] paths =
  32.         e.Context.GetPickedEntities();
  33.       // Go through the results of the selection
  34.       // and detect the curves
  35.       string curveInfo = "";
  36.       Transaction tr =
  37.         doc.TransactionManager.StartTransaction();
  38.       using (tr)
  39.       {
  40.         // Open each object, one by one
  41.         foreach (FullSubentityPath path in paths)
  42.         {
  43.           ObjectId[] ids = path.GetObjectIds();
  44.           if (ids.Length > 0)
  45.           {
  46.             ObjectId id = ids[ids.GetUpperBound(0)];
  47.             DBObject obj =
  48.               tr.GetObject(id, OpenMode.ForRead);
  49.             if (obj != null)
  50.             {
  51.               // If it's a curve, get its length
  52.               Curve cv = obj as Curve;
  53.               if (cv != null)
  54.               {
  55.                 double length =
  56.                   cv.GetDistanceAtParameter(cv.EndParam) -
  57.                   cv.GetDistanceAtParameter(cv.StartParam);
  58.                 // Then add a message including the object
  59.                 // type and its length
  60.                 curveInfo +=
  61.                   obj.GetType().Name + "'s length: " +
  62.                   string.Format("{0:F}", length) + "\n";
  63.               }
  64.             }
  65.           }
  66.         }
  67.         // Cheaper than aborting
  68.         tr.Commit();
  69.       }
  70.       // Add the tooltip of the lengths of the curves detected
  71.       if (curveInfo != "")
  72.         e.AppendToolTipText(curveInfo);
  73.     }
  74.     catch
  75.     {
  76.       // Not sure what we might get here, but not real action
  77.       // needed (worth adding an Exception parameter and a
  78.       // breakpoint, in case things need investigating).
  79.     }
  80.   }
  81. }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-3-28 17:43 , Processed in 4.022178 second(s), 17 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表