CGDevTools Forum

Welcome to the Official CGDevTools Support Community Forums.

Using Lock Indicator from child frames

by esthermann » 26 Aug 2014 16:33

I have a base form which I draw all my child frames for the application. The child frames then may contain pop-up dialogs. How can I use a lock indicator on these child frames and their descending pop-ups? I have attached an example. The Lock Indicator is only on the main form but I need it to work and lock all activity on the page. How can I accomplish this?
You do not have the required permissions to view the files attached to this post.
esthermann
 
Posts: 171
Joined: 22 Jun 2012 11:17

by Alexander Bulei » 26 Aug 2014 17:41

Hi esthermann,

You can't control it - I mean, lock the main tab/window when popup shows.

You should transform/load your content to dialog with modal = True.

Best Regards.
Group: Developers | Support Team

  • info [at] cgdevtools.com - General information
  • sales [at] cgdevtools.com - Sales department
  • support [at] cgdevtools.com - Product and Technical Support
User avatar
Alexander Bulei
Site Admin
 
Posts: 3635
Joined: 15 May 2012 08:52
Location: Mealhada, Portugal

by esthermann » 26 Aug 2014 19:43

But I need to be able to lock the dialog while performing background services such as emailing. What is the best way to accomplish this?
esthermann
 
Posts: 171
Joined: 22 Jun 2012 11:17

by Alexander Bulei » 27 Aug 2014 09:56

Hi esthermann,

But I need to be able to lock the dialog while performing background services such as emailing.


You can use "unclosable" modal dialog, and close it when your service is done.

Best Regards.
Group: Developers | Support Team

  • info [at] cgdevtools.com - General information
  • sales [at] cgdevtools.com - Sales department
  • support [at] cgdevtools.com - Product and Technical Support
User avatar
Alexander Bulei
Site Admin
 
Posts: 3635
Joined: 15 May 2012 08:52
Location: Mealhada, Portugal

by ScottWGast » 27 Aug 2014 14:53

Hey Esther,

I had a similar issue when running a monthly billing batch, but I wanted the user to be able to click a cancel button in order to stop the process if necessary. I create a thread to do the processing...I can post the code, if you like.

Best regards,
Scott

UPDATE: Also, I add a .LockIndicator property to every frame and set that property when the frame is created, so the lock indicator gets passed down the line.
ScottWGast
 
Posts: 875
Joined: 23 May 2012 11:02

by esthermann » 27 Aug 2014 15:20

Scott that would be awesome if you would. Thanks!
esthermann
 
Posts: 171
Joined: 22 Jun 2012 11:17

by ScottWGast » 27 Aug 2014 15:43

Esther,

Here you go!

I know this is a lot of code, but I've snipped out most of the database updating and other code that is not specifically related to the overall process.

If you'd like to talk on the phone, I'm in East Texas... just send me an email to Scott AT S2Software DOT com

Scott

This first code block is the OnClick for starting the monthly billing process.

Code: Select all
procedure TframePhoneSystemClientList.buttonRunMonthlyBillingJQButtonOptionsClick(Sender: TObject; AParams: TStringList);
var
  iBillingCycleID: Integer;
