Board index » delphi » Disabling main form while processing - any elegant way of doing it?

Disabling main form while processing - any elegant way of doing it?

In some of my apps, while some processing is being done, I like to show a
foem with, say, an AVI and a message like 'Processing. Please wait ...'.
This form obviously cannot be modal, so what is the best way of preventing
the user from playing with controls on the main form while the other one is
showing?
I find that if you disable the main form, sometimes windows will switch to
another apps main form while the 'message form' is showing - and then it
doesn't switch back.

Any ideas on an elegant solution? Is it to disable all controls on the form
through a loop through the components list?

TIA
- G Vincent

 

Re:Disabling main form while processing - any elegant way of doing it?


In article <01be00d7$85253790$5cb822c4@gvincent>, G Vincent
<l...@header.for.email> writes
Quote
>In some of my apps, while some processing is being done, I like to show a
>foem with, say, an AVI and a message like 'Processing. Please wait ...'.
>This form obviously cannot be modal, so what is the best way of preventing
>the user from playing with controls on the main form while the other one is
>showing?

You could try installing mouse and keyboard hook filters in your form's
OnCreate event, and removing them in OnDestroy. Then, inside your hook
functions disallow all mouse and keyboard events (or allow only certain
ones - WM_MOUSEMOVE and WM_NCMOUSEMOVE for instance).

Take a look at the Windows API on-line help for
SetWindowsHookEx(WH_MOUSE) and SetWindowsHookEx(WH_KEYBOARD).

Hope the above helps.
--
Steve Turner
Leeds, England

Re:Disabling main form while processing - any elegant way of doing it?


Instead of disabling the main form, you might try hiding it.

