明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
12
返回列表 发新帖

[Kean专集] Kean专题(15)—User_Interface

   关闭 [复制链接]
 楼主| 发表于 2009-9-7 15:36 | 显示全部楼层
本帖最后由 作者 于 2009-9-9 8:20:41 编辑

十一、将AC2009嵌入到对话框中
March 27, 2008
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):

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...".

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.

Then you simply need to place the control on your 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.

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.
  1. using System;
  2. using System.Windows.Forms;
  3. namespace EmbedAutoCAD
  4. {
  5.   public partial class MainForm : Form
  6.   {
  7.     public MainForm()
  8.     {
  9.       InitializeComponent();
  10.     }
  11.     private void browseButton_Click(
  12.       object sender, EventArgs e)
  13.     {
  14.       OpenFileDialog dlg =
  15.         new OpenFileDialog();
  16.       dlg.InitialDirectory =
  17.         System.Environment.CurrentDirectory;
  18.       dlg.Filter =
  19.         "DWG files (*.dwg)|*.dwg|All files (*.*)|*.*";
  20.       Cursor oc = Cursor;
  21.       String fn = "";
  22.       if (dlg.ShowDialog() ==
  23.         DialogResult.OK)
  24.       {
  25.         Cursor = Cursors.WaitCursor;
  26.         fn = dlg.FileName;
  27.         Refresh();
  28.       }
  29.       if (fn != "")
  30.         this.drawingPath.Text = fn;
  31.       Cursor = oc;
  32.     }
  33.     private void loadButton_Click(
  34.       object sender, EventArgs e)
  35.     {
  36.       if (System.IO.File.Exists(drawingPath.Text))
  37.         axAcCtrl1.Src = drawingPath.Text;
  38.       else
  39.         MessageBox.Show("File does not exist");
  40.     }
  41.     private void postButton_Click(
  42.       object sender, EventArgs e)
  43.     {
  44.       axAcCtrl1.PostCommand(cmdString.Text);
  45.     }
  46.   }
  47. }
Finally, when we run the application and load a drawing via the browse/load buttons, the real fun starts. :-)

Try entering commands via dynamic input, or via the "ost 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.

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-9-9 08:19 | 显示全部楼层
十二、可选多种文件格式的文件对话框
August 21, 2009
Allowing a user to select from multiple file formats inside AutoCAD using .NET
This is a topic that I’ve covered to some degree in a couple of previous posts:
Using AutoCAD's file selection dialog from .NET
Replacing AutoCAD's OPEN command using .NET
Neither focused on the question of allowing the user to select from a number of different file format filters (try saying “different file format filters” five times, quickly :-), so I thought I’d compare and contrast the two approaches in this post.
There are two primary mechanisms provided by AutoCAD’s .NET interface for file selection:
   1. Methods from the Editor class:
          * GetFileNameForOpen()
          * GetFileNameForSave()
   2. Classes in the Autodesk.AutoCAD.Windows namespace:
          * OpenFileDialog
          * SaveFileDialog
Within the two mechanisms the open and save choices are broadly similar, the main differences being around prompting the user to overwrite (in the case of save) and the need for a file to exist (in the case of open). In this case we’ll focus on open: providing the same treatment for save is left as an exercise for the reader.
Here’s the C# code that compares the two approaches. You may need to add additional project references to AcWindows.dll and System.Windows.Forms:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.EditorInput;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.Windows;
  5. namespace FileSelectionOptions
  6. {
  7.   public class Commands
  8.   {
  9.     [CommandMethod("SF")]
  10.     public void SelectFiles()
  11.     {
  12.       Editor ed =
  13.         Application.DocumentManager.MdiActiveDocument.Editor;
  14.       // First let's use the editor method, GetFileNameForOpen()
  15.       PromptOpenFileOptions opts =
  16.         new PromptOpenFileOptions(
  17.           "Select a file using Editor.GetFileNameForOpen()"
  18.         );
  19.       opts.Filter =
  20.         "Drawing (*.dwg)|*.dwg|Design Web Format (*.dwf)|*.dwf|" +
  21.         "All files (*.*)|*.*";
  22.       PromptFileNameResult pr = ed.GetFileNameForOpen(opts);
  23.       if (pr.Status == PromptStatus.OK)
  24.       {
  25.         ed.WriteMessage(
  26.           "\nFile selected was "{0}".\n",
  27.           pr.StringResult
  28.         );
  29.       }
  30.       // Now let's create and use an OpenFileDialog object
  31.       OpenFileDialog ofd =
  32.         new OpenFileDialog(
  33.           "Select a file using an OpenFileDialog",
  34.           null,
  35.           "dwg; dwf; *",
  36.           "SelectFileTest",
  37.           OpenFileDialog.OpenFileDialogFlags.DoNotTransferRemoteFiles
  38.         );
  39.       System.Windows.Forms.DialogResult dr = ofd.ShowDialog();
  40.       if (dr == System.Windows.Forms.DialogResult.OK)
  41.       {
  42.         ed.WriteMessage(
  43.           "\nFile selected was "{0}".\n",
  44.           ofd.Filename
  45.         );
  46.       }
  47.     }
  48.   }
  49. }
Now let’s see what happens when we run the SF command.
Here’s the first dialog displayed using Editor.GetFileNameForOpen():

And here’s the equivalent dialog using OpenFileDialog:

Overall I prefer the way the control you have over the filter list using the GetFileNameForXxx() methods: OpenFileDialog has you provide the extensions and then attempts to determine the appropriate description (which works fine for DWGs but less so for other extensions, as far as I can tell).

