Why are IMessage events still accessing a form that has been Disposed

  • Thread starter Thread starter Sean Connery
  • Start date Start date
S

Sean Connery

I have a Microsoft UI Process Application Block that is controlling child
forms in an MDI parent container. The views node in the app.config file has
been set to stayOpen=false.

Because there are a series of data entry forms using ONLY the numeric key
pad, certain keys on this pad have been configured to behave as "function
keys". In order for this to happen, I have implemented the IMessageFilter
interface to intercept messages in the message pump. These are trapped as
virutal methods in the base form to be optionally used by derived child
forms.

Everything works fine... up to a point. If "Data Entry" F2 is selected, the
overridden virutal method in the Data Entry form fires and causes a
navigation to the "Bill Selection" form. UIP closes Data Entry and
intantiantes Bill Selection... Furthermore, the Data Entry base class
Disposes its components.

I expect to do the reverse in Bill Search, switching to Data Entry via the
F1 key this time. Thus, F1/F2 toggle these two views back and forth. Closing
one and opening the other.

This works fine.

The problem is pressing F2 (or F1) twice in a row. Because Data Entry has
been disposed, I DO NOT expect it to trap the F2 event AFTER it has been
disposed. However, this is not the case and I am at a loss as to how this
can be happening (there should be no instance of an F2 listener insofar as
the form listening for it has been disposed.)

Does anyone have any ideas on how this could be possible?
 
Sean Connery said:
The problem is pressing F2 (or F1) twice in a row. Because Data Entry has
been disposed, I DO NOT expect it to trap the F2 event AFTER it has been
disposed. However, this is not the case and I am at a loss as to how this
can be happening (there should be no instance of an F2 listener insofar as
the form listening for it has been disposed.)

You should not create more than one message loop (if that is what I understand you are doing). Don't have each form implement
IMessageFilter, do this in one location. The message loop for the app is still running so of course each IMessageFilter will get
messages. You are probably better overriding WndProc or just using one of the Key events or overrides.
 
Actually, what I have is an MDI child form base class with the following
smippet:

public class ChildViewBase :
Microsoft.ApplicationBlocks.UIProcess.WinFormView, IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
const int WM_KEYDOWN = 0x100;

if (m.Msg == WM_KEYDOWN)
return HandleKeys((Keys)(int)m.WParam & Keys.KeyCode);
else
return false;
}

private bool HandleKeys(Keys keyCode)
{
bool ret = false;

switch (keyCode)
{
case Keys.Enter: ret = EnterKeyPressed(); break;
case Keys.Divide:
case Keys.F1: ret = F1KeyPressed(); break;
case Keys.Multiply:
case Keys.F2: ret = F2KeyPressed(); break;
case Keys.F3: ret = F3KeyPressed(); break;

(etc.)

default: ret = false; break;
}

return ret;
}

protected virtual bool EnterKeyPressed() {return false;}
protected virtual bool F1KeyPressed() {return false;}
protected virtual bool F2KeyPressed() {return false;}
protected virtual bool F3KeyPressed() {return false;}
}


This is enherited by child forms as follows:


public class DataEntryView : ChildViewBase
{
protected override bool F2KeyPressed()
{
TADController.BillSelection();
return true;
}
}

public class BillSelectionView : ChildViewBase
{
protected override bool F1KeyPressed()
{
TADController.DataEntry();
return true;
}
}

Each of these views are disposed, so when DataEntryView is activated, in
theory there should be no overridden F1 in BillSelectionView and when
BillSelectionView is activated there should be no F2 listener in
DataEntryView.

This does not appear to be the case. Hitting F2 when BillSelectionView is
active fires F2KeyPressed in DataEntryView... which shouldn't exist.
 
Sean Connery said:
Actually, what I have is an MDI child form base class with the following
smippet:

Yes, but each instance of your form is creating another IMessageFilter. You should use message filters sparingly due to performance
issues, implementing them in every form is a big no-no.
Each of these views are disposed, so when DataEntryView is activated, in
theory there should be no overridden F1 in BillSelectionView and when
BillSelectionView is activated there should be no F2 listener in
DataEntryView.

No, disposed does not mean a thing unless you do something with it. I can create a class that implements IDisposable and just do
nothing in Dispose function and continue on working with it. I could also call IDispose as many times as I like in this case with no
effect.

In this case you should be removing your form from the message filter and with the quick look I had I didn't see you doing this
anywhere. You should do this in the forms dispose method, although, you shouldn't be doing this at all.

Why can't you just use the normal key events?
 
Michael Culley said:
Yes, but each instance of your form is creating another IMessageFilter.
You should use message filters sparingly due to performance
issues, implementing them in every form is a big no-no.

The problem is that this is not a typical MDI application due to behavior
imposed by Microsoft's UI Process Application Block, which gives limited
support for an MDI container other than as a basic container. Navigation,
views, and the view controller are all within the child forms, which are
uncoupled, and configured in tha app.config file. Because of the "heads
down" nature of some of the high-speed Data Entry forms (which will also be
implemented as low-speed Web Forms for occasional branch office use -- hence
the value of an MVC architecture), it was imparative that all activity be
accomplishable from the ten-key, which remaps some of the "shifted" keys to
behave as mainframe-like "PF" keys so the technician does not even have to
look at the screen or remove her hand from the ten-key. Forms and
naviigation and aid-keys are all FORM-specific and form-orientated. The
specific compliment of forms and navigation requirments is determined via
technician logon and assigned roles in a workflow process.

The aid-keys are thus form-related as opposed to control-specific, which is
why it was necessary to insert a premessagefilter within the message stream
prior to their being dispensed to their final destination. Your suggestion
below does not allow me to do this (or at least I don't know how to do it.
:) ).

Anyway, I found the solution to my problem by listening for the forms
Closing event and adding "Application.RemoveMessageFilter(this);" to the
Closing method as you suggested below. The behavior works as expected now.

It does appear that without doing this the Application still believes the
form exists and fires KeyDown messages to my keyhandler within a closed
form. I am guessing the code still exists in memory and hasn't been garbage
collected, which is why the overridden virtual method in my derived child
form is able to run in spite of the fact the form has been closed.
No, disposed does not mean a thing unless you do something with it. I can
create a class that implements IDisposable and just do
nothing in Dispose function and continue on working with it. I could also
call IDispose as many times as I like in this case with no
effect.

In this case you should be removing your form from the message filter and
with the quick look I had I didn't see you doing this
anywhere. You should do this in the forms dispose method, although, you
shouldn't be doing this at all.
 
Back
Top