begin

  IWCGMessageDlg('Run Monthly Phone System Billing now?',mtConfirmation,[mbYes,mbCancel],
    procedure(Dialog: TIWCGJQMsgDialog; AResult: TModalResult)
      begin
        if AResult = mrYes then
          begin

            iBillingCycleID := S2Snax.StringToInteger(Self.comboBillingCycle.SelectedItem.Value);

            UserSession.StartMonthlyPhoneSystemBillingProcessThread('Monthly Phone System Billing Process', iBillingCycleID);
            Self.timerMonthlyPhoneSystemBilling.Enabled := True;

            // this code displays a modal dialog with a cancel button, effectively locking the screen while the thread is processing
            Self.FmsgDlgCancel := IWCGMessageDlg('Creating Monthly Phone System Invoices...',mtInformation,[mbCancel],
              procedure(Dialog: TIWCGJQMsgDialog; AResult: TModalResult)
                begin
                  if (AResult = mrCancel) then
                    begin
                      // loop until the thread is not "SuperBusy"
                      while UserSession.MonthlyPhoneSystemBillingProcessThread.SuperBusy do Sleep(100);

                      // if the user clicks the Cancel button AND the thread has not .Finished, then terminate the thread.  Keep in mind, the looping mechanisim
                      // inside the thread is iterating much more quickly than the TIWTimer.OnAsyncTimer and as a result, the thread might have finished between the
                      // time the user clicks the Cancel button and the next line of code.  If this happens, then the thread "wins" and the process is completed, even
                      // though the user clicked the Cancel button.
                      if (not UserSession.MonthlyPhoneSystemBillingProcessThread.Finished) then
                        UserSession.MonthlyPhoneSystemBillingProcessThread.Terminate;

                      // we MUST make sure that the TIWCGJQMessageDlg is nil when the user closes the dialog, otherwise Assigned(Self.FmsgDlgCancel) will return TRUE
                      // when, in fact, the dialog was destroyed when it was closed and is no longer functional, BUT is is NOT nil.  I do this because we must close
                      // the dialog in the TIWTimer.OnAsyncTimer event if the user clicks the "Cancel" button in the middle of the processing.
                      Self.FmsgDlgCancel := nil;

                      // Force a full post to ensure that the data on the screen is completely refreshed
                      Self.RefreshData;
                      RenderRegionAsync(Self.regionMain, False, True, True);

                    end;
              end);
          end;
      end);
end;


This is the Async OnTimer event:

Code: Select all
procedure TframePhoneSystemClientList.timerMonthlyPhoneSystemBillingAsyncTimer(Sender: TObject; EventParams: TStringList);
begin
  // if the thread is not nil
  if Assigned(UserSession.MonthlyPhoneSystemBillingProcessThread) then
    begin
      // set the progress bar caption
      Self.labelProgressBar.Caption :='<table border="0" align="center">'+
                                        '<tr>'+
                                          '<td>' + UserSession.MonthlyPhoneSystemBillingProcessThread.ProgressBarCaption + '</td>'+
                                        '</tr>'+
                                        '<tr>'+
                                          '<td><b>' + S2Snax.IntegerToString(UserSession.MonthlyPhoneSystemBillingProcessThread.PercentComplete) + '% ' +
                                                    '(' + IntToStr(UserSession.MonthlyPhoneSystemBillingProcessThread.RecordsProcessed) + ' of ' +
                                                            IntToStr(UserSession.MonthlyPhoneSystemBillingProcessThread.RecordCount) + ')' + '</b></td>' +
                                        '</tr>'+
                                      '</table>';
      // update the progress bar and render
      Self.progressBar.JQProgressBarOptions.Value := UserSession.MonthlyPhoneSystemBillingProcessThread.PercentComplete;
      RenderRegionAsync(Self.regionProgressBar, False, True, True);

      // if the thread has been cancelled, then display appropriate message and turn the timer off
      if UserSession.MonthlyPhoneSystemBillingProcessThread.Cancelled then
        begin
          if not S2Snax.IsEmptyString(UserSession.MonthlyPhoneSystemBillingProcessThread.ThreadResultMessage) then
            IWCGJQShowMessage.ShowNotification(UserSession.MonthlyPhoneSystemBillingProcessThread.ThreadResultMessage, jqsntError, 0);

          if UserSession.MonthlyPhoneSystemBillingProcessThread.RecordsProcessed > 0 then
            IWCGJQShowMessage.ShowNotification(IntToStr(UserSession.MonthlyPhoneSystemBillingProcessThread.RecordsProcessed) + ' of ' +
                                                IntToStr(UserSession.MonthlyPhoneSystemBillingProcessThread.RecordCount) + ' invoices created and posted successfully.', jqsntLog, 0);

          IWCGJQShowMessage.ShowNotification('Monthly Phone System Billing Process Cancelled!', jqsntError, 0);
          Self.timerMonthlyPhoneSystemBilling.Enabled := False;

          if Assigned(Self.FmsgDlgCancel)  then
            begin
              Self.FmsgDlgCancel.JQDialogOptions.Close;
              Self.FmsgDlgCancel := nil;
            end;

        end
      // if the thread has finished successfully, then display appropriate message and turn the timer off
      else if (UserSession.MonthlyPhoneSystemBillingProcessThread.Finished) then
        begin

          if UserSession.MonthlyPhoneSystemBillingProcessThread.RecordsProcessed > 0 then
            IWCGJQShowMessage.ShowNotification(IntToStr(UserSession.MonthlyPhoneSystemBillingProcessThread.RecordsProcessed) + ' of ' +
                                                IntToStr(UserSession.MonthlyPhoneSystemBillingProcessThread.RecordCount) + ' invoices created and posted successfully.', jqsntLog, 0);
          IWCGJQShowMessage.ShowNotification('Monthly Phone System Billing Process Complete!', jqsntLog, 0);
          Self.timerMonthlyPhoneSystemBilling.Enabled := False;
          // if the Cancel dialog has not been destroyed, then close it and make it nil.  the MessageDlg is destroyed on .Close, but it is NOT set to nil
          if Assigned(Self.FmsgDlgCancel)  then
            begin
              Self.FmsgDlgCancel.JQDialogOptions.Close;
              Self.FmsgDlgCancel := nil;
            end;
        end;
    end
  else
    // if the thread nil, then turn off the timer
    begin
      Self.timerMonthlyPhoneSystemBilling.Enabled := False;
    end;
  // if the timer is still .Enabled, then the progress bar stays visible
  Self.regionProgressBar.Visible := Self.timerMonthlyPhoneSystemBilling.Enabled;