本帖子中包含更多资源

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

x
 楼主| 发表于 2010-2-3 22:15 | 显示全部楼层
十三、在工具面板中动态加入项目
Adding items to an AutoCAD tool palette using .NET
This post carries directly on from the last one, which implemented a rudimentary “Quick SaveAs” capability in AutoCAD. Much of the explanation behind the design of today’s code is there, so please do read it first (if you haven’t already).
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Windows.ToolPalette;
  6. using System.Runtime.InteropServices;
  7. using System.IO;
  8. using System;
  9. namespace QuickSaveAs
  10. {
  11.   public class Commands
  12.   {
  13.     // Set up static variable for the path to our folder
  14.     // of drawings, as well as the base filename and a
  15.     // counter to make the unique filename
  16.     static string _path = "",
  17.                   _base = "";
  18.     static int _count = 0;
  19.     // Various filename and path-related constants
  20.     const string sfxSep = " ",
  21.                  extSep = ".",
  22.                  pthSep = "\",
  23.                  lspSep = "/",
  24.                  dwgExt = ".dwg",
  25.                  scrExt = ".txt",
  26.                  bmpExt = ".bmp",
  27.                  bmpLoc = "Images",
  28.                  scrLoc = "Scripts";
  29.     // Our QuickSaveAs command
  30.     [CommandMethod("QSAVEAS")]
  31.     public void QuickSaveAs()
  32.     {
  33.       Document doc =
  34.         Application.DocumentManager.MdiActiveDocument;
  35.       Editor ed = doc.Editor;
  36.       Database db = doc.Database;
  37.       // If this is the first time run...
  38.       if (_path == "" || _base == "")
  39.       {
  40.         // Ask the user for a base file location
  41.         PromptSaveFileOptions opts =
  42.           new PromptSaveFileOptions(
  43.             "Select location to save first drawing file"
  44.           );
  45.         opts.Filter = "Drawing (*.dwg)|*.dwg";
  46.         PromptFileNameResult pr =
  47.           ed.GetFileNameForSave(opts);
  48.         // Delete the file, if it exists
  49.         // (may be a problem if the file is in use)
  50.         
  51.         if (File.Exists(pr.StringResult))
  52.         {
  53.           try
  54.           {
  55.             File.Delete(pr.StringResult);
  56.           }
  57.           catch { }
  58.         }
  59.         if (pr.Status == PromptStatus.OK)
  60.         {
  61.           // If a file was selected, and it contains a path...
  62.           if (pr.StringResult.Contains(pthSep))
  63.           {
  64.             // Separate the path from the file name
  65.             int idx = pr.StringResult.LastIndexOf(pthSep);
  66.             _path =
  67.               pr.StringResult.Substring(0, idx);
  68.             string fullname =
  69.               pr.StringResult.Substring(idx+1);
  70.             
  71.             // If the path has an extension (this should always
  72.             // be the case), extract the base file name
  73.             if (fullname.Contains(extSep))
  74.             {
  75.               _base =
  76.                 fullname.Substring(
  77.                   0,
  78.                   fullname.LastIndexOf(extSep)
  79.                 );
  80.               // Create folders for our icons and our scripts
  81.               Directory.CreateDirectory(
  82.                 _path + pthSep + bmpLoc
  83.               );
  84.               Directory.CreateDirectory(
  85.                 _path + pthSep + scrLoc
  86.               );
  87.             }
  88.           }
  89.         }
  90.       }
  91.       // Assuming the path and name were set appropriately...
  92.       if (_path != "" && _base != "")
  93.       {
  94.         string name = _base;
  95.         // Add our suffix if not the first time run
  96.         if (_count > 0)
  97.           name += sfxSep + _count.ToString();
  98.         // Our drawing is located in the base path
  99.         string dwgPath = _path + pthSep + name + dwgExt;
  100.         
  101.         // While our script is in a sub-folder
  102.         string scrPath =
  103.           _path + pthSep + scrLoc + pthSep + name + scrExt;
  104.         // Create a dummy script, so we can make sure we pick
  105.         // up the contents in our dummy execute command
  106.         File.WriteAllText(
  107.           scrPath,
  108.           "This is a dummy script for " + name + "."
  109.         );
  110.         // Now we want to save our drawing and use the image
  111.         // for our tool icon
  112.         // Using either COM or .NET doesn't generate a
  113.         // thumbnail in the resultant file (or its Database)
  114.         // .NET:
  115.         // db.SaveAs(dwgPath, false, DwgVersion.Current, null);
  116.         
  117.         // COM:
  118.         // AcadDocument adoc = (AcadDocument)doc.AcadDocument;
  119.         // adoc.SaveAs(dwgPath, AcSaveAsType.acNative, null);
  120.         // So we'll send commands to the command-line
  121.         // We'll use LISP, to avoid having to set FILEDIA to 0
  122.         object ocmd = Application.GetSystemVariable("CMDECHO");
  123.         string dwgPath2 = dwgPath.Replace(pthSep, lspSep);
  124.         string scrPath2 = scrPath.Replace(pthSep, lspSep);
  125.         string c1 =
  126.           "(setvar "CMDECHO" 0)" +
  127.           "(command "_.SAVEAS" "" "" + dwgPath2 + "")";
  128.         string c2 =
  129.           "(setvar "CMDECHO" " + ocmd.ToString() + ")" +
  130.           "(tp-create "" + name + "" "" + scrPath2 + "")" +
  131.           "(princ) ";
  132.         string cmd = c1 + c2;
  133.         if (cmd.Length <= 255)
  134.         {
  135.           doc.SendStringToExecute(cmd, false, false, false);
  136.         }
  137.         else
  138.         {
  139.           doc.SendStringToExecute(c1+" ", false, false, false);
  140.           doc.SendStringToExecute(c2, false, false, false);
  141.         }
  142.         // Print a confirmation message for the DWG save
  143.         // (which actually gets displayed before the queued
  144.         // string gets executed, but anyway)
  145.         ed.WriteMessage("\nSaved to: "" + dwgPath + """);
  146.         _count++;
  147.       }
  148.     }
  149.     // Our LISP-registered continuation function to create a
  150.     // command tool on our tool palette
  151.     [LispFunction("TP-CREATE")]
  152.     public ResultBuffer CreateToolPaletteCommand(
  153.       ResultBuffer rb
  154.     )
  155.     {
  156.       const int RTSTR = 5005;
  157.       Document doc =
  158.         Application.DocumentManager.MdiActiveDocument;
  159.       Editor ed = doc.Editor;
  160.       if (rb == null)
  161.       {
  162.         ed.WriteMessage("\nError: too few arguments.");
  163.       }
  164.       else
  165.       {
  166.         // We're only interested in the first two arguments
  167.         Array args = rb.AsArray();
  168.         if (args.Length != 2)
  169.         {
  170.           ed.WriteMessage(
  171.             "\nError: wrong number of arguments."
  172.           );
  173.         }
  174.         else
  175.         {
  176.           // First argument is the name, second is the path
  177.           // to the script
  178.           TypedValue tv1 = (TypedValue)args.GetValue(0);
  179.           TypedValue tv2 = (TypedValue)args.GetValue(1);
  180.           if (tv1 != null && tv1.TypeCode == RTSTR &&
  181.               tv2 != null && tv2.TypeCode == RTSTR)
  182.           {
  183.             string name = Convert.ToString(tv1.Value);
  184.             string lspScrPath = Convert.ToString(tv2.Value);
  185.             string scrPath =
  186.               lspScrPath.Replace(lspSep, pthSep);
  187.             bool success =
  188.               CreateCommand(doc.Database, name, scrPath);
  189.             return
  190.               (success ?
  191.                 new ResultBuffer(
  192.                   new TypedValue(RTSTR, tv1.Value)
  193.                 )
  194.                 : null);
  195.           }
  196.         }
  197.       }
  198.       return null;
  199.     }
  200.     // Function to add a command tool to our tool palette to
  201.     // execute the script
  202.     private bool CreateCommand(
  203.       Database db,
  204.       string name,
  205.       string scrPath
  206.     )
  207.     {
  208.       const string catName = "ScriptCatalog";
  209.       const string palName = "Scripts";
  210.       ToolPaletteManager tpm = ToolPaletteManager.Manager;
  211.       // Get the GUID of our dummy custom tool
  212.       Type t = typeof(DummyTool);
  213.       GuidAttribute ga =
  214.         (GuidAttribute)t.GetCustomAttributes(
  215.           typeof(GuidAttribute), false)[0];
  216.       Guid g = new Guid(ga.Value);
  217.       // Instanciate our dummy tool - this will allow us to use
  218.       // its helper functions
  219.       DummyTool tool = new DummyTool();
  220.       Catalog cat;
  221.       Palette pal = null;
  222.       // First we check whether our GUID is in a catalog
  223.       CatalogItem ci = tpm.StockToolCatalogs.Find(g);
  224.       if (ci != null)
  225.       {
  226.         // If it is, search each catalog for our palette
  227.         foreach(CatalogItem ci2 in tpm.Catalogs)
  228.         {
  229.           for (int i = 0; i < ci2.ChildCount; i++)
  230.           {
  231.             CatalogItem ci3 = ci2.GetChild(i);
  232.             if (ci3 != null && ci3.Name == palName)
  233.             {
  234.               pal = ci3 as Palette;
  235.               break;
  236.             }
  237.           }
  238.           if (pal != null)
  239.             break;
  240.         }
  241.       }
  242.       // If we didn't find our palette, create it
  243.       if (pal == null)
  244.       {
  245.         cat = tool.CreateStockTool(catName);
  246.         pal = tool.CreatePalette(cat, palName);
  247.       }
  248.       // To add our command tool instance we need an icon
  249.       ImageInfo ii = new ImageInfo();
  250.       if (db.ThumbnailBitmap != null)
  251.       {
  252.         // Which we create from the Database's thumbnail
  253.         string bmpPath =
  254.           _path + pthSep + bmpLoc + pthSep + name + bmpExt;
  255.         db.ThumbnailBitmap.Save(bmpPath);
  256.         ii.ResourceFile = bmpPath;
  257.       }
  258.       ii.Size = new System.Drawing.Size(65, 65);
  259.       // And then we use our dummy tool to create the
  260.       // command tool
  261.       tool.CreateCommandTool(
  262.         pal,
  263.         name,
  264.         ii,
  265.         "_EXECSCR "" + scrPath.Replace(pthSep, lspSep) + """
  266.       );
  267.       
  268.       // Finally we reload the catalogs to display the change
  269.       tpm.LoadCatalogs();
  270.       
  271.       return true;
  272.     }
  273.     // A dummy command to simulate the execution of our script
  274.     // (which simply reads the contents and displays them on
  275.     // the command-line)
  276.     [CommandMethod("EXECSCR")]
  277.     public void ExecuteScript()
  278.     {
  279.       Document doc =
  280.         Application.DocumentManager.MdiActiveDocument;
  281.       Editor ed = doc.Editor;
  282.       PromptResult pr =
  283.         ed.GetString(
  284.           "\nEnter location of script to execute: "
  285.         );
  286.       if (pr.Status == PromptStatus.OK)
  287.       {
  288.         string path =
  289.           pr.StringResult.Replace(lspSep, pthSep);
  290.         if (File.Exists(path))
  291.         {
  292.           string contents = File.ReadAllText(path);
  293.           ed.WriteMessage(
  294.             "\nDummy script contained: "{0}"",
  295.             contents
  296.           );
  297.         }
  298.       }
  299.     }
  300.   }
  301.   // Our dummy tool which simply derives from CustomToolBase
  302.   // (there may be a more straightforward way to get access
  303.   // to the helpers in CustomToolBase, but anyway)
  304.   [Guid("3B725500-0451-4081-A1BB-B37CE6A65767")]
  305.   [Tool("MyDummyTool", "IDB_TOOL")]
  306.   [ClassInterface(ClassInterfaceType.AutoDual)]
  307.   public class DummyTool : CustomToolBase
  308.   {
  309.   }
  310. }
