Hi,
I used send command for Export Layout
But the line only executes after exiting command method.
Can anyone help in this case.
thanks in advance.
SendCommand is asynchronous, and works that way.
If you are on a version of AutoCAD that uses .NET 3.5 or later, you can use this wrapper for RunCommand
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; using System.Reflection; using Autodesk.AutoCAD.ApplicationServices; namespace Autodesk.AutoCAD.EditorInput { public static class EditorInputExtensionMethods { public static PromptStatus Command( this Editor editor, params object[] args ) { if( editor == null ) throw new ArgumentNullException( "editor" ); return runCommand( editor, args ); } static Func<Editor, object[], PromptStatus> runCommand = GenerateRunCommand(); static Func<Editor, object[], PromptStatus> GenerateRunCommand() { MethodInfo method = typeof( Editor ).GetMethod( "RunCommand", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public ); var instance = Expression.Parameter( typeof( Editor ), "instance" ); var args = Expression.Parameter( typeof( object[] ), "args" ); return Expression.Lambda<Func<Editor, object[], PromptStatus>>( Expression.Call( instance, method, args ), instance, args ) .Compile(); } } }
With the above code, you can use this:
Document doc = Application.DocumentManager.MdiActiveDocument; Editor editor = doc.Editor; editor.Command( "._EXPORTLAYOUT", "filename-goes-here" );
Included the wrapper. Even now it is not working.
Following is the code i'm using,
Document doc = Application.DocumentManager.MdiActiveDocument;
Database AcDb = doc.Database;
using (Transaction tr = AcDb.TransactionManager.StartTransaction())
{
LayoutManager AcLtMgr = LayoutManager.Current;
DBDictionary layoutDic = tr.GetObject(AcDb.LayoutDictionaryId, OpenMode.ForRead, false) as DBDictionary;
ObjectId layoutId;
Layout layout;
foreach (DBDictionaryEntry entry in layoutDic)
{
layoutId = entry.Value;
layout = tr.GetObject(layoutId, OpenMode.ForRead) as Layout;
if (!layout.LayoutName.Equals("Model"))
{
LayoutManager.Current.CurrentLayout = layout.LayoutName;
string filename = Path.Combine(Path.GetTempPath(), layout.LayoutName + ".dwg");
Editor editor = doc.Editor;
editor.Command("._EXPORTLAYOUT", filename);
}
}
tr.Commit();
}
HI,
Sorry, for not explaining clearly.
I am trying to export the layout to new drawing.
So, I used ExpotLayout.
But i'm not getting the output file.
The Command() method shouldn't be used inside an active Transaction, so first, commit the transaction before you use the Command() method. You can also skip all of that code that gets the current layout name by just reading the CTAB system variable, and do away with the use of the transaction entirely.
When using the Command() method, you are basically using a managed wrapper for the LISP (command) function, so the CMDECHO system variable should be set to 0 under normal use, but should be set to 1 when you're debugging so that you can see the prompts issued by the commands. Try running the code with CMDECHO set to 1 and see what the command is doing.
Just to be sure there wasn't a problem with my wrapper for RunCommand, I added your use case to the set of tests and examples I have for it.
Here's a working example:
/// ExportLayouts.cs (c) 2013 Tony Tanzillo /// /// AutoCAD.NET API sample that automates /// the EXPORTLAYOUT command to export all /// layouts in the current document. /// /// This example requires EditorExtensions.cs using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Linq.Expressions; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; namespace ExportLayoutsExample { public static class ExportLayoutsCommands { /// /// Automates the EXPORTLAYOUT command to export /// all paper space layouts to .DWG files. /// /// In this example, we export each layout to /// a drawing file in the same location as the /// current drawing, wherein each file has the /// name "_.dwg". /// /// This is not a functionally-complete example: /// /// No checking is done to see if any of the /// files already exist, and existing files /// are overwritten without warning or error. /// /// No checking is done to detect if an existing /// file exists and is in-use by another user, or /// cannot be overwritten for any other reason. /// /// No checking is done to ensure that the user /// has sufficient rights to write files in the /// target location. /// /// You can and should deal with any or all of /// the above as per your own requirements. /// /// [CommandMethod( "EXPORTLAYOUTS" )] public static void ExportLayoutsCommand() { var doc = Application.DocumentManager.MdiActiveDocument; var db = doc.Database; var editor = doc.Editor; try { if( (short) Application.GetSystemVariable( "DWGTITLED" ) == 0 ) { editor.WriteMessage( "\nCommand cannot be used on an unnamed drawing" ); return; } string format = Path.Combine( Path.GetDirectoryName( doc.Name ), Path.GetFileNameWithoutExtension( doc.Name ) ) + "_{0}.dwg"; string[] layoutNames = null; using( Transaction tr = doc.TransactionManager.StartTransaction() ) { // Get the localized name of the model tab: BlockTableRecord btr = (BlockTableRecord) SymbolUtilityServices.GetBlockModelSpaceId( db ) .GetObject( OpenMode.ForRead ); Layout layout = (Layout) btr.LayoutId.GetObject( OpenMode.ForRead ); string model = layout.LayoutName; // Open the Layout dictionary: IDictionary layouts = (IDictionary) db.LayoutDictionaryId.GetObject( OpenMode.ForRead ); // Get the names of all paper space layouts into a list: layoutNames = layouts.Keys.Cast() .Where( name => name != model ).ToArray(); tr.Commit(); } int cmdecho = 0; #if DEBUG cmdecho = 1; #endif using( new ManagedSystemVariable( "CMDECHO", cmdecho ) ) using( new ManagedSystemVariable( "CMDDIA", 0 ) ) using( new ManagedSystemVariable( "FILEDIA", 0 ) ) using( new ManagedSystemVariable( "CTAB" ) ) { foreach( string name in layoutNames ) { string filename = string.Format( format, name ); editor.WriteMessage( "\nExporting {1}\n", name, filename ); Application.SetSystemVariable( "CTAB", name ); editor.Command( "._EXPORTLAYOUT", filename ); } } } catch( System.Exception ex ) { #if DEBUG editor.WriteMessage( ex.ToString() ); #else throw ex; #endif } } } public static class EditorInputExtensionMethods { public static PromptStatus Command( this Editor editor, params object[] args ) { if( editor == null ) throw new ArgumentNullException( "editor" ); return runCommand( editor, args ); } static Func<Editor, object[], PromptStatus> runCommand = GenerateRunCommand(); static Func<Editor, object[], PromptStatus> GenerateRunCommand() { MethodInfo method = typeof( Editor ).GetMethod( "RunCommand", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public ); var instance = Expression.Parameter( typeof( Editor ), "instance" ); var args = Expression.Parameter( typeof( object[] ), "args" ); return Expression.Lambda<Func<Editor, object[], PromptStatus>>( Expression.Call( instance, method, args ), instance, args ) .Compile(); } } /// /// Automates saving/changing/restoring system variables /// public class ManagedSystemVariable : IDisposable { string name = null; object oldval = null; public ManagedSystemVariable( string name, object value ) : this( name ) { Application.SetSystemVariable( name, value ); } public ManagedSystemVariable( string name ) { this.name = name; this.oldval = Application.GetSystemVariable( name ); } public void Dispose() { if( oldval != null ) { object temp = oldval; oldval = null; Application.SetSystemVariable( name, temp ); } } } }
The above posted code will not compile as-is because the Autodesk discussion group server has mangled the code (removing some angle braces).
A copy that actually compiles can be found here:
hi DiningPhilosopher:
I run the command exportlayouts, but failed to export a dwg file.
Is there any contition to use the wrapper method?
the system?or vs version? i am in win7x64+vs2010+.net4.0+autocad2013
Regards
swaywood
swaywood a écrit :
in autocad 2016 not work too
Since AutoCAD 2015 you have to use the Editor.Command() (or CommandAsync()) method instead of the wrapper of Editor.Runcommand() provided by tony Tanzillo.
See these threads:
https://www.theswamp.org/index.php?topic=49124.msg542370#msg542370
@_gile wrote:
swaywood a écrit :in autocad 2016 not work too
Since AutoCAD 2015 you have to use the Editor.Command() (or CommandAsync()) method instead of the wrapper of Editor.Runcommand() provided by tony Tanzillo.
See these threads:
https://www.theswamp.org/index.php?topic=49124.msg542370#msg542370
Hi @_gile, regarding your swamp post:
The last shown behavior is interesting, for example, to pass a list of arguments to a command (e.g. a points list for LINE, PLINE, SPLINE).
This also requires the CommandAsync using.
public async void Cmd3() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Point2d[] pts = { new Point2d(0.0, -10.0), new Point2d(10.0, 0.0), new Point2d(0.0, 10.0), new Point2d(-10.0, 0.0) }; await ed.CommandAsync("_.PLINE"); foreach (Point2d pt in pts) { await ed.CommandAsync(pt); } await ed.CommandAsync("_close"); ZoomEntLast(); }
This is not true at all.
When a method takes a variable number of arguments of a specific type, you can pass individual arguments separated by commas, or you can pass all arguments in a single array of the same type as the params array.
[CommandMethod("COMMANDARGS")] public static void GilesCommandExample() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Point2d[] points = { new Point2d(0.0, -10.0), new Point2d(10.0, 0.0), new Point2d(0.0, 10.0), new Point2d(-10.0, 0.0) }; object[] args = new object[] {"._PLINE"} .Concat(points.Cast<Object>()) .Concat(new [] {"", "._ZOOM", "_Extents"}) .ToArray(); // For any type 'T', if a method takes a 'params T[] args', // you can pass individual arguments of type T, or you can // pass all arguments contained in a single array of T[]: ed.Command(args); }
But we can make things a bit easier as well:
public static class MyCommandHelpers { public static object[] BuildList(this Editor editor, params object[] args) { return BuildListWorker(args).ToArray(); } static IEnumerable<object> BuildListWorker(IEnumerable args) { if(args != null) { foreach(object o in args) { if(o != null) { IEnumerable items = o as IEnumerable; if(items != null && !(items is string)) { foreach(object item in BuildListWorker(items)) { yield return item; } } else yield return o; } } } } } [CommandMethod("COMMANDARGS2")] public static void CommandExample2() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; Point2d[] points = { new Point2d(0.0, -11.0), new Point2d(11.0, 0.0), new Point2d(0.0, 11.0), new Point2d(-11.0, 0.0) }; ed.Command(ed.BuildList("._PLINE", points, "", "._ZOOM", "_Extents")); }
BuildList() works with any type of IEnumerable, for example:
[CommandMethod("CHANGETORED")] public static void CommandArgs3() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; var psr = ed.GetSelection(); if(psr.Status == PromptStatus.OK) { ed.Command(ed.BuildList("._CHPROP", psr.Value.GetObjectIds(), "", "color", 1, "")); } }
You're right about the possibility of building an array to pass a collection of points to Command(). My purpose was more to compare Command() vs CommandAsync() than to explain how the params keyword works.
That said, it seems to me you are not completely right. According to my tests, when you do:
ed.Command (ed.BuildList ("._ PLINE", points, "", "._ZOOM", "_Extents"));
The command stops after drawing the polyline and does not zoom.
This does work as expected:
await ed.CommandAsync("_.pline"); foreach (var pt in points) await ed.CommandAsync(pt); await ed.CommandAsync("", "_.zoom", "_extents");
@_gile wrote:
You're right about the possibility of building an array to pass a collection of points to Command(). My purpose was more to compare Command() vs CommandAsync() than to explain how the params keyword works.
That said, it seems to me you are not completely right. According to my tests, when you do:ed.Command (ed.BuildList ("._ PLINE", points, "", "._ZOOM", "_Extents"));The command stops after drawing the polyline and does not zoom.
This does work as expected:
await ed.CommandAsync("_.pline"); foreach (var pt in points) await ed.CommandAsync(pt); await ed.CommandAsync("", "_.zoom", "_extents");
Hi @_gile.
I wrote that example without testing it, and yes the ZOOM requires a separate call to Command() that follows the one that starts and ends PLINE, but the use of additional commands after finishing PLINE wasn't really what this is about.
It is about being able to build an array containing a variable number of arguments and passing that array in a single call to Command() rather than having to express each argument literally, which doesn't require the use of CommandAsync() at all.
So, a more-correct example would use a second call to Command() for running each subsequent command, still not requiring any use of CommandAsync().
After searching this discussion group, I see that many seem to be confused about this, and are using CommandAsync() in cases where it isn't really needed. The only case where one really needs to use CommandAsync() is one where there is a pause for user input (something I would never do), or a case where not all of the arguments are known at the point when the command starts. IOW, the need for CommandAsync() is actually quite rare.
It's helpful to illustrate the differences between the two methods. Although I think it would be even more helpful to demonstrate the use of CommandAsync() in a case where it is actually necessary. And, because I don't ever pause for user input in the middle of a command, I'm at a loss to come up with any other use case where it would be needed.
Can't find what you're looking for? Ask the community or share your knowledge.