procedure Form1.ProcessThis;
begin
    Hide;
    ProgressDlg.Show;

    { loop here, don't forget Application.ProcessMessages! }

    ProgressDlg.Close;
    Show;
end;

Quote
G Vincent wrote in message <01be00d7$85253790$5cb822c4@gvincent>...
>In some of my apps, while some processing is being done, I like to
show a
>foem with, say, an AVI and a message like 'Processing. Please wait
...'.
>This form obviously cannot be modal, so what is the best way of
preventing
>the user from playing with controls on the main form while the other
one is
>showing?
>I find that if you disable the main form, sometimes windows will
switch to
>another apps main form while the 'message form' is showing - and then
it
>doesn't switch back.

>Any ideas on an elegant solution? Is it to disable all controls on
the form
>through a loop through the components list?

>TIA
>- G Vincent

Re:Disabling main form while processing - any elegant way of doing it?


Quote
> In article <01be00d7$85253790$5cb822c4@gvincent>, G Vincent
> <l...@header.for.email> writes
> >In some of my apps, while some processing is being done, I like to show a
> >foem with, say, an AVI and a message like 'Processing. Please wait ...'.
> >This form obviously cannot be modal, so what is the best way of preventing
> >the user from playing with controls on the main form while the other one is
> >showing?

I created an object I called a TFormDisabler.  I pass this a pointer to a
form which I wish to disable.  The object then iterates through each
component on the form and determines which components are disabled or enabled
already and adds the component number and the enable state to a TList.  Each
component is then disabled.  When I have done what I need to do, I call the
TFormDisabler.EnableForm method which sets each component back to it's
original state.  Works really well and is simple to use and took about 40 or
50 lines of code, I think.

Trevor Hand

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    

Re:Disabling main form while processing - any elegant way of doing it?


In article <01be00d7$85253790$5cb822c4@gvincent>, "G Vincent"

Quote
<l...@header.for.email> writes:
>Any ideas on an elegant solution? Is it to disable all controls on the form
>through a loop through the components list?

1 Open the "Please wait" form modally and then call the processing procedure
from the OnFormShow handler of the form. When the processing is finished then
set ModalResult to non-zero.

2 Use threads for the processing and showing the "Please wait" form.

3 If you want a SystemModal form there was some discussion on this some time
ago (6 months)

Alan Lloyd
alangll...@aol.com

Re:Disabling main form while processing - any elegant way of doing it?


AlanGLLoyd <alangll...@aol.com> wrote in article
<19981026164319.26675.00000...@ngol05.aol.com>...

Quote
> 1 Open the "Please wait" form modally and then call the processing
procedure
> from the OnFormShow handler of the form. When the processing is finished
then
> set ModalResult to non-zero.

That's the ideal solution, except I want a generic message form that can be
used for any process.

Quote
> 2 Use threads for the processing and showing the "Please wait" form.

But that would still allow the user to 'play' on the main form. And they do
so love to do that!
Hmm, but you have given me an idea there .....

- Graeme Vincent

Re:Disabling main form while processing - any elegant way of doing it?


Steve Turner <st...@dmg-hq.demon.co.uk> wrote in article
<KtkKfAA6IHN2E...@dmg-hq.demon.co.uk>...

Quote
> You could try installing mouse and keyboard hook filters in your form's
> OnCreate event, and removing them in OnDestroy. Then, inside your hook
> functions disallow all mouse and keyboard events (or allow only certain
> ones - WM_MOUSEMOVE and WM_NCMOUSEMOVE for instance).

I will try that. Problem is my form will often have a 'Cancel' button.

- Graeme Vincent

Re:Disabling main form while processing - any elegant way of doing it?


t.h...@trl.telstra.com.au wrote in article
<712re4$2h...@nnrp1.dejanews.com>...

Quote

> I created an object I called a TFormDisabler.  I pass this a pointer to a
> form which I wish to disable.  The object then iterates through each
> component on the form and determines which components are disabled or
enabled
> already and adds the component number and the enable state to a TList.
Each
> component is then disabled.  When I have done what I need to do, I call
the
> TFormDisabler.EnableForm method which sets each component back to it's
> original state.  Works really well and is simple to use and took about 40
or
> 50 lines of code, I think.

I have done something similiar. I just hoped the solution would be simpler!

- Graeme Vincent

Re:Disabling main form while processing - any elegant way of doing it?


Quote
G Vincent wrote in message <01be0173$82f677d0$5cb822c4@grvin>...
>t.h...@trl.telstra.com.au wrote in article
><712re4$2h...@nnrp1.dejanews.com>...

>> I created an object I called a TFormDisabler.  I pass this a pointer to a
>> form which I wish to disable.  The object then iterates through each
>> component on the form and determines which components are disabled or
>enabled
>> already and adds the component number and the enable state to a TList.
>Each
>> component is then disabled.  When I have done what I need to do, I call
>the
>> TFormDisabler.EnableForm method which sets each component back to it's
>> original state.  Works really well and is simple to use and took about 40
>or
>> 50 lines of code, I think.

>I have done something similiar. I just hoped the solution would be simpler!

Yes it is. *Much* simpler.

Try:
  EnableWindow(Handle, False);

This will disable the entire form, but processing will continue. This is
useful
if you want to show a small "wait" dialog that *appears* to be modal, by
making a small form with fsStayOnTop FormStyle.

--
Jeremy Collins
Kansai Business Systems

Re:Disabling main form while processing - any elegant way of doing it?


In article <01be0173$bfd44920$5cb822c4@grvin>, G Vincent
<l...@header.for.email> writes

Quote
>I will try that. Problem is my form will often have a 'Cancel' button.

It's funny that you mentioned that!

The LParam passed to the mouse hook function by Windows is actually a
pointer to a TMouseHookStruct record. You can use a type cast to
determine whether the mouse was clicked over a particular button on your
form. An example follows (the form is called ProgressForm, and it
contains a button called CancelButton):

function MouseHook(Code: Integer; wParam: Word;
    lParam: Longint): Longint; far;
type
    PTMouseHookStruct = ^TMouseHookStruct;
var
    PMouseInfo: PTMouseHookStruct;
begin
    with ProgressForm do
    begin
        { Keep Windows sweet by calling the next hook in the chain
          if the Code parameter is less than zero }
        if (Code < 0) then
            Result := CallNextHookEx(MouseHookHandle, Code,
                wParam, lParam)
        else
        begin
            { The lParam parameter points to a TMouseHookStruct record.
              Use a type cast to allow access to the record members }
            PMouseInfo := PTMouseHookStruct(lParam);

            with PMouseInfo^ do
            begin
                { Determine whether we are over the progress form's
                  cancel button. Allow the message to be processed
                  if so }
                if (hWnd = CancelButton.Handle) then
                    Result := 0     { Allow the mouse message }
                else
                    Result := 1;    { Disallow the mouse message }

            end;

{ etc, etc... }

        end; { end if }

    end; { end with }

end;

You can do similar operations in your keyboard hook function to look for
certain key combinations, for example:

function KeyboardHook(Code: Integer; wParam: Word;
    lParam: Longint): Longint; far;
type
    { The lParam parameter passed to this function is a bitfield. Since
      bitfields are not allowed in Pascal these type definitions allow
      the splitting of the parameter into a Pascal-accessible record.
      Refer to the API help for the routine SetWindowsHookEx for further
      details }
    TPrevKeyState = (pkUp, pkDown);
    TTransKeyState = (ksPressed, ksReleased);
    TKeyboardHookStruct = record
        RepeatCount: Word;
        ScanCode: Word;
        ExtendedKey: Boolean;
        NotUsed1: Word;
        UsedInternally: Word;
        AltKeyDown: Boolean;
        PrevKeyState: TPrevKeyState;
        TransKeyState: TTransKeyState;
    end;

var
    KeyboardHookStruct: TKeyboardHookStruct;

    { Local routine which returns a bitfield value between the given
      start and end bits (the bit indices are zero based, i.e. bit 0 is
      the first least significant bit).
      For example, GetBitField($76543210, 8, 11) will return $2 - the
      integer value of the four bits 8, 9, 10, 11 }
    function GetBitfield(Val: Longint; StartBit, EndBit: Byte): Longint;
    var
        BitCount: Longint;
    begin
        Result := 0;

        for BitCount := StartBit to EndBit do
            Result := Result + (Val and (1 shl BitCount));

        Result := Result shr StartBit;
    end;

begin
    with ProgressForm do
    begin
        { Keep Windows sweet by calling the next hook in the chain if
          the Code parameter is less than zero }
        if (Code < 0) then
            Result := CallNextHookEx(KeyboardHookHandle, Code, wParam,
               lParam)
        else
        begin
            { The lParam parameter passed to this function is a
              bitfield. Since bitfields are not allowed in Pascal
              extract the various bitfields into a record to allow
              access to the bitfield members. Refer to the API help for
              the routine SetWindowsHookEx for further details }
            with KeyboardHookStruct do
            begin
                RepeatCount := GetBitfield(lParam, 0, 15);
                ScanCode := GetBitfield(lParam, 16, 23);
                ExtendedKey := Boolean(GetBitfield(lParam, 24, 24));
                AltKeyDown := Boolean(GetBitfield(lParam, 29, 29));
                PrevKeyState := TPrevKeyState(GetBitfield(lParam, 30,
                    30));
                TransKeyState := TTransKeyState(GetBitfield(lParam, 31,
                    31));

                { Allow the enter key to be processed so that the
                  default "Cancel" button can be accessed }
                if ((wParam = VK_RETURN) and (AltKeyDown = False)) then
                    Result := 0     { Allow the enter key to be
                                      processed }
                else
                    Result := 1;    { Disallow all other keys }

            end; { end with }

{ etc, etc... }

        end; { end if }

    end; { end with }

end;

It might not be the most elegant method, but it works for me in my
applications.
--
Steve Turner
Leeds, England

Re:Disabling main form while processing - any elegant way of doing it?


In article <01be0173$46c3daa0$5cb822c4@grvin>, "G Vincent"

Quote
<l...@header.for.email> writes:
>AlanGLLoyd <alangll...@aol.com> wrote in article
><19981026164319.26675.00000...@ngol05.aol.com>...
>> 1 Open the "Please wait" form modally and then call the processing
>procedure
>> from the OnFormShow handler of the form. When the processing is finished
>then
>> set ModalResult to non-zero.

>That's the ideal solution, except I want a generic message form that can be
>used for any process.

Give your modal form a property named OnCalculate (or whatever) of an
appropriate defined type (you define it in the modal form like an event handler
type but not "of project") which is called from within the modal form OnShow.

MyCalculateProc has whatever code you wish for that partioular call to
MyWaitForm.

Then call the modal form with :-

MyWaitForm.OnCalculate := MyCalculateProc;
MyWaitForm.ShowModal;

Quote
>> 2 Use threads for the processing and showing the "Please wait" form.

>But that would still allow the user to 'play' on the main form. And they do
>so love to do that!
>Hmm, but you have given me an idea there .....

I'm not really knowledgeable on threads but can't they "wait for single object"
or use a mutex or something.

Alan Lloyd
alangll...@aol.com

Re:Disabling main form while processing - any elegant way of doing it?


Quote
G Vincent wrote:

> Any ideas on an elegant solution? Is it to disable all controls on the form
> through a loop through the components list?

> TIA
> - G Vincent

My thoughts.

1. Don't use any loops that call application.processmessages. They are
clunky, not good CPU usage, and can present re-entrancy problems.

perhaps something (off the top of my head) like:

procedure MyForm.Disable;

var
        iter:integer;

for iter:=0 to MyForm.ControlCount do
begin
        if (MyForm.Controls[iter] is TButton) or
           (MyForm.Controls[iter] is ....
           ....
                                              then
        begin
                with MyForm.Controls[iter] do
                begin
                        Tag:=Integer(Enabled);
                        Enabled:=false;
                end;
        end;
end;

and on the enable, do the reverse, check the tag values to get the
previously enabled/disabled states (remembering to range check), and
then reset the original states.

MH.

--
Martin Harvey

mailto:mar...@aziraphale.demon.co.uk
http://www.harvey27.demon.co.uk/mch24/

Undoubtedly, someone can learn Object Oriented Programming from
reading code - after all Alan Kay learned OO by deciphering
an 80 page Simula program thinking it was Algol.
However, most people are not in the same league as Alan.
- Bjarne Stroustrup

Other Threads