Today we’re taking the code further by automatically creating an item on a tool palette with the thumbnail of the recently-saved drawing which, when used, will run a script created when we saved the drawing.
Some notes on the changes:
Lines 5, 6 and 8 add some additional namespaces.
It's worth noting that you'll need to add an assembly reference to AcTcMgd (depending on the version of AutoCAD you're using), for the updated code to build.
Lines 28-32 add some additional constants related to scripts and icon images.
Lines 95-102 create additional directories for our scripts and images.
Lines 123-134 create a dummy script when we save a drawing.
Lines 154-173 deal with a limitation we have with sending strings to the command-line:
AutoCAD’s command-line input buffer is only 255 characters in size, so if our string is longer (because of a deep file path), we send it in two pieces, terminating the first with a space character. It’s still possible that really deep paths could cause a problem with this code, but splitting the string further is left as an exercise for the reader. :-)
The string also calls a new continuation function (registered via LISP, see below) to create an item on our tool palette.
Lines 185-241 register a continuation function via LISP, so we can get control back in our code once the SAVEAS command has completed.
Lines 243-332 define a function to create an item on our tool palette. This function is called from the above LISP function.
Lines 334-363 simulate the execution of a script, so that when our tool palette is used, something happens.
The command simply reads in the contents of the "script" file and prints the contents to the command-line.
Lines 365-374 define a dummy custom tool, which we use as a shortcut for certain tool palette-related operations.
Now let’s take a look at the results of running this. In the last post we saw an example where a number of drawings get created in a particular folder. If we perform the same operations with this code, the same things happen (no need to show the drawings or the command-line output, they should be the same), but in addition we see a tool palette populated with images of our model at various stages:

You may have to right-click the stacked tool palette tabs to locate the “Scripts” tool palette (I haven’t found a way of doing this automatically, as it wasn’t really essential for my particular application).
When we select the items on the tool palette in sequence, we see our EXECSCR command is called with the location of the script created for each DWG file, which then gets read and printed to the command-line:
  1. Command: _EXECSCR
  2. Enter location of script to execute:
  3. "C:/QSaveAs Test/Scripts/Solid model.txt"
  4. Dummy script contained: "This is a dummy script for Solid model."
  5. Command: _EXECSCR
  6. Enter location of script to execute:
  7. "C:/QSaveAs Test/Scripts/Solid model 1.txt"
  8. Dummy script contained: "This is a dummy script for Solid model 1."
  9. Command: _EXECSCR
  10. Enter location of script to execute:
  11. "C:/QSaveAs Test/Scripts/Solid model 2.txt"
  12. Dummy script contained: "This is a dummy script for Solid model 2."
  13. Command: _EXECSCR
  14. Enter location of script to execute:
  15. "C:/QSaveAs Test/Scripts/Solid model 3.txt"
  16. Dummy script contained: "This is a dummy script for Solid model 3."
  17. Command: _EXECSCR
  18. Enter location of script to execute:
  19. "C:/QSaveAs Test/Scripts/Solid model 4.txt"
  20. Dummy script contained: "This is a dummy script for Solid model 4."
  21. Command: _EXECSCR
  22. Enter location of script to execute:
  23. "C:/QSaveAs Test/Scripts/Solid model 5.txt"
  24. Dummy script contained: "This is a dummy script for Solid model 5."
复制代码
While clearly not actually doing anything useful, the script file that we’ve created (and I don’t mean script in the AutoCAD sense of the term – I’m using it in a more generic sense) could actually be regenerating the drawing contents (for instance). With a little more work. :-)

本帖子中包含更多资源

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

x
 楼主| 发表于 2010-2-3 22:21 | 显示全部楼层
