Author Topic: Modeless Dialog help  (Read 9612 times)

0 Members and 1 Guest are viewing this topic.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Modeless Dialog help
« on: January 09, 2007, 08:02:47 PM »
I am doing my first modeless dialog routine for Acad 2006 in .net, and need some help.
When done, the tool I am making will let you pick an entity, then show a dialog (or update it if shown) with its properties.
The dialog will also have ways to change the items layer properties, and buttons to step up or down in the nesting level.
Its a rewrite of the free ChLayColt tool on cadthinking.com, I will give out complete code along the way and when done so people can learn from this project.
I am using VB.net and VS2005.

I started by creating the form.  I did not do a user control since I don't want a palette in acad, just a form.
Question 1) can I use a user control to make a modeless dialog, just like how its done for a paletteset for acad?  the adesk labs give an example for a palette but not for a modeless dialog...

So I am going to give this tool a command name of CCM for the moment, since its fast to type.
I outlined the tasks to do with comments, some are blank.
This code for the form works fine though so far, compiles, loads into acad, and shows fine:

<CommandMethod("CCM")> _
    Public Sub ShowModalForm()
        'get entity pick

        'get properties

        'show dialog box if not already

        'lock doc
        Dim docLock As DocumentLock
        Try
            docLock = Application.DocumentManager.MdiActiveDocument.LockDocument()
            Dim modelessForm As frmMain = New frmMain()
            Application.ShowModelessDialog(modelessForm)
            docLock.Dispose()
        Catch ex As Exception

        End Try
    End Sub

Question 2) Is the docLocking necessary?  I read it was for modeless dialogs.  Clarification requested if its needed when showing a form, or just when an action spawned by the form has to do something involving acad, like change a layer property.

Question 3) Since this dialog will stay open, how do I test if its already open when CCM is run?  Obviously I'm new at .net, I could not find info on this one anywhere though.

Thanks for any help, feel free to point me to other examples of modeless dialogs, I could not find any.
James Maeding

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Modeless Dialog help
« Reply #1 on: January 10, 2007, 12:13:08 AM »
maybe you can try

Code: [Select]
Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog();

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Modeless Dialog help
« Reply #2 on: January 10, 2007, 12:23:59 AM »
maybe you can try

Code: [Select]
Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog();


return this.Replace("maybe", "absolutely", 0, this.Length);
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #3 on: January 10, 2007, 12:28:52 AM »
that's what I did, and it does maintain the ownership of the form.  When acad minimizes, so does the form.
I did see some examples of checking for modeless forms with general .net, not within acad.  I will see if I can adapt them.
James Maeding

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #4 on: January 10, 2007, 01:03:01 AM »
I looked over the C# code posted, and I do not see how that addresses what I was asking.  The code I posted worked fine and showed the modeless dialog in acad.
I need to figure out how to test if its already shown next time CCM is run.
I also want to understand if doclocking is needed.
thx
James Maeding

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Modeless Dialog help
« Reply #5 on: January 10, 2007, 01:15:15 AM »
Try this solution
it's ugly but it works :)

added this is for Autocad 2007 you will need to change some code for 2006

enjoy


Code: [Select]
using System;
using Autodesk.AutoCAD.Runtime;

[assembly: CommandClass(typeof(ClassLibrary.CRPClass))]

namespace ClassLibrary
{
  public class CRPClass
  {
    public CRPClass()
    {
    }
    [CommandMethod("Jim")]
    static public void test() // This method can have any name
    {
      forjames.Form1 myform = new forjames.Form1();
      Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog(myform);
    }

  }
}


Code: [Select]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
//
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;

