Solved

How to close a Modal Dialog form from parent form

Posted on 2010-09-16
18
3,506 Views
Last Modified: 2012-05-10
I have a form, that intercepts keyboard input (using Application.AddMessageFilter) to determine input from a barcode scanner (this is not important for the purpose of this question).

When a serial number is succesfully validated from this filter, a process is spawned (syncronously I assume) to search a DB for this serial number. If it is not found, a modal dialog is shown to determine what to do with it.

If I then scan another barcode, I want the modal form to be closed, and forgotten about, so that the process can start again (and show the modal form again if necessary). If the modal form is not needed, there is no problem (the modal form will show again the next time its needed without any issues), if however the modal form is needed again as part of the same process that closed the previous instance, then I receive this error:

InvalidOperationException was unhandled.

"Form that is already displayed modally cannot be displayed as a modal dialog box. Close the form before calling showDialog."

Some code:
        frmUnknownItem dlgUnknownItem = new frmUnknownItem();

        public frmMain() {
            InitializeComponent();
            Application.AddMessageFilter(new KeyboardMessageFilter(this));
        }

// This is called when a valid serial number is detected from the barcode scanner via the KeyboardMessageFilter
        private void ProcessInputtedText(string InputtedText) {
            dlgUnknownItem.CloseIfOpen();

            if ("FOUND IN DB") {
                //Do as required
            } else {
                //Otherise, open modal dialog form.
                dlgUnknownItem.SetOptions(InputtedText);
                dlgUnknownItem.ShowDialog();
                //More code depending on decision from the modal dialog
            }
        }


class frmUnknownItem : Form {
        bool _isOpen = false; //flag is set to true when form is shown.

        public void CloseIfOpen(){
            if (_isOpen) {
                _isOpen = false;
                this.Hide();
                this.Close();
            }
        }
}

Open in new window


Maybe this is just a huge logic flaw, as the code is siting and waiting for the modal form to close so it can continue execution through the method, but at the same time the keyboardmessagefilter spawns a new process that closes the form from a different instance of the method that opened it... so there could be two instances of code running at the same time? is that correct? I am not presented with any of the usual multi-threading issues that I would normally face if this is true.

Any thoughts, ideas, suggestions, or teachings please?

The form has to be modal, and it has to be programatically closable and forgotten about...
0
Comment
Question by:DjDezmond
  • 7
  • 6
  • 3
  • +2
18 Comments
 
LVL 8

Expert Comment

by:Gururaj Badam
ID: 33693293
I'm expecting that when you say closeable you meant to close/dispose not hide? Is this dialog custom dialog or standard messagebox?
0
 
LVL 9

Author Comment

by:DjDezmond
ID: 33693564
it is a normal windows form, and when I say closeable, i mean hideable really, as I want to reuse the one instance of it on my main form.

The error suggests it needs to be closed though? and calling just Hide() makes no difference at all.

I suspect this is more of a wierd threading issue, as the code works fine as long as the modal form is not re-opened in the same pass that closes it (if that makes sense :-/ )
0
 
LVL 13

Expert Comment

by:Naman Goel
ID: 33693595
can you send me complete code and exception call stack on my email address

goel.naman@gmail.com
0
 
LVL 8

Expert Comment

by:Gururaj Badam
ID: 33693671
Post your code here for reference rather sending out in email.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 33693701
I'm not sure this is necessarily the best approach, but the first thing that comes to mind...since using ShowDialog(), by definition, halts execution in the calling thread I think you're going to run into issues.  Instead, try setting the "Owner" property of your modal dialog to be your main form - and on open of the modal dialog disable the parent form and on close of the modal enable it.
In your message filter you can simple check to see if the modal has been disposed/is visible and close it if yes.
0
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 500 total points
ID: 33693747
Sorry, got distracted by a phone call...forgot to attach this code.
using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;



namespace WindowsFormsApplication1

{

	public partial class Form1 : Form, IMessageFilter

	{

		private const int WM_LBUTTONDBLCLK = 0x0203;

		MyModalDialog modal;



		public Form1()

		{

			InitializeComponent();

			Application.AddMessageFilter(this);

		}



		public bool PreFilterMessage(ref Message m)

		{



			if (m.Msg == WM_LBUTTONDBLCLK)

			{

				if (modal != null && !modal.IsDisposed)

					modal.Close();

				modal = new MyModalDialog(this);

				modal.Show();

				return true;

			}

			else

				return false;



		}

	}



	public class MyModalDialog : Form

	{

		public MyModalDialog()

		{

			this.Load += new EventHandler(MyModalDialog_Load);

			this.FormClosing += new FormClosingEventHandler(MyModalDialog_FormClosing);

		}



		void MyModalDialog_FormClosing(object sender, FormClosingEventArgs e)

		{

			if (this.Owner != null)

				this.Owner.Enabled = true;

		}



		void MyModalDialog_Load(object sender, EventArgs e)

		{

			if (this.Owner != null)

				this.Owner.Enabled = false;

		}

		public MyModalDialog(Form owner)

			:this()

		{

			this.Owner = owner;

		}

	}

}

Open in new window

0
 
LVL 9

Author Comment

by:DjDezmond
ID: 33693784
The project itself is part of a "suite" of tools I am developing, and too big and unneccessary to upload. I have taken the three forms relevant to this issue.

All of the database code is stored in a DLL file, and as the databases themselves are held internally within our company, you wouldn't be able to access them anyway, so you will just have to use your intiative and replace any invalid references with True or False statements to mimic the desired outcome.
StockIO.zip
0
 
