Updating a Gauge and Detecting a User Interrupt (C++ 5.0 and later)

Take the following steps to update a gauge during processing and detect a user interrupt:

1.

Start with the project that you created in Loading and Displaying an Image

2.

Go to the Project WorkSpace and click the ResourceView tab.

3.

Double-click Dialog and double-click IDD_TUTOR_DIALOG to bring up the application's dialog box.

4.

image\btncmd.gif Add a new button under the Cancel button. To do this, select the button control on the Controls toolbar. Then, click and drag to position the button on the dialog box.

5.

Add another button under the previously added button.

6.

Double-click the first button and change IDC_BUTTON1 to IDC_MEDIAN. Also, set the caption to Do &Median.

7.

Double-click the second button and change IDC_BUTTON2 to IDC_QUIT. Also, set the caption to &Quit.

8.

To build a gauge, select the Progress control on the Controls toolbar. (If you do not have a Progress control, you can use any control that has a rectangular shape and a border.) Put the gauge at the top of the dialog box to keep it away from the image.

9.

Double-click on the gauge and change the ID from IDC_PROGRESS1 to IDC_GAUGE.

10.

Close all the windows until you reach the project window, answering yes when asked whether to save the changes.

11.

Press Ctrl-W to go to the MFC Class Wizard; then do the following:

 

a.

In the Class name combo box, select CTutorDlg.

 

b.

In the Object IDs list box, select IDC_MEDIAN.

 

c.

In the Messages list box, select BN_CLICKED.

 

d.

Click the Add function button. Choose OK for the default function name (OnMedian).

 

e.

Click the Edit Code button to start entering the code.

12.

Enter new code as follows:

void CTutorDlg::OnMedian() 
{
  // TODO: Add your control notification handler code here
  // Enable the ProgressStatus event
  m_pRasterProc->PutEnableProgressEvent( TRUE );
  // Initialize the indicators
  fEscape = FALSE; // The user does not want to quit
  fInProc = TRUE; // Processing is taking place
  // Perform a relatively slow median filter
  m_pRasterProc->Median(m_LEADRasterView1.GetRaster(), 4); 
  // Clean up
  fInProc = FALSE; //  Processing is no longer taking place
  m_LEADRasterView1.ForceRepaint();
  // reset the gauge
  GetDlgItem(IDC_GAUGE)->InvalidateRect(NULL, TRUE);
}

13.

Press Ctrl-W to go to the MFC Class Wizard; then do the following:

 

a.

In the Class name combo box, select CTutorDlg.

 

b.

In the Object IDs list box, select IDC_QUIT.

 

c.

In the Messages list box, select BN_CLICKED.

 

d.

Click the Add function button. Choose OK for the default function name (OnQuit).

 

e.

Click the Edit Code button to start entering the code.

14.

Enter new code as follows:

void CTutorDlg::OnQuit() 
{
  // TODO: Add your control notification handler code here
  fEscape = TRUE; // The user wants to quit
  // Set the gauge back to the beginning
  GetDlgItem(IDC_GAUGE)->InvalidateRect(NULL, TRUE);
}

15.

Edit the TUTORDLG.H file and change the definition of CTutorDlg : CDialog by inserting the following lines after DECLARE_MESSAGE_MAP:

public:
   BOOL    fInProc;
   BOOL    fEscape;
   ILEADRasterProcess *m_pRasterProc;
   CRasterProcSink    *m_pRasterProcSink;
   IConnectionPoint   *m_pCP;
   DWORD               m_dwCookie;

16.

Edit the TUTORDLG.CPP file and add the following code to the end of the OnInitDialog function:

   //Instantiate the sink class and hold a pointer to it.
   m_pRasterProcSink = new CRasterProcSink;
   m_pRasterProcSink->m_pDlg = this;

   //Create the RasterProcess object
   CoCreateInstance(CLSID_LEADRasterProcess, NULL, CLSCTX_ALL,
                    IID_ILEADRasterProcess, (void**)&m_pRasterProc);

   //Establish a connection between source and sink.
   LPUNKNOWN pUnkSink = m_pRasterProcSink->GetIDispatch(FALSE);
   AfxConnectionAdvise(m_pRasterProc, DIID__LEADRasterProcessEvents, pUnkSink, FALSE, &m_dwCookie); 

   fInProc = FALSE;

17.

Press Ctrl-W to go to the MFC Class Wizard; then do the following:

 

a.

Click the Add Class button.

 

b.

Click New....

 

c.

Type CRasterProcSink for the name of the class

 

d.

Select CCmdTarget for the base class of the new class.

 

e.

Under Automation, click the Automation radio button.

 

f.

Click OK to create the class.

 

g.

In the RasterProcSink.h file, move the destructor so that it is public:

// Implementation
   virtual ~CRasterProcSink();
protected:

 

h.

In the RasterProcSink.h file, add the following to the top of the file:

 

 

class CTutorDlg;

 

i.

In the RasterProcSink.h file, add the following to the CRasterProcSink class in the //Attributes public section:

// Attributes
public:
CTutorDlg *m_pDlg;

 

j.