end;


Here is the base worker thread code:

Code: Select all
type
  TWorkerThread = class(TThread)
  private
    function GetPercentComplete: Integer;
  protected
    FThreadName: string;
    FCancelled: Boolean;
    FRecordCount: Integer;
    FRecordsProcessed: Integer;
    FPercentComplete: Integer;
    FProgressBarCaption: String;
    FSuperBusy: Boolean;
    function GetTerminated: Boolean;
  public
    property Cancelled: Boolean read FCancelled;
    property RecordCount: Integer read FRecordCount;
    property RecordsProcessed: Integer read FRecordsProcessed;
    property PercentComplete: Integer read GetPercentComplete;
    property ProgressBarCaption: String read FProgressBarCaption;
    property SuperBusy: Boolean read FSuperBusy;
    constructor Create(ThreadName: String); overload; virtual;
  end;

implementation

{ TWorkerThread }

constructor TWorkerThread.Create(ThreadName: String);
begin
  inherited Create(False);
  FThreadName := ThreadName;
  FRecordCount := 0;
  FRecordsProcessed := 0;
end;

function TWorkerThread.GetPercentComplete: Integer;
begin
  GetPercentComplete := 0;
  if Self.FRecordCount > 0 then
    GetPercentComplete := Trunc((Self.FRecordsProcessed / Self.FRecordCount)*100);
end;

function TWorkerThread.GetTerminated: Boolean;
begin
  GetTerminated := Self.Terminated;
end;



The Monthly Processing thread code:

Code: Select all
type
  TMonthlyPhoneSystemBillingProcessThread = class(TWorkerThread)
  private
    FUserSession: TObject;
    FADOConnection: TADOConnection;
    FSQLConnectionString: String;
    FBillingCycleID: Integer;
    FThreadResult: TFunctionResult;
    FThreadResultMessage: String;
    procedure CalculateLDCharges(
                          ClientName: String;
                          EntityID: Integer;
                                       ClientLDOptionsID: Integer;
                                       OrderID: Integer;
                              PostingDate: TDateTime;
                              DetailLineTypeID: Integer;
                                       DetailLineType: String;
                                       IsTaxable: Boolean;
                                       IsGST: Boolean;
                                       GLAccountDRID: Integer;
                                     GLAccountCRID: Integer);

  protected
    procedure Execute; override;
  public
    property ThreadResult: TFunctionResult read FThreadResult;
    property ThreadResultMessage: String read FThreadResultMessage;
    constructor Create(UserSession: TObject; ThreadName: String; SQLConnectionString: String; BillingCycleID: Integer); overload; virtual;
  end;