namespace forjames
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
    //for 2006
    /*[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "?acedPostCommand@@YAHPBD@Z")]
    extern static public int acedPostCommand(string cmd);*/
    //for 2007
    [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode,
    EntryPoint = "?acedPostCommand@@YAHPB_W@Z")]
    extern static public int acedPostCommand(string cmd);
    //a new setfocus
    [DllImport("user32.dll")]
    private static extern System.IntPtr SetFocus(System.IntPtr hwnd);
    //
    private void button_ok_Click(object sender, EventArgs e)
    {
      this.Close();
    }
    //
    private void button_get_Click(object sender, EventArgs e)
    {
      Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
      PromptEntityOptions entopts = new PromptEntityOptions("Pick an entity of your choice from the drawing");
      entopts.Message = "Pick an entity of your choice from the drawing";
      PromptEntityResult ent = null;
      try
      {
        SetFocus(Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Handle);
        acedPostCommand("");
        ent = ed.GetEntity(entopts);

        if (ent.Status != PromptStatus.Error)
        {
          ObjectId entid = ent.ObjectId;
          Database db = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;
          Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
          using (Transaction myT = tm.StartTransaction())
          {
            Entity entity = (Entity)tm.GetObject(entid, OpenMode.ForRead, true);
            this.textBox1.Text = entity.GetType().Name;
            myT.Commit();
          }
        }
        this.Focus();
      }
      catch
      {

      }
    }
  }
}

edit ;; put more of the code in the try {}
« Last Edit: January 10, 2007, 01:36:56 AM by Danielm103 »

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Modeless Dialog help
« Reply #6 on: January 10, 2007, 02:05:24 AM »
I am a little confused about "locking the document", I have never done this with any of my forms whether Modeless or not.
Maybe it’s because I always try to activate the active document before calling any methods.
Maybe one of the experts can shine a light on this as I am interested too.

 I had a lot of problems with forms pre 2007. I work with dual monitors and if my command line was not docked,
cad got a little confused on which window was active. That’s when I started using the p/invoke stuff.
So there might be better ways of setting the focus with 2007+

Dan
« Last Edit: January 10, 2007, 02:09:40 AM by Danielm103 »

Glenn R

  • Guest
Re: Modeless Dialog help
« Reply #7 on: January 10, 2007, 09:55:34 AM »
Keep a private class level variable of your form's type and check each time the command is run for it's existence - if there, the dialog is active and do nothing. When someone closes the form, nullify the variable for next run OR just hide it and reshow - it's up to you.

As far as locking the document, you must do this if you're executing from the application context, which a modeless dialog is.

Cheers,
Glenn.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Modeless Dialog help
« Reply #8 on: January 10, 2007, 11:33:09 AM »
Sorry James, I miss read your post.
Just a question though, how are thinking of getting the objectid of the selected entity from your modal dialog to your ModelessDialog?

I don’t know how to do this with .NET but you might be able to get the hWnd (window handle) of your ModelessDialog to detect whether it’s open.
« Last Edit: January 10, 2007, 11:41:54 AM by Danielm103 »

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #9 on: January 10, 2007, 01:08:36 PM »
No problem, any input helps.
The tool will only have one dialog, not two.  Assume for this command, that you are in Acad. Its not a tool that gets run from the outside somehow.
The lisp version of the tool does it with a modal so the modeless part is not critical, its almost an excercise to understand how to do it. It will make the tool slicker though, no doubt.

The class level variable is exactly what I had seen for general .net modeless dialogs.  That makes sense because the existance of the form object itself tells all.
I need some help on that approach though.
The private variable gets set to nothing after the form is shown.  I found this during debug, the variable has a value (a class id) in the ShowModelessForm method, but nothing when the close button callbak fires.
Here is the current code:

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Windows
Imports Autodesk.AutoCAD.Runtime

Public Class frmMain
    Inherits System.Windows.Forms.Form
    Private modelessForm As frmMain '<----- Private variable discussed

    <CommandMethod("CCM")> _
    Public Sub ShowModelessForm()
        'show dialog box if not already
        Dim docLock As DocumentLock
        If Me.modelessForm Is Nothing OrElse Me.modelessForm.IsDisposed Then
            Try
                docLock = Application.DocumentManager.MdiActiveDocument.LockDocument()
                Me.modelessForm = New frmMain()
                Me.AddOwnedForm(Me.modelessForm)
                Application.ShowModelessDialog(modelessForm)
                docLock.Dispose()
            Catch ex As Exception
            End Try
        Else
            Try
                docLock = Application.DocumentManager.MdiActiveDocument.LockDocument()
                Me.modelessForm.Activate()
                docLock.Dispose()
            Catch ex As Exception
            End Try
        End If
    End Sub

    Private Sub cmdClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClose.Click
        If Not Me.modelessForm Is Nothing AndAlso Not Me.modelessForm.IsDisposed Then
            Me.modelessForm.Close()
        End If
    End Sub