十四、简化的QuickSaveAs命令(2010版本)
October 30, 2009
Streamlined QuickSaveAs command for AutoCAD 2010
A big thanks to Tony Tanzillo for providing some tips to help improve the implementation of the application we saw in these previous posts: in particular Tony pointed out the ability of AutoCAD 2010 to generate a thumbnail image for a document in the editor programmatically (something I had forgotten was possible… at least I think I knew it existed – it certainly seemed familiar once I saw it :-S). Anyway, the version of the code in this post will only work from AutoCAD 2010 onwards because of the use of this function, Document.CapturePreviewImage().
Tony’s code also showed some interesting capabilities of the .NET Framework related to filename and path manipulation, so I also borrowed some of those techniques to avoid some ugly string parsing/manipulation.
Because of this ability to generate thumbnails – something I really wanted from the beginning – we can avoid the use of SAVEAS and simply use Document.SaveAs(), which will save a copy of the drawing without renaming the version in the editor (which in my particular situation is desirable). And clearly there’s no longer any need for a continuation function (whether or not you believe that was an appropriate way to implement the application in the first place).
Here’s the updated C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Windows.ToolPalette;
  6. using System.Runtime.InteropServices;
  7. using System.Drawing;
  8. using System.IO;
  9. using System;
  10. namespace QuickSaveAs
  11. {
  12.   public class Commands
  13.   {
  14.     // Set up static variable for the path to our folder
  15.     // of drawings, as well as the base filename and a
  16.     // counter to make the unique filename
  17.     static string _path = "",
  18.                   _base = "";
  19.     static int _count = 0;
  20.     // Various filename and path-related constants
  21.     const string sfxSep = " ",
  22.                 pthSep = "\",
  23.                 lspSep = "/",
  24.                 dwgExt = ".dwg",
  25.                 scrExt = ".txt",
  26.                 bmpExt = ".bmp",
  27.                 bmpLoc = "Images",
  28.                 scrLoc = "Scripts";
  29.     // Our QuickSaveAs command
  30.     [CommandMethod("QSAVEAS")]
  31.     public void QuickSaveAs()
  32.     {
  33.       Document doc =
  34.         Application.DocumentManager.MdiActiveDocument;
  35.       Editor ed = doc.Editor;
  36.       Database db = doc.Database;
  37.       // If this is the first time run...
  38.       if (_path == "" || _base == "")
  39.       {
  40.         // Ask the user for a base file location
  41.         PromptSaveFileOptions opts =
  42.           new PromptSaveFileOptions(
  43.             "Select location to save first drawing file"
  44.           );
  45.         opts.Filter = "Drawing (*.dwg)|*.dwg";
  46.         PromptFileNameResult pr =
  47.           ed.GetFileNameForSave(opts);
  48.         if (pr.Status == PromptStatus.OK)
  49.         {
  50.           // If a file was selected, and it contains a path...
  51.           // Separate the path from the file name
  52.           _base =
  53.             Path.GetFileNameWithoutExtension(pr.StringResult);
  54.           _path =
  55.             Path.GetDirectoryName(pr.StringResult);
  56.           // Create folders for our icons and our scripts
  57.           Directory.CreateDirectory(
  58.             _path + pthSep + bmpLoc
  59.           );
  60.           Directory.CreateDirectory(
  61.             _path + pthSep + scrLoc
  62.           );
  63.         }
  64.       }
  65.       // Assuming the path and name were set appropriately...
  66.       if (_path != "" && _base != "")
  67.       {
  68.         string name = _base,
  69.               dwgFile;
  70.         // Add our suffix if not the first time run
  71.         do
  72.         {
  73.           if (_count > 0)
  74.             name += sfxSep + _count.ToString();
  75.           // Our drawing is located in the base path
  76.           dwgFile = _path + pthSep + name + dwgExt;
  77.           _count++;
  78.         }
  79.         while (File.Exists(dwgFile));
  80.         // While our script is in a sub-folder
  81.         string scrPath =
  82.           _path + pthSep + scrLoc + pthSep + name + scrExt;
  83.         // Create a dummy script, so we can make sure we pick
  84.         // up the contents in our dummy execute command
  85.         File.WriteAllText(
  86.           scrPath,
  87.           "This is a dummy script for " + name + "."
  88.         );
  89.         // Now we want to save our drawing and use the image
  90.         // for our tool icon
  91.         if (!string.IsNullOrEmpty(dwgFile))
  92.         {
  93.           Bitmap thumb = doc.CapturePreviewImage(320, 240);
  94.           doc.Database.ThumbnailBitmap = thumb;
  95.           doc.Database.SaveAs(dwgFile, DwgVersion.Current);
  96.           ed.WriteMessage(
  97.             "\nCopy of current document saved to {0}",
  98.             Path.GetFileName(dwgFile)
  99.           );
  100.           CreateCommand(thumb, name, scrPath);
  101.         }
  102.       }
  103.     }
  104.     // Function to add a command tool to our tool palette to
  105.     // execute the script
  106.     private void CreateCommand(
  107.       Bitmap thumb,
  108.       string name,
  109.       string scrPath
  110.     )
  111.     {
  112.       const string catName = "ScriptCatalog";
  113.       const string palName = "Scripts";
  114.       ToolPaletteManager tpm = ToolPaletteManager.Manager;
  115.       // Get the GUID of our dummy custom tool
  116.       Type t = typeof(DummyTool);
  117.       GuidAttribute ga =
  118.         (GuidAttribute)t.GetCustomAttributes(
  119.           typeof(GuidAttribute), false)[0];
  120.       Guid g = new Guid(ga.Value);
  121.       // Instanciate our dummy tool - this will allow us to use
  122.       // its helper functions
  123.       DummyTool tool = new DummyTool();
  124.       Catalog cat;
  125.       Palette pal = null;
  126.       // First we check whether our GUID is in a catalog
  127.       CatalogItem ci = tpm.StockToolCatalogs.Find(g);
  128.       if (ci != null)
  129.       {
  130.         // If it is, search each catalog for our palette
  131.         foreach(CatalogItem ci2 in tpm.Catalogs)
  132.         {
  133.           for (int i = 0; i < ci2.ChildCount; i++)
  134.           {
  135.             CatalogItem ci3 = ci2.GetChild(i);
  136.             if (ci3 != null && ci3.Name == palName)
  137.             {
  138.               pal = ci3 as Palette;
  139.               break;
  140.             }
  141.           }
  142.           if (pal != null)
  143.             break;
  144.         }
  145.       }
  146.       // If we didn't find our palette, create it
  147.       if (pal == null)
  148.       {
  149.         cat = tool.CreateStockTool(catName);
  150.         pal = tool.CreatePalette(cat, palName);
  151.       }
  152.       // To add our command tool instance we need an icon
  153.       ImageInfo ii = new ImageInfo();
  154.       if (thumb != null)
  155.       {
  156.         // Which we create from the Database's thumbnail
  157.         string bmpPath =
  158.           _path + pthSep + bmpLoc + pthSep + name + bmpExt;
  159.         thumb.Save(bmpPath);
  160.         ii.ResourceFile = bmpPath;
  161.       }
  162.       ii.Size = new System.Drawing.Size(65, 65);
  163.       // And then we use our dummy tool to create the
  164.       // command tool
  165.       tool.CreateCommandTool(
  166.         pal,
  167.         name,
  168.         ii,
  169.         "_EXECSCR "" + scrPath.Replace(pthSep, lspSep) + """
  170.       );
  171.       // Finally we reload the catalogs to display the change
  172.       tpm.LoadCatalogs(
  173.         CatalogTypeFlags.Catalog,
  174.         LoadFlags.LoadImages
  175.       );
  176.     }
  177.     // A dummy command to simulate the execution of our script
  178.     // (which simply reads the contents and displays them on
  179.     // the command-line)
  180.     [CommandMethod("EXECSCR")]
  181.     public void ExecuteScript()
  182.     {
  183.       Document doc =
  184.         Application.DocumentManager.MdiActiveDocument;
  185.       Editor ed = doc.Editor;
  186.       PromptResult pr =
  187.         ed.GetString(
  188.           "\nEnter location of script to execute: "
  189.         );
  190.       if (pr.Status == PromptStatus.OK)
  191.       {
  192.         string path =
  193.           pr.StringResult.Replace(lspSep, pthSep);
  194.         if (File.Exists(path))
  195.         {
  196.           string contents = File.ReadAllText(path);
  197.           ed.WriteMessage(
  198.             "\nDummy script contained: "{0}"",
  199.             contents
  200.           );
  201.         }
  202.       }
  203.     }
  204.   }
  205.   // Our dummy tool which simply derives from CustomToolBase
  206.   // (there may be a more straightforward way to get access
  207.   // to the helpers in CustomToolBase, but anyway)
  208.   [Guid("3B725500-0451-4081-A1BB-B37CE6A65767")]
  209.   [Tool("MyDummyTool", "IDB_TOOL")]
  210.   [ClassInterface(ClassInterfaceType.AutoDual)]
  211.   public class DummyTool : CustomToolBase
  212.   {
  213.   }
  214. }