And, finally, the monthly billing thread code.

Code: Select all
procedure TIWUserSession.StartMonthlyPhoneSystemBillingProcessThread(ThreadName: String; BillingCycleID: Integer);
begin
  Self.FMonthlyPhoneSystemBillingProcessThread := TMonthlyPhoneSystemBillingProcessThread.Create(Self, Name, Self.SQLConnectionString, BillingCycleID);
end;

{ TMonthlyPhoneSystemBillingProcessThread }
constructor TMonthlyPhoneSystemBillingProcessThread.Create(UserSession: TObject; ThreadName, SQLConnectionString: String; BillingCycleID: Integer);
begin
  inherited Create(ThreadName);

  Self.FUserSession := (UserSession as TIWUserSession);

  Self.FSQLConnectionString := SQLConnectionString;
  Self.FBillingCycleID := BillingCycleID;
  Self.FSuperBusy := False;

end;

procedure TMonthlyPhoneSystemBillingProcessThread.Execute;
var
  adoPhoneSystemItems, adoClients, adoOrder, adoOrderDetail, adoTenant: TADODataSet;
  strSQL, strOrderNumber, strClientName, strPhoneSystemName, strPeriod: String;
  iOrderID, iEntityID, iCompanyLocationID, iClientLDOptionsID: Integer;
  dGLJournalBatchID: Double;
  datePosted: TDateTime;
  resultPosting: TFunctionResult;
  resultPhoneSystem: TFunctionResult;
begin
    Self.FADOConnection := TADOConnection.Create(nil);
    Self.FADOConnection.ConnectionString := Self.FSQLConnectionString;
    Self.FADOConnection.Open;

    adoPhoneSystemItems := TADODataSet.Create(nil);
    adoPhoneSystemItems.Connection := Self.FADOConnection;
    adoPhoneSystemItems.CursorType := TCursorType.ctOpenForwardOnly;