In the RasterProcSink.cpp file, add the following to the top of the file (after the #include "RasterProcSink.h")

#include "tutorDlg.h"

18.

Add #include statements so you can access the new class:

 

a.

In the Project Workspace, click the FileView tab.

 

b.

Double-click the tutor files folder to open it.

 

c.

Double-click the Header Files folder to open it.

 

d.

Double-click the StdAfx.h file to edit it.

 

e.

Add the following lines to the end of the file:

#include <AFXCTL.H>
#include "RasterProcSink.h"

19.

Edit the header for the Sink class:

 

a.

In the Project Workspace, click the FileView tab.

 

b.

Double-click the tutor files folder to open it.

 

c.

Double-click the Header Files folder to open it.

 

d.

Double-click the RasterProcSink.h file to edit it.

 

e.

Add the following just before //}}AFX_MSG:

afx_msg void OnProgressStatus(short iPercent);

20.

Edit the source for the Sink class:

 

a.

In the Project Workspace, click the FileView tab.

 

b.

Double-click the tutor files folder to open it.

 

c.

Double-click the Source Files folder.

 

d.

Double-click the RasterProcSink.cpp file to edit it.

 

e.

Add the following to the DISPATCH_MAP:

DISP_FUNCTION_ID(CRasterProcSink,"ProgressStatus",1,OnProgressStatus,VT_EMPTY,VTS_I2)

 

f.

Inside the BEGIN_INTERFACE_MAP section, change the INTERFACE_PART to the following:

INTERFACE_PART(CRasterProcSink, DIID__LEADRasterProcessEvents, Dispatch)

 

g.

Add the following to the end of the file:

void CRasterProcSink::OnProgressStatus(short iPercent)
{
  MSG msg;
  // are there any messages in the queue (like a button being pressed) ?
  while( PeekMessage(&msg,NULL, 0, 0, PM_REMOVE) )
  {
    TranslateMessage( &msg );      /* Translates virtual key codes.  */
    DispatchMessage( &msg );       /* Dispatches message to window.  */
  }
  if( !m_pDlg->fEscape ) // Look for the Click on the Quit button
  {
    CWnd *pwndGauge = m_pDlg->GetDlgItem( IDC_GAUGE );
    RECT rcGauge;
    pwndGauge->GetClientRect( &rcGauge );
    // Update the gauge
    short LineLength = (rcGauge.right-rcGauge.left) * iPercent / 100; 
    CDC *pdc = pwndGauge->GetDC();
    CPen hpen( PS_SOLID, rcGauge.bottom - rcGauge.top, RGB(0,0,0) ); // black pen
    CPen *pOldPen = pdc->SelectObject(&hpen);
    pdc->MoveTo( rcGauge.left, (rcGauge.top + rcGauge.bottom) / 2 );
    pdc->LineTo( rcGauge.left + LineLength, (rcGauge.top + rcGauge.bottom) / 2 );
    pdc->SelectObject(pOldPen);
  }
  else
    m_pDlg->m_pRasterProc->PutEnableProgressEvent(FALSE); // Cancel the task
}

21.

Press Ctrl-W to go to the MFC Class Wizard; then do the following:

 

a.

In the Class name combo box, select CTutorDlg.

 

b.

In the Object IDs list box, select CTutorDlg.

 

c.

In the Messages list box, select WM_DESTROY.

 

d.

Click the Add function button. Choose OK for the default function name (OnDestroy).

 

e.

Click the Edit Code button to start entering the code.

22.

Enter new code as follows:

void CTutorDlg::OnDestroy() 
{
   //Terminate a connection between source and sink.
   LPUNKNOWN pUnkSink = m_pRasterProcSink->GetIDispatch(FALSE);
   AfxConnectionUnadvise(m_pRasterProc, DIID__LEADRasterProcessEvents,
                         pUnkSink, FALSE, m_dwCookie); 
   delete m_pRasterProcSink;
   m_pRasterProc->Release();
   CDialog::OnDestroy();
}

23.

Continue with the remaining steps to prevent the user from quitting while a process is still going. This requires additional checks in the handling of the OK and Cancel buttons.

24.

Press Ctrl-W to go to the MFC Class Wizard; then do the following:

 

a.

In the Class name combo box, select CTutorDlg.

 

b.

In the Object IDs list box, select IDCANCEL.

 

c.

In the Messages list box, select BN_CLICKED.

 

d.

Click the Add function button. Choose OK for the default function name (OnCancel).

 

e.

Click the Edit Code button to start entering the code.

25.

Enter new code as follows:

void CTutorDlg::OnCancel() 
{
  // TODO: Add extra cleanup here
  if( !fInProc )
    CDialog::OnCancel();
}

26.

Press Ctrl-W to go to the MFC Class Wizard; then do the following:

 

a.

In the Class name combo box, select CTutorDlg.

 

b.

In the Object IDs list box, select IDOK.

 

c.

In the Messages list box, select BN_CLICKED.

 

d.

Click the Add function button. Choose OK for the default function name (OnOK).

 

e.

Click the Edit Code button to start entering the code.

27.

Enter new code as follows:

void CTutorDlg::OnOK() 
{
  // TODO: Add extra validation here
  if( !fInProc )
    CDialog::OnOK();
}

28.

Change the MFC settings for this project:

 

a.

On the main menu, select Project | Settings.

 

b.

Select the General tab.

 

c.

Under Microsoft Foundation Classes, select Use MFC in a Shared DLL.

 

d.

Select OK.

29.

Rebuild the application and run the program.