The QSAVEAS command executes more quickly and cleanly, providing results comparable to the previous version’s:

本帖子中包含更多资源

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

x
 楼主| 发表于 2012-7-21 14:14 | 显示全部楼层
本帖最后由 雪山飞狐_lzh 于 2012-7-21 14:16 编辑

十五 从AutoCAD的可绑定对象层获取数据
Dumping data from AutoCAD’s Bindable Object Layer using .NET
Some time ago, I posted code that used the Autodesk.AutoCAD.Windows.Data namespace to list the hatch patterns in the current drawing.

Fenton Webb posted a follow-up on the AutoCAD DevBlog that took this further, extracting additional data from AutoCAD and using it to populate an Excel spreadsheet. Within that post, Fenton showed the technique required to access and iterate across other data collections – something I hadn’t managed to do when creating my original post.

Rather than repeat exactly what Fenton has put together – which is really nice, do take a look at it – I’m just taking a small amount of Fenton’s code and replicating it here – along with some code using reflection, to keep the code succinct – to dump some data to the command-line:

  1. using Autodesk.AutoCAD.ApplicationServices;

  2. using Autodesk.AutoCAD.EditorInput;

  3. using Autodesk.AutoCAD.Runtime;

  4. using Autodesk.AutoCAD.Windows.Data;



  5. public class DataStuff

  6. {

  7.   [CommandMethod("HPS")]

  8.   static public void GetHatchPatterns()

  9.   {

  10.     Editor ed =

  11.       Application.DocumentManager.MdiActiveDocument.Editor;

  12.     foreach (

  13.       string str in HatchPatterns.Instance.AllPatterns)

  14.       ed.WriteMessage("\n" + str);

  15.   }



  16.   [CommandMethod("DUMPBOL")]

  17.   static public void DumpBindableObjects()

  18.   {

  19.     Editor ed =

  20.       Application.DocumentManager.MdiActiveDocument.Editor;



  21.     // Get our set of collections



  22.     var cols = Application.UIBindings.Collections;



  23.     // Use reflection to determine the properties of type

  24.     // DataItemCollection (could extend to support others, too)



  25.     var t = cols.GetType();

  26.     var tprops = t.GetProperties();

  27.     foreach (var tprop in tprops)

  28.     {

  29.       // Make sure we only deal with DataItemCollection properties

  30.       // that do not take any parameters



  31.       if (

  32.         tprop.PropertyType == typeof(DataItemCollection) &&

  33.         tprop.GetGetMethod().GetParameters().Length == 0

  34.       )

  35.       {

  36.         // Dump our the property name first



  37.         ed.WriteMessage("\n{0}:", tprop.Name);



  38.         // Get the collection itself and iterate through it



  39.         var col = (DataItemCollection)tprop.GetValue(cols, null);

  40.         foreach (var desc in col)

  41.         {

  42.           // Get the collection's property descriptors



  43.           var props = desc.GetProperties();

  44.           try

  45.           {

  46.             // Dump the value of each "Name" property



  47.             ed.WriteMessage(

  48.               " \"{0}\"", props["Name"].GetValue(desc)

  49.             );

  50.           }

  51.           catch

  52.           {

  53.             // Just in case no "Name" property exists, we try-catch

  54.           }

  55.         }

  56.       }

  57.     }

  58.   }

  59. }