End Class

Now the modelessForm variable works great to test if the form is already shown, that question is answered.
Only issue now is the close button callback, it never runs the Me.modelessForm.Close() code because modelessForm is nothing (discovered from debugging).
I must have scoped or be testing the variable wrong.
I am wondering why I dont just do a Try...Me.modelessForm.Close() type statement, would that cause problems?

I also don't understand what the disposed property is.  Is that wen garbage collection has not destroyed the object yet?
thanks
James Maeding

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #10 on: January 10, 2007, 01:25:33 PM »
Maybe I should not be doing all this in the form.  It looks like everyone is saying to make a class, then have the class call the form.
The class has the private variable, not the form.  That did seem odd to me that a form would have a variable pointing to itself.
I am still confused how the modelessForm variable got set to nothing if the form is still open though...

I'll do some playing around, it has to be a scoping thing.
James Maeding

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #11 on: January 10, 2007, 02:42:29 PM »
I think the close issue is easier than I thought.  I changed the program structure to be two parts, a class and a form that gets called.
All I want is the close button to close the form, which must be showing to be called.  No need to check if it exists like with the code that shows the form.
So now the only code in the form is:

Private Sub cmdClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClose.Click
       Me.Close()
End Sub

Seems to work great, I wonder if dock locking is neeeded for the close method though.  This locking and error checking is theoretical so far, that will likely change soon...
James Maeding

Glenn R

  • Guest
Re: Modeless Dialog help
« Reply #12 on: January 11, 2007, 06:05:51 AM »
Whatever file your [CommandMethod("WhatEver")] is defined in, is a class......sooooo, toss a private member variable of your form type in there and away you go........and in your commandmethod itself, that's where you check for the existence of your private variable and do your appropriate mojo...to show or not show, that is the question.

Cheers,
Glenn.

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #13 on: January 11, 2007, 02:18:36 PM »
Exactly, that is working well.
I put this in the class:
Private modelessForm As frmMain

then tested with this to see if I should open new or not:
If Me.modelessForm Is Nothing OrElse Me.modelessForm.IsDisposed Then...

I know you don't need the code, but I thought to slap it down so others reading this might see it.
I'm a happy camper at this point, the modeless form is working great.  Thanks for everyone's help!
James Maeding

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Modeless Dialog help
« Reply #14 on: January 11, 2007, 06:56:33 PM »
Exactly, that is working well.
I put this in the class:
Private modelessForm As frmMain

then tested with this to see if I should open new or not:
If Me.modelessForm Is Nothing OrElse Me.modelessForm.IsDisposed Then...


I know you don't need the code, but I thought to slap it down so others reading this might see it.

I'm a happy camper at this point, the modeless form is working great.  Thanks for everyone's help!

Cool James, we eat up samples of code  :lol:
We hope you come out to play with us more often

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #15 on: January 12, 2007, 08:15:36 PM »
Hey Danielm,
I just finished translating the setfocus and postcommand functions to vb syntax and they work great!
My modeless is humming along now, this is starting to be fun.
James Maeding

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Modeless Dialog help
« Reply #16 on: January 12, 2007, 11:46:32 PM »
Oh Cool, glad it worked for you,
do we get to see a pic? :-)

jmaeding

  • Bull Frog
  • Posts: 304
  • I'm just here for the Shelties.
Re: Modeless Dialog help
« Reply #17 on: January 13, 2007, 03:29:17 PM »
but of course!
Remeber that the lisp/ObjectDCL version of this is available at cadthinking.com for free.
It works for 2000 to 2007.
Here is the dialog...

I still have to work on a few things:
1) the cyan button is supposed to be the color of the layer, not sure how to translate an acad color number to a .net color variable

2) You cannot step through the nesting levels yet

3) not sure how to tell if a layer is frozen in current viewport.  I think I would test if I was "in" a paperspace viewport, then get the viewport if so.  Maybe the vport has an extension dictionary with the layer settings for it.

4) you cannot "apply" any changes yet, its just info in the dialog.  I'm getting to it though.

Some people might ask why not just use the properties box?  The answer is the properties box does not let you change layer properties or step through nesting levels.  This tool SHOWS you entity and layer props and lets you CHANGE the layer props.  No need to go to the layer dialog and look up the layer name...
James Maeding