@jwe03 wrote:
Yes you're right, the user form is modeless.
I created it using the visual basic wizard not through code. So, i guess this is the default behavior. And I need it to be like that in my application
Is there a way to work around that?
I don't have VB.NET code to show how to work around it, but the way to do that is to define a CommandMethod that does not use the CommandFlags.Session flag, and in that method, call the Editor's Command() method to do what you need. From the click handler of your button, use the Document's SendStringToExecute() method to run the command method you defined.
In AutoCAD 2016 or later you can also use ExecuteInCommandContextAsync(), which is really just a gift-wrapped version of that very same method. If you need to run your code on AutoCAD 2015 or earlier, you can just use the method I described which does the same thing as ExecuteInCommandContextAsync().
See the code below and the attached file. If you find it's too complicated or more than you need, then you can find a less-complete/complicated version of it in >this post<.
For anyone who may be struggling with this and other context-related issues, the code below is the usage sample portion of the attached file, which is a more complete version of the code in the post I referred to above.
I honestly don't know if this will convert to VB without errors but you can always give it a try.
////////////////////////////////////////////////////////////////////////
/// USAGE EXAMPLE
///
/// The above code is API, and everything that
/// follows is example code demonstrating the
/// use of the API code.
/// All example code is accessed from buttons
/// on a PaletteSet that can be activated using
/// the DCPALETTE command.
///
namespace InvokeExample
{
using Autodesk.AutoCAD.ApplicationServices;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;
using System.Windows.Forms;
using Autodesk.AutoCAD.Geometry;
using System.Drawing;
/// <summary>
/// A specialization of System.Windows.Forms.Button whose
/// Click event handler executes in the document context.
/// </summary>
public class CommandButton : Button
{
protected override void OnClick(EventArgs e)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
if(doc!=null)
doc.Invoke(() => base.OnClick(e));
}
}
/// <summary>
/// An example UserControl hosted by the included PalletteSet.
/// </summary>
public class MyUserControl : UserControl
{
Button button1;
Button button2;
Button button3;
Button button4;
CommandButton commandButton1;
public MyUserControl()
{
this.Dock = DockStyle.Fill;
button1 = new Button();
button1.Text = "Draw a Circle";
button1.AutoSize = true;
button1.Click += button1_Click;
this.Controls.Add(button1);
button2 = new Button();
button2.Text = "Copy objects to new Document";
button2.Click += button2_Click;
this.Controls.Add(button2);
button3 = new Button();
button3.Text = "Draw rectangles in each open Document";
button3.Click += button3_Click;
this.Controls.Add(button3);
commandButton1 = new CommandButton();
commandButton1.Text = "Draw Circles using CommandButton";
commandButton1.Click += commandButton1_Click;
this.Controls.Add(commandButton1);
button4 = new Button();
button4.Text = "Batch process drawing files";
button4.Click += button4_Click;
this.Controls.Add(button4);
// Bind the UserControl's Enabled property to the
// EditorStateView's HasQuiescentDocument property,
// to automatically enable/disable the UserControl
// when a quiescent document is/is not active.
this.DataBindings.Add("Enabled", EditorStateView.Instance, "HasQuiescentDocument");
}
protected override void OnLayout(LayoutEventArgs e)
{
this.SuspendLayout();
base.OnLayout(e);
int top = 0;
foreach(Control control in this.Controls)
{
control.Height = control.GetPreferredSize(new Size(1, 1)).Height;
control.SetMargins(margin, Alignment.Horizontal);
control.Center(Alignment.Horizontal);
top = top == 0 ? margin : top + control.Height + margin;
control.Top = top;
}
this.ResumeLayout(false);
}
const int margin = 6;
/// The click handler for button1 on the UserControl calls
/// the Invoke() method and passes a delegate that will
/// execute in the document context, asynchronously.
///
/// When the button is clicked the delegate executes and
/// calls the Editor's Command() method to draw a circle.
void button1_Click(object sender, EventArgs e)
{
/// Ask the ContextManager if its possible to
/// use the Invoke() method at this point:
Document doc = Application.DocumentManager.MdiActiveDocument;
if(doc != null && doc.CanInvoke())
{
/// The call to Invoke() is usually the last statement
/// in the event handler, as any code that follows it
/// will execute <em>before</em> the delegate runs.
doc.Invoke(delegate()
{
/// The code within this delegate runs in the
/// document context, making it possible to use
/// the Command() method, so let's draw a circle:
doc.Editor.Command("._CIRCLE", "10,10", "5");
});
/// Code appearing here runs in the application context
/// <em>before</em> the code in the above delegate runs.
/// Do not place code here that depends on side-effects
/// of code in the above delegate, or that must run after
/// the above delegate executes.
}
}
/// <summary>
/// The click handler for the "Copy objects to new Document"
/// button demonstrates how to use the OnEndInvoke() method to
/// execute code in the application context, after the delegate
/// passed to Invoke() (which runs in the document context) has
/// returned. It also shows how you can chain together Invoke()
/// and OnEndInvoke() delegates to repeatedly switch between the
/// document and application contexts to perform more complex
/// operations involving multiple open documents, and operations
/// that involve opening, activating, and closing documents.
///
/// The button click handler will prompt for a selection of
/// objects, and then issue the COPYBASE command to copy the
/// selected objects to the clipboard.
///
/// Then, it creates a new document, acivates it, and issues
/// the PASTEORIG command to paste the copied objects into the
/// new document.
///
/// Finally, it switches back to the original document from
/// which the objects were copied.
/// </summary>
void button2_Click(object sender, EventArgs e)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
if(doc != null && doc.CanInvoke())
{
PromptSelectionResult psr;
/// Prevent the user from switching documents while
/// they are selecting objects:
Application.DocumentManager.DocumentActivationEnabled = false;
try
{
/// Set the focus to the drawing view:
doc.Editor.SetFocus();
/// Get a selection set of objects from the user:
psr = doc.Editor.GetSelection();
if(psr.Status != PromptStatus.OK)
return;
}
finally
{
/// enable document activation:
Application.DocumentManager.DocumentActivationEnabled = true;
}
// all code in this method up to this point
// runs in the application context.
doc.Invoke(delegate()
{
// this code runs in the document context:
doc.Editor.Command("._COPYBASE", Point3d.Origin, psr.Value, "");
doc.OnEndInvoke(delegate()
{
// this code runs in the application context:
Document newDoc = Application.DocumentManager.Add("acad");
newDoc.Invoke(delegate()
{
// this code runs in the document context:
newDoc.Editor.Command("._PASTEORIG");
newDoc.OnEndInvoke(delegate()
{
// this code runs in the application context:
Application.DocumentManager.MdiActiveDocument = doc;
});
});
});
});
}
}
/// <summary>
/// The third button click handler shows how to use the
/// ForEach() extension method of the DocumentCollection,
/// to execute a delegate in the document context multiple
/// times, once for each open document.
///
/// The delegate accepts a single argument of type Document,
/// which is the Document that it is being invoked in, and
/// uses that argument to work with the document.
///
/// The delegate must not close the document or switch to
/// another document.
///
/// The example draws 2 rectangles in every open document.
/// </summary>
void button3_Click(object sender, EventArgs e)
{
Application.DocumentManager.ForEach(
delegate(Document doc)
{
doc.Editor.Command("._RECTANG", "8,8", "12,10");
doc.Editor.Command("._RECTANG", "7,7", "13,11");
}
);
}
/// <summary>
/// The Click event handler for commandButton1 demonstrates
/// the use of the included CommandButton class, whose click
/// event handler executes in the document context.
///
/// The handler draws 2 circles using the Command() method.
///
/// Notice that unlike the other button event handlers,
/// there's no use of the Invoke() method at all. Rather,
/// the code in the click handler can quite simply assume
/// it's running in the document context, thereby making
/// the task of switching to the document context totally
/// transparent to the programmer.
/// </summary>
void commandButton1_Click(object sender, EventArgs e)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
doc.Editor.Command("._CIRCLE", "8,8", "4");
doc.Editor.Command("._CIRCLE", "8,8", "2");
}
/// <summary>
/// The click event for button4 demonstrates how to use
/// the second overload of the ForEach() extension method
/// to batch-process multiple DWG files selected from a
/// file dialog.
/// </summary>
/// <remarks>
/// Each file in the argument list is sequentially opened
/// and the delegate invoked with the Document representing
/// the open file. When the delegate returns, the document
/// is closed, and processing proceeds to the next file.
///
/// The process performed on each file:
///
/// 1. Switch to model space
/// 2. Zoom Extents
/// 3. Save the file
///
/// The delegate that performs the processing of each file
/// executes in the document context, allowing the use of
/// the Command() method. The delegate must save the file
/// if necessary, as it will not be saved automatically,
/// and any changes made by the delegate will be discarded.
/// </remarks>
void button4_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.AddExtension = true;
ofd.Filter = "Drawing files|*.dwg";
ofd.FilterIndex = 0;
ofd.Multiselect = true;
ofd.DefaultExt = ".dwg";
if(ofd.ShowDialog() == DialogResult.OK && ofd.FileNames.Length > 0)
{
Application.DocumentManager.ForEach(ofd.FileNames,
delegate(Document doc)
{
doc.Editor.Command("._TILEMODE", "1");
doc.Editor.Command("._ZOOM", "_E");
doc.Save();
}
);
}
}
}
/// A PaletteSet that hosts the above UserControl
///
/// This PaletteSet also demonstrates best-practices
/// for consuming a PaletteSet, which is by deriving
/// a class from the PaletteSet class, and placing all
/// of the initialization code in the constructor of
/// the derived class, rather than in some other place
/// (like a command handler). The practice is identical
/// to the way Windows Forms are consumed, serving to
/// make a PaletteSet a self-contained component having
/// no dependence on any other code or component.
///
/// The custom PaletteSet class also uses the Singleton
/// pattern to marshal and manage the single reference
/// to the PaletteSet, which helps to simplify its use.
/// The command method that shows the PaletteSet is also
/// incorporated into the custom class, if you follow
/// this pattern, remember that command methods that are
/// added to a PaletteSet class must be static methods.
public class DCPaletteSet : PaletteSet
{
/// <summary>
/// Static singleton instance:
/// </summary>
static DCPaletteSet instance;
MyUserControl control;
public DCPaletteSet() : base("MyPaletteSet")
{
control = new MyUserControl();
Add("MyUserControl", control);
this.DockEnabled = DockSides.None;
this.Visible = true;
this.Dock = DockSides.None;
}
protected override void Dispose(bool disposing)
{
if(instance == this)
instance = null;
if(control != null)
{
control.Dispose();
control = null;
}
base.Dispose(disposing);
}
/// <summary>
/// A command to create/show the example PaletteSet
/// </summary>
[CommandMethod("DCPALETTE", CommandFlags.Session)]
public static void ShowCommand()
{
if(instance == null)
instance = new DCPaletteSet();
instance.Visible = true;
}
}
/// <summary>
/// WinForm Control support extension methods:
/// </summary>
public static class MyControlExtensions
{
public static void Center(this Control control, Alignment alignment)
{
Control parent = control.Parent;
if(parent != null)
{
if(alignment.HasFlag(Alignment.Horizontal))
control.Left = (parent.Width - control.Width) / 2;
if(alignment.HasFlag(Alignment.Vertical))
control.Top = (parent.Height - control.Height) / 2;
}
}
public static void SetMargins(this Control control, int value, Alignment alignment)
{
Control parent = control.Parent;
if(parent != null)
{
if(alignment.HasFlag(Alignment.Horizontal))
control.Width = parent.Width - (value * 2);
if(alignment.HasFlag(Alignment.Vertical))
control.Height = parent.Height - (value * 2);
}
}
}
[Flags]
public enum Alignment
{
Horizontal = 1,
Vertical = 2,
Both = 4
}
}