The main trick is actually to get data from the BOL – AutoCAD’s Bindable Object Layer – via the various properties inside Application.UIBindings.Collections.

We’re using reflection to avoid hardcoding the various properties we want to access: we just loop through the main collection set’s properties, looking for any of type DataItemCollection. We might also extend the code to support other property types – such as EnumItemCollection – but that’s left as an exercise for the reader.

Here’s the command-line output we see in a blank drawing when we run the DUMPBOL command:

Command: DUMPBOL

UcsPlanes:

PlotStyles:

RenderPresets: "" "" "" "" ""

TableCellStyles:

NamedViews:

Linetypes: "ByBlock" "ByLayer" "Continuous" "PHANTOM2"

LayerFilters: "All" "All Used Layers" "Unreconciled New Layers" "Viewport Overrides"

LayerStates:

Layers: "0"

VisualStyles: "2D Wireframe" "Conceptual" "Hidden" "Realistic" "Shaded" "Shaded with edges" "Shades of Gray" "Sketchy" "Wireframe" "X-Ray"

TextStyles: "Standard" "Annotative"

DimensionStyles: "Standard" "Annotative"

DetailViewStyles: "Imperial24"

SectionViewStyles: "Imperial24"

MleaderStyles: "Annotative" "Standard"

TableStyles: "Standard"
 楼主| 发表于 2012-7-21 14:22 | 显示全部楼层
十六 利用绑定对象层监控图层变化
Finding out about changes to AutoCAD layers via the Bindable Object Layer using .NET
In the last few posts on this topic, we saw some examples of getting information from and controlling AutoCAD via its Bindable Object Layer.

In this post, we’re going to look at a way to find out when changes are made to AutoCAD’s layer table: when layers are added, changed or removed.

There are certainly other ways to do this: you can use Database.ObjectAppended(), ObjectModified() and ObjectErased() to find out about changes to LayerTableRecords, for instance, but this is an alternative approach that may be interesting to some people.

In this implementation, we attach some event handlers to keep an eye on what’s happening in the BOL collection bound to the layer table. We also maintain a list of layer names so we have good information on what has changed (in terms of what layers have been removed, at least – if we wanted information on specific changes we’d need to cache more information than that). From this starting point, it should be possible for developers to get a more comprehensive mechanism that provides information on when commands create or remove multiple layers (something this particular implementation hasn’t specifically been designed to deal with or tested against).

Here’s the C# code:

  1. using Autodesk.AutoCAD.ApplicationServices;

  2. using Autodesk.AutoCAD.EditorInput;

  3. using Autodesk.AutoCAD.Runtime;

  4. using Autodesk.AutoCAD.Windows.Data;

  5. using System.Collections.Specialized;

  6. using System.Collections.Generic;

  7. using System.ComponentModel;



  8. public class BolInfo

  9. {

  10.   static private List<string> _layerNames = null;



  11.   [CommandMethod("LAYMODS")]

  12.   static public void GetNotifiedOnLayerChange()

  13.   {

  14.     // Get our layer list and extract the initial layer names



  15.     var layers = Application.UIBindings.Collections.Layers;

  16.     UpdateStoredLayerNames();



  17.     // Attach event handlers to the layer list...



  18.     // Find out when items are added or removed from the

  19.     // collection



  20.     layers.CollectionChanged +=

  21.       (s, e) =>

  22.       {

  23.         Editor ed =

  24.           Application.DocumentManager.MdiActiveDocument.Editor;



  25.         ed.WriteMessage("\nCollection changed: {0}", e.Action);



  26.         if (

  27.           e.Action == NotifyCollectionChangedAction.Add &&

  28.           e.NewStartingIndex == -1

  29.         )

  30.         {

  31.           // What happens for commands that create >1 layers?



  32.           var newLays = Application.UIBindings.Collections.Layers;

  33.           ed.WriteMessage(

  34.             "\nNew item: \"{0}\"",

  35.             GetItemValue(newLays[newLays.Count - 1])

  36.           );

  37.         }

  38.         else if (

  39.           e.Action == NotifyCollectionChangedAction.Remove

  40.         )

  41.         {

  42.           ed.WriteMessage(

  43.             "\nRemoved item: \"{0}\"",

  44.             _layerNames[e.OldStartingIndex]

  45.           );

  46.         }



  47.         // As we can't access data in e.NewItems or e.OldItems

  48.         // (they contain NewDataItem objects - a class that isn't

  49.         // exposed) get the collection again and list it



  50.         ed.WriteMessage("\nUpdated collection: ");



  51.         foreach (

  52.           var item in Application.UIBindings.Collections.Layers

  53.         )

  54.         {

  55.           ed.WriteMessage(" \"{0}\"", GetItemValue(item));

  56.         }

  57.         UpdateStoredLayerNames();

  58.       };



  59.     // Find out when items have been changed in the collection

  60.     // (although not what specifically has changed)



  61.     layers.ItemsChanged +=

  62.       (s, e) =>

  63.       {

  64.         Editor ed =

  65.           Application.DocumentManager.MdiActiveDocument.Editor;



  66.         ed.WriteMessage("\nItem(s) changed.");

  67.         UpdateStoredLayerNames();

  68.       };



  69.     // Find out when properties of the collection (typically

  70.     // the Count, for instance) have changed



  71.     layers.PropertyChanged +=

  72.       (s, e) =>

  73.       {

  74.         Editor ed =

  75.           Application.DocumentManager.MdiActiveDocument.Editor;



  76.         ed.WriteMessage(

  77.           "\nCollection property changed: {0}", e.PropertyName

  78.         );

  79.       };

  80.   }



  81.   // Store a cache of the layer names



  82.   private static void UpdateStoredLayerNames()

  83.   {

  84.     var layers = Application.UIBindings.Collections.Layers;



  85.     _layerNames = new List<string>(layers.Count);

  86.     foreach (var layer in layers)

  87.     {

  88.       _layerNames.Add(GetItemValue(layer));

  89.     }

  90.   }



  91.   // Extract the name of an item from the item descriptor



  92.   private static string GetItemValue(ICustomTypeDescriptor item)

  93.   {

  94.     return (string)item.GetProperties()["Name"].GetValue(item);

  95.   }

  96. }