LVL 9

Author Comment

by:DjDezmond
ID: 33693895
tgerbert:

Thanks for your suggestion, I tried to implement that, to no avail.

public MyModalDialog(Form owner)
:this()
{
this.Owner = owner;
}

:this() returned an error saying that it didn't except 0 paramaters?
If I removed that, the error still showed as before.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 33693951
My example modal dialog form has two constructors, one taking zero parameters and one taking a single Form parameter.  The this:() line associated with the 1-parameter constructor causes the parameter-less constructor to be called first, before the 1-parameter constructor.  This allows you to put code that is common to both constructors in one place (the parameter-less constructor), and code specific to the 1-parameter constructor in that constructor.
public class MyModalDialog : Form

{

    public MyModalDialog()

    {

        this.Load += new EventHandler(MyModalDialog_Load);

    }

    public MyModalDialog(Form owner)

      :this()

    {

        this.Owner = owner;

    }

    private void MyModalDialog_Load(object sender, EventArgs e)

    {

        // blah blah blah

    }

}

Open in new window

0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 9

Author Comment

by:DjDezmond
ID: 33693953
Actually I am lying, i forgot to reinitialize the form before opening again.

So that works now, kind of, but I can re-mould it back into how I want it displaying. So thanks for that.

I think more so, I was kinda wanting an explanation about the execution contex around this, as it wasn't making sense to me. Obviously ShowDialog() halts execution as you said, so how could the KeyboardMessageFilter carry on executing, without being multithreaded? I was able to close the modal dialog box ok from outside of the calling thread, without invoking or messing about jumping between threads.

Its kind of multithreading without being multithreaded...? is that right?
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 33693958
...therefore you must be missing the parameter-less constructor is the point I was trying to make. ;)
0
 
LVL 9

Author Comment

by:DjDezmond
ID: 33693966
tgerbert::

yes mate, i realised that afterwards, i was just being a tard and not reading your code fully!
0
 
LVL 8

Expert Comment

by:Gururaj Badam
ID: 33693975
I don't see any issues with your code. I'm attaching the working code for your reference.
StockIO.zip
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 33694013
Well, I'm a little foggy on the details of that myself - but I suspect that the reason the MessageFilter still works is either because the ModalDialog's WndProc is processing messages, or because the behind-the-scenes code of ShowDialog is actually a loop that continually alternates between processing Windows messages and running the code for the modal window.
I wonder if the message filter continues to work if you show a message box, or System.Threading.Thread.Sleep() in the modal window?
0
 
LVL 9

Author Comment

by:DjDezmond
ID: 33694026
OK thanks Novice Novice,

Although you have removed most of it, well the relevant bits anyway, so maybe I should have thought more about what I was (or wasn't) posting on here, as you were obviously unable to create a working example with the amount of missing references. so sorry about that.
0
 
LVL 9

Author Comment

by:DjDezmond
ID: 33694062
hmm, yea maybe worth a look tomorrow when I get back into the office. I am the kinda person who needs to understand everything about why something works the way it does before I can properly utilize it to its best potential elsewhere.

Cheers again anyway tgerbert.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 33694113
fyi...Quick test tells me that the message filter stops working as soon as the thread is Sleep()'ed or MessageBox.Show(...), so I'd venture to say so long as any form is open and processing Windows messages in the queue the message filter will continue to work.
0
 
LVL 10

Expert Comment

by:Mathiyazhagan
ID: 33694210
create frmUnknownItem   object  in local scope and dispose it after it serves it's purpose. as

 using( frmUnknownItem  dlgUnknownItem= new frmUnknownItem  ())
{
   //code here
 
}
"using " keyword dispose object when scope completed.set DialogResult value in frmUnknownItem  to close it  like

private     bthOK_click(object sender,EventArgs e)
{
     this.Count =localvalue; //somethimk like that
      DialogResult = DialogResult.OK; //closes the modal dialog
}
private     bthCancel_click(object sender,EventArgs e)
{
         DialogResult = DialogResult.Cancel; //closes the modal dialog
}


depending DialogResult  value of frmUnknownItem  :

                   if (dlgUnknownItem.ShowDialog() == DialogResult.OK)
                  {
                     //More code depending on decision from the modal dialog
                      int intValue = dlgUnknownItem.Count;//something like
                  }

hope this helps. if you want to run in single thread at time use lock () as:

            if ("FOUND IN DB") {
                //Do as required
            } else
              {
                     object sync = new Obejct();
                      lock(sync)
                       {
                           //show frmUnknownItem
                       }    
                }
 
private void ProcessInputtedText(string InputtedText)
 {

            if ("FOUND IN DB") {
               
            } 
           else {
                //Otherise, open modal dialog form.
                using( frmUnknownItem  dlgUnknownItem= new frmUnknownItem  ())
                {
                   dlgUnknownItem.SetOptions(InputtedText);
                   if (dlgUnknownItem.ShowDialog() == DialogResult.OK)
                  {
                     //More code depending on decision from the modal dialog
                      int intValue = dlgUnknownItem.Count;//something like
                  }
                }
            }
        }

Open in new window

0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Many of us here at EE write code. Many of us write exceptional code; just as many of us write exception-prone code. As we all should know, exceptions are a mechanism for handling errors which are typically out of our control. From database errors, t…
Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
This video discusses moving either the default database or any database to a new volume.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now