<< SNIP >>

    // Thread properties
    Self.FRecordCount := adoClients.RecordCount;
    Self.FRecordsProcessed := 0;

    // this should be set to the "selected" company_location_id if there are multiple locations.
    iCompanyLocationID := 1;

    while (not adoClients.Eof) and (not Self.FCancelled) do
      begin

        dGLJournalBatchID := (Self.FUserSession as TIWUserSession).GL_CreateGLJournalBatchID;

        iEntityID := adoClients.FieldByName('entity_id').AsInteger;

        // connect to the phone system.
        strClientName := adoClients.FieldByName('mail_name').AsString;
        strPhoneSystemName := adoClients.FieldByName('phone_system_name').AsString;
        datePosted := adoClients.FieldByName('next_billing_date').AsDateTime;
        strPeriod :=  FormatDateTime('YYYYMM', datePosted);

        Self.FProgressBarCaption := 'Connecting to ' + strPhoneSystemName + ' Phone System';

        Self.FCancelled := Self.Terminated;

        if not Self.FCancelled then
          begin
            Self.FSuperBusy := True;
            try
              resultPhoneSystem := (Self.FUserSession AS TIWUserSession).ConnectToPhoneSystem(Self.FADOConnection, iEntityID);
              if resultPhoneSystem <> TFunctionResult.resultSuccess then
                begin
                  Self.FThreadResult := resultPhoneSystem;
                  Self.FThreadResultMessage := 'ERROR: ' + CAT5Include.C_FUNCTIONRESULTDESCRIPTION[Ord(resultPhoneSystem)];
                  Self.Terminate;
                end;
            finally
            end;
            Self.FSuperBusy := False;
          end;

        Self.FCancelled := Self.Terminated;

        if not Self.FCancelled then
          begin
            Self.FProgressBarCaption := 'Retrieving ' + strPhoneSystemName + ' data for ' + strClientName;
            Self.FSuperBusy := True;
            adoPhoneSystemItems := (Self.FUserSession AS TIWUserSession).GetClientMonthlyPhoneSystemBillingCharges(Self.FADOConnection, iEntityID);
            if not Assigned(adoPhoneSystemItems) then
              begin
                Self.FThreadResult := TFunctionResult.resultPhoneSystemErrorConnectingToPhoneSystem;
                Self.FThreadResultMessage := 'ERROR: ' + CAT5Include.C_FUNCTIONRESULTDESCRIPTION[Ord(Self.FThreadResult)];
                Self.Terminate;
              end;

            Self.FSuperBusy := False;
          end;

        Self.FCancelled := Self.Terminated;


        if (not Self.FCancelled) then
          begin
              // start the SQL transaction
            Self.FADOConnection.BeginTrans;

            // Create the monthly invoice order record for this hosted phone system customer
            strSQL := 'EXEC ' + CAT5Include.C_SP_CREATEORDER + ' ' +
                                IntToStr(iCompanyLocationID) + ', ' +
                                '''H'',''' +
                                FormatDateTime('MM/DD/YYYY', datePosted) + ''',' +
                                IntToStr(iEntityID);
            adoOrder.CommandText := strSQL;
            adoOrder.Open;
            iOrderID := adoOrder.FieldByName('id').AsInteger;
            strOrderNumber := adoOrder.FieldByName('order_number').AsString;
            adoOrder.Close;

            Self.FProgressBarCaption := 'Creating invoice ' + strOrderNumber + ' dated ' + FormatDateTime('MM/DD/YYYY', datePosted) + ' for ' + strClientName;

<< SNIP >>
                // Post the Monthly Invoice to A/R
                Self.FThreadResult := (Self.FUserSession as TIWUserSession).PostOrderToAR(iOrderID, datePosted, dGLJournalBatchID, Self.FADOConnection);

                if Self.FThreadResult <> TFunctionResult.resultSuccess then
                  begin
                    Self.FThreadResultMessage := 'ERROR: ' + CAT5Include.C_FUNCTIONRESULTDESCRIPTION[Ord(Self.FThreadResult)] + ' while attempting to post Order # ' + strOrderNumber + ' for ' + strClientName + ' to the A/R module.';
                  end
                else
                  begin
                    // Post the G/L Transactions
                    Self.FThreadResult := (Self.FUserSession as TIWUserSession).GL_PostGLJournalEntries(Self.FADOConnection, datePosted, dGLJournalBatchID, True);

                    if Self.FThreadResult <> TFunctionResult.resultSuccess then
                      begin
                        Self.FThreadResultMessage := 'ERROR: ' + CAT5Include.C_FUNCTIONRESULTDESCRIPTION[Ord(Self.FThreadResult)] + ' while attempting to post Order # ' + strOrderNumber + ' for ' + strClientName + ' to the G/L module.';
                      end
                  end;

                if Self.FThreadResult <> TFunctionResult.resultSuccess then
                  begin
                    Self.Terminate;
                  end;

              end;

            Self.FCancelled := Self.Terminated;

            // Commit or Rollback the SQL transaction
            if Self.FCancelled then
              Self.FADOConnection.RollbackTrans
            else
              begin
                Self.FADOConnection.CommitTrans;
                Inc(Self.FRecordsProcessed);
              end;

          end;

        adoClients.Next;

      end;
    adoClients.Close;


  Self.FADOConnection.Close;

end;

// Calculate the Long Distance Charges for this period
procedure TMonthlyPhoneSystemBillingProcessThread.CalculateLDCharges(ClientName: String;
                                                                      EntityID: Integer;
                                                                      ClientLDOptionsID: Integer;
                                                                      OrderID: Integer;
                                                                      PostingDate: TDateTime;
                                                                      DetailLineTypeID: Integer;
                                                                      DetailLineType: String;
                                                                      IsTaxable: Boolean;
                                                                      IsGST: Boolean;
                                                                      GLAccountDRID: Integer;
                                                                      GLAccountCRID: Integer);

begin
<< SNIP >>
end;
ScottWGast
 
Posts: 875
Joined: 23 May 2012 11:02


Return to JQLockIndicator

Who is online

Users browsing this forum: No registered users and 2 guests

Contact Us.