When we run the custom LAYMODS command and then use the standard LAYER command to add, change and remove some layers, we can see the level of information provided:
  1. Command: LAYMODS

  2. Command: LAYER

  3. Command:

  4. Command:

  5. Collection changed: Add

  6. New item: "Layer1"

  7. Updated collection:  "0" "Layer1"

  8. Collection property changed: Count

  9. Command:

  10. Collection changed: Add

  11. New item: "Layer2"

  12. Updated collection:  "0" "Layer1" "Layer2"

  13. Collection property changed: Count

  14. Command:

  15. Collection changed: Add

  16. New item: "Layer3"

  17. Updated collection:  "0" "Layer1" "Layer2" "Layer3"

  18. Collection property changed: Count

  19. Command:

  20. Collection changed: Add

  21. New item: "Layer4"

  22. Updated collection:  "0" "Layer1" "Layer2" "Layer3" "Layer4"

  23. Collection property changed: Count

  24. Command:

  25. Collection changed: Add

  26. New item: "Layer5"

  27. Updated collection:  "0" "Layer1" "Layer2" "Layer3" "Layer4" "Layer5"

  28. Collection property changed: Count

  29. Command:

  30. Command:

  31. Collection changed: Add

  32. New item: "Layer7"

  33. Updated collection:  "0" "Layer1" "Layer2" "Layer3" "Layer4" "Layer5" "Layer6" "Layer7"

  34. Collection property changed: Count

  35. Command:

  36. Collection changed: Add

  37. New item: "Layer8"

  38. Updated collection:  "0" "Layer1" "Layer2" "Layer3" "Layer4" "Layer5" "Layer6" "Layer7" "Layer8"

  39. Collection property changed: Count

  40. Command:

  41. Collection changed: Remove

  42. Removed item: "Layer3"

  43. Updated collection:  "0" "Layer1" "Layer2" "Layer4" "Layer5" "Layer6" "Layer7" "Layer8"

  44. Collection property changed: Count

  45. Command:

  46. Collection changed: Remove

  47. Removed item: "Layer4"

  48. Updated collection:  "0" "Layer1" "Layer2" "Layer5" "Layer6" "Layer7" "Layer8"

  49. Collection property changed: Count

  50. Command:

  51. Collection changed: Remove

  52. Removed item: "Layer5"

  53. Updated collection:  "0" "Layer1" "Layer2" "Layer6" "Layer7" "Layer8"

  54. Collection property changed: Count

  55. Command:

  56. Collection changed: Remove

  57. Removed item: "Layer6"

  58. Updated collection:  "0" "Layer1" "Layer2" "Layer7" "Layer8"

  59. Collection property changed: Count

  60. Command:

  61. Item(s) changed.

  62. Command:

  63. Item(s) changed.

  64. Command:

  65. Command:

  66. Collection changed: Add

  67. New item: "Layer4"

  68. Updated collection:  "0" "Layer1" "Layer2" "Layer7" "Layer8" "Layer3" "Layer4"

  69. Collection property changed: Count

  70. Command:

  71. Collection changed: Add

  72. New item: "Layer5"

  73. Updated collection:  "0" "Layer1" "Layer2" "Layer7" "Layer8" "Layer3" "Layer4" "Layer5"

  74. Collection property changed: Count

  75. Command:

  76. Collection changed: Add

  77. New item: "Layer6"

  78. Updated collection:  "0" "Layer1" "Layer2" "Layer7" "Layer8" "Layer3" "Layer4" "Layer5" "Layer6"

  79. Collection property changed: Count
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-19 05:24 , Processed in 0.349987 second(s), 17 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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