Solved

CPropertySheet in a CFormView problem

Posted on 2002-03-15
13
1,036 Views
Last Modified: 2013-11-20
Hi,
I have a problem Destroy/Re-create CPropertySheet with ActiveX in one of the CPropertySheet in a CFormView.

I succeed adding CPropertySheet to a CFormView (using codeguru example: http://www.codeguru.com/propertysheet/inside_formview.shtml). I can Destroy/Re-Create this CPropertySheet dynamically in the CFormView with no problem. I Destroy the CPropertySheet and re-create it in order to change its style from Tab to Wizard. this action work fine only if there aren't any MS FlexGrid (ActiveX control) in one of the CPropertyPage I add to the CPropertSheet. when one of the CPropertyPage include MS FlexGrid control the application hang (take a full 100% CPU). this problem occurs only after destroy and re-create the CPropertySheet (and not in the first creation of CPropertySheet).

I read one of the comments to the codeguru article (http://www.codeguru.com/mfc/comments/26344.shtml), that suggests I need to perform the following steps:
Step 1 - Override OnInitDialog() for the child CPropertySheet, and add the WS_EX_CONTROLPARENT style.
BOOL CPropertySheet::OnInitDialog()
{
BOOL bResult = CPropertySheet::OnInitDialog();
ModifyStyleEx( 0, WS_EX_CONTROLPARENT );
return bResult;
}
Step 2 - you need to modify extended style of your ActiveX control. It should look like this:
BOOL CVsiControlsCtrl::PreCreateWindow( CREATESTRUCT& cs )
{
cs.lpszClass = _T("STATIC");
cs.dwExStyle |= WS_EX_CONTROLPARENT;
return COleControl::PreCreateWindow(cs);
}

Some thing similar is mention in Microsoft's resources: http://support.microsoft.com/default.aspx?scid=kb;EN-US;q149501
I tried what Microsoft article said but it doesn't help. I also tried to perform what the above codeguru comment suggest, but couldn't find a way to modify extended style of MS FlexGrid (see my question on Experts Exchange: http://www.experts-exchange.com/jsp/qManageQuestion.jsp?ta=mfc&qid=20277330).

thanks for any advise, suggestion.
0
Comment
Question by:BlueMoon
  • 7
  • 6
13 Comments
 
LVL 49

Expert Comment

by:DanRollins
ID: 6869560
What steps are you you doing to destroy the CPropertySheet?  

I think the only safe way may be to "Cancel" it... which you can do by CPropertySheet::PressButton( PSBTN_CANCEL ); or PSBTN_FINISH.

Regarding your MS FlexGrid control:  Did you add this to a CPropertyPage via the "Insert ActiveX..." option of the Dialog editor (followed by using the ClassWizard to create a Control-type variable for it), or are you creating it manually?  The former is preferrable to the latter.

-=-=-=-=-=-=-=-
Have you tried using the debugger to single-step through the actions that take place as you destroy the window?

-- Dan

0
 

Author Comment

by:BlueMoon
ID: 6871893
Hi,

Thanks for your comments.

1)
>>What steps are you you doing to destroy the CPropertySheet?  

bool CMyParamView::CreateMyParamSheet()
{
      try
      {
            if (m_pMyParamSheet != NULL)// define as data member of CFormView: CMyParamSheet * m_pMyParamSheet;
            {
                  m_pMyParamSheet->DestroyWindow();
                  delete m_pMyParamSheet;
                  m_pMyParamSheet = NULL;
            }
            CWnd* pwndPropSheetHolder = GetDlgItem(IDC_PLACEHOLDER);
            m_pMyParamSheet = new CMyParamSheet(pwndPropSheetHolder,m_enDialogStatus); //

            if (!m_pMyParamSheet->Create(pwndPropSheetHolder,
                  WS_CHILD | WS_VISIBLE, 0))
            {
                  delete m_pMyParamSheet;
                  m_pMyParamSheet = NULL;
                  return false;
            }

            // fit the property sheet into the place holder window, and show it
            CRect rectPropSheet;
            pwndPropSheetHolder->GetWindowRect(rectPropSheet);
            m_pMyParamSheet->SetWindowPos(NULL, 0, 0,
                  rectPropSheet.Width(), rectPropSheet.Height(),
                  SWP_NOZORDER | SWP_NOACTIVATE);
            
            return true;
      }
      catch(...)
      {
            //TODO: Error
            return false;
      }
      return false;
}


CMyParamSheet::CMyParamSheet(CWnd* pParentWnd,enum enDIALOG_STATUS i_enDialogStatus)
      :CPropertySheet("", pParentWnd)
{
      AddPage(&m_cMyParamPage);
      if (i_enDialogStatus == enNew)
            EnableStackedTabs(true);
      else
            SetWizardMode();
}

2)
>>I think the only safe way may be to "Cancel" it... which you can do by CPropertySheet::PressButton(
PSBTN_CANCEL ); or PSBTN_FINISH.

Doesnt work. I replaced the DestroyWindow line with:
m_pMyParamSheet->PressButton(PSBTN_FINISH);
in the above code.I also need to set the CProperySheet style before creating it (in the c'tor).

3)
>>Regarding your MS FlexGrid control:  Did you add this to a CPropertyPage via the "Insert ActiveX..." Option of the Dialog editor (followed by using the ClassWizard to create a Control-type variable for it), or are you creating it manually?  The former is preferrable to the latter.

I added the Flexgrid (Microsoft FlexGrid Control, version 6.0) using 'Component and Control" option in the 'Project' menu. After adding the control to my CPropertyPage dialog resource, VStudio added msflexgrid.h/cpp files to my project. Then I used the ClassWizard to add the Control-type variable. I don't recall the "Insert ActiveX... option you have mentioned. although VStudio add msflexgrid.h/cpp files to my project I couldn't find FlexGrid class in the ClassWizard. According to one of the comments I mention in my question (http://www.codeguru.com/mfc/comments/26344.shtml), I wish to implement the PreCreateWindow method of FlexGrid object in order to modify the FlexGrid style to 'WS_EX_CONTROLPARENT'.

4)
>>Have you tried using the debugger to single-step through the actions that take place as you destroy the window?

The problem doesn't occur while or after I destroy the CPropertySheet. It occurs after the re-creation of the CPropertySheet and its CProperyPages. This creation perform twice. One it work fine and in the second time after destroying the window (CPropertySheet) and re-create it the application hangs.
I performed 'Break' action in the VStudio debugger after the application hang. Here is the 'Call Stack' view:

CMapPtrToPtr::GetValueAt(void * 0x001905fa) line 182
CHandleMap::LookupTemporary(void * 0x001905fa) line 92 + 22 bytes
CHandleMap::FromHandle(void * 0x001905fa) line 124 + 12 bytes
CWnd::FromHandle(HWND__ * 0x001905fa) line 286 + 12 bytes
CWnd::GetNextWindow(unsigned int 2) line 255 + 78 bytes
_AfxNextControl(CWnd * 0x00302050 {CMyParamView hWnd=0x001605be}, CWnd * 0x00303160 {CTempWnd hWnd=0x00110624}, unsigned int 0) line 111 + 10 bytes
_AfxRemoveDefaultButton(CWnd * 0x00302050 {CMyParamView hWnd=0x001605be}, CWnd * 0x00303160 {CTempWnd hWnd=0x00110624}) line 351 + 15 bytes
_AfxCheckDefPushButton(CWnd * 0x00302050 {CMyParamView hWnd=0x001605be}, CWnd * 0x00303a10 {CTempWnd hWnd=0x001d05dc}, CWnd * 0x00303160 {CTempWnd hWnd=0x00110624}) line 417
COccManager::IsDialogMessageA(CWnd * 0x00302050 {CMyParamView hWnd=0x001605be}, tagMSG * 0x0041ca00 {msg=0x00000202 wp=0x00000000 lp=0x0011000b}) line 626
CWnd::IsDialogMessageA(tagMSG * 0x0041ca00 {msg=0x00000202 wp=0x00000000 lp=0x0011000b}) line 179 + 33 bytes
CWnd::PreTranslateInput(tagMSG * 0x0041ca00 {msg=0x00000202 wp=0x00000000 lp=0x0011000b}) line 3424
CFormView::PreTranslateMessage(tagMSG * 0x0041ca00 {msg=0x00000202 wp=0x00000000 lp=0x0011000b}) line 213
CWnd::WalkPreTranslateTree(HWND__ * 0x0011056c, tagMSG * 0x0041ca00 {msg=0x00000202 wp=0x00000000 lp=0x0011000b}) line 2667 + 18 bytes
CWinThread::PreTranslateMessage(tagMSG * 0x0041ca00 {msg=0x00000202 wp=0x00000000 lp=0x0011000b}) line 665 + 18 bytes
CWinThread::PumpMessage() line 841 + 30 bytes
CWinThread::Run() line 480 + 11 bytes
CWinApp::Run() line 400
AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00132628, int 1) line 49 + 11 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00132628, int 1) line 30
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 77e87903()

0
 
LVL 49

Accepted Solution

by:
DanRollins earned 200 total points
ID: 6872045
The error is occuring when the system is trying to process a WM_LBUTTONUP message.  

I'll bet that you are calling your CreateMyParamSheet on a button click or some other U/I event handler.  Here is what you need to do:

You must not delete and recreate the sheet during a single message.  Instead, when you get the request to swap between modes, you must simply call:

   PostMessage( UM_SwapSheetStyle );

=-=-=-=-=-
Add this to the CMyParamView.h file:

   LRESULT DoSwapSheetStyle(WPARAM, LPARAM);

=-=-=-=-=-

Then add this to CMyParamView.Cpp:

#define UM_SwapSheetStyle (WM_USER+100)

BEGIN_MESSAGE_MAP(CMyParamView, ...
     //{{AFX_MSG_MAP(CD01App)
...
     //}}AFX_MSG
     ON_MESSAGE(UM_SwapSheetStyle, DoSwapSheetStyle)
END_MESSAGE_MAP()

...

LRESULT CMyParamView:DoSwapSheetStyle( WPARAM, LPARAM)
{
    // do the destroy/delete here
    // Use the PropertySheet::PressButton if possible
    // do the create new here
    return( 0 );
}

--==-=-=-=-=-=-=-=-=-=--=
The upshot is that the destroy/re-create gets done while processing a different message.

--==-=-=-=-=-=-=-=-=-=--=
>>I wish to implement the PreCreateWindow method of FlexGrid object in order to modify the FlexGrid style to 'WS_EX_CONTROLPARENT'.

As I said in the other Q, there is no need to set the WS_EX_CONTROLPARENT in the GridControl.

--==-=-=-=-=-=-=-=-=-=--=
>>I also need to set the CProperySheet style before creating it (in the c'tor).

No you don't.  Just call
    m_pMyParamSheet->SetWizardMode();
before you call m_pMyParamSheet->Create()

>> I added the Flexgrid (Microsoft FlexGrid Control, version 6.0) using 'Component and Control"
>> option in the 'Project' menu.

I've found it easiest to use the dialog editor.  Just right-click in the dialog box and choose 'Insert ActiveX Control'  FOr one thing, that lets you set the size and placement of the control.

=--==-=-=-=-
One more note:  You may need to delete the individual CPropertyPages when you delete the Sheet.  See the example code in the MSDN help for CPropertySheet::Create

-- Dan
0
 

Author Comment

by:BlueMoon
ID: 6873678
1)
>>You must not delete and recreate the sheet during a single message.  Instead, when you get the request
to swap between modes, you must simply call:
  PostMessage( UM_SwapSheetStyle );

Thank you very much. It solve the hang problem. But I have some question:
a) Why does it solve the problem?
b) How can I call this event from my CPropertySheet and CPropertyPage objects? I tried performing PostMessage( UM_SwapSheetStyle ); and it doesn't reach to the LRESULT CMyParamView:DoSwapSheetStyle( WPARAM, LPARAM).

     if (m_pMyParamSheet != NULL)
     {
          CMyParamView *pMyParamView = (CMyParamView *)(m_pMyParamSheet->GetParent());
          if (pMyParamView != NULL)
          {
               pMyParamView->PostMessage(UM_SwapSheetStyle);
          }

2)
>>>>I also need to set the CProperySheet style before creating it (in the c'tor).

>>No you don't.  Just call
   m_pMyParamSheet->SetWizardMode();
before you call m_pMyParamSheet->Create()

I still need to call DestroyWindow and Create again. So how can I catch the 'Cancel' and ‘Finish' button event in CMyParamView? The only way I know in done in the CPropertyPage. I even don't know how it is done in the CPropertySheet.

3)
>>As I said in the other Q, there is no need to set the WS_EX_CONTROLPARENT in the GridControl.

Just for my future knowledge, Please answer my second question. How to add method/event catch to MS FlexGrid?
If you can, please answer it in the second thread.

Thanks

0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6873707
>>a) Why does it solve the problem?
A message often results in a flurry of activity.  In this case, there is a bunch of unfinished business (some cascading messages) that have not been processed by the PropertySheet.   So when you delete it, this unfisished business causes problems.  Posting a new message lets the current 'flurry' of messages run their course.

>>b) How can I call this event from my CPropertySheet...
 
You are just asking for trouble (you are basically deleting the horse out from underneath yourself).  But the code that you posted above should work.  Put some breakpoints in that code and in the message handler that we added to the view.

>> So how can I catch the 'Cancel' and ‘Finish' button...
I don't know this off hand.  I think you need to provide handlers in each page and then pass a message up the chain to the view.  You can use Spy++ to see what messages get sent automatically to the view.  There is probably one that you can intercept -- possibly with PreTranslateMessge()

-- Dan
0
 

Author Comment

by:BlueMoon
ID: 6873793
1)
>>But the code that you posted above should work.  Put some breakpoints in that code and in the message
handler that we added to the view.

My code doesn’t work. I put break point in the 'DoSwapSheetStyle'  method. If I call PostMessage from CPropertySheet it doesn’t work. Only if I am handle event (for example a button message) in the CFormView (CMyParamView class) the PostMessage work ok.

2)
>>I don't know this off hand.  I think you need to provide handlers in each page and then pass a message
up the chain to the view.

I succeed catching "manualy" the 'Cancel' message in the CPropertySheet. But this event should call again 'DoSwapSheetStyle' of CFormView, in order to change the CPropertySheet style. this means I need to destroy and re-create CPropertySheet window. Therefore I need to handle the 'Cancel' and 'Finish' message in the CFormView (which contains the CPropertySheet).  

thanks

0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:BlueMoon
ID: 6873897
1)
>>>>But the code that you posted above should work.  Put some breakpoints in that code and in the message handler that we added to the view.

>>My code doesn’t work.

I succeed Post the 'DoSwapSheetStyle' Message from the CPropertySheet. The problem was in the GetParent() method. I don't know what is the problem, but if I send manually the CFormView pointer ('this') to CPropertySheet the message is send. But if I use the code I post above using GetParent() method it isn't working. Do u know why?


Thanks


0
 

Author Comment

by:BlueMoon
ID: 6873991
another thing:

>>>>I also need to set the CProperySheet style before creating it (in the c'tor).

>>No you don't.  Just call
   m_pMyParamSheet->SetWizardMode();
before you call m_pMyParamSheet->Create()

I tried to Set the Property Sheet style without delete it and create it again (with new operator). It doesn't work.

this code doesn't wotk

          CWnd* pwndPropSheetHolder = GetDlgItem(IDC_PLACEHOLDER);

          if (m_pLCCoreParamSheet != NULL)
          {
               m_pLCCoreParamSheet->DestroyWindow();
               m_pLCCoreParamSheet->SetSheetStatus(m_enDialogStatus);
          }
          else
          {
               m_pLCCoreParamSheet = new CLCCoreParamSheet(pwndPropSheetHolder,m_enDialogStatus,this);
          }

          if (!m_pLCCoreParamSheet->Create(pwndPropSheetHolder,
               WS_CHILD | WS_VISIBLE, 0))
          {
               delete m_pLCCoreParamSheet;
               m_pLCCoreParamSheet = NULL;
               return false;
          }

          // fit the property sheet into the place holder window, and show it
          CRect rectPropSheet;
          pwndPropSheetHolder->GetWindowRect(rectPropSheet);
          m_pLCCoreParamSheet->SetWindowPos(NULL, 0, 0,
               rectPropSheet.Width(), rectPropSheet.Height(),
               SWP_NOZORDER | SWP_NOACTIVATE);
----------------------------------------------

this code work:
          CWnd* pwndPropSheetHolder = GetDlgItem(IDC_PLACEHOLDER);

          if (m_pLCCoreParamSheet != NULL)
          {
               m_pLCCoreParamSheet->DestroyWindow();
               delete m_pLCCoreParamSheet;
               m_pLCCoreParamSheet = NULL;
          }
          m_pLCCoreParamSheet = new CLCCoreParamSheet(pwndPropSheetHolder,m_enDialogStatus,this);
          if (!m_pLCCoreParamSheet->Create(pwndPropSheetHolder,
               WS_CHILD | WS_VISIBLE, 0))
          {
               delete m_pLCCoreParamSheet;
               m_pLCCoreParamSheet = NULL;
               return false;
          }

          // fit the property sheet into the place holder window, and show it
          CRect rectPropSheet;
          pwndPropSheetHolder->GetWindowRect(rectPropSheet);
          m_pLCCoreParamSheet->SetWindowPos(NULL, 0, 0,
               rectPropSheet.Width(), rectPropSheet.Height(),
               SWP_NOZORDER | SWP_NOACTIVATE);

any suggestion?

thanks
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6875534
>>u know why
No.  But maybe the property sheet is wrapped by some other window.  So its direct parent is not the View.  Use Spy++ to learn what is the parent.

>>this code work:
>>any suggestion?
For what?  If it works, then you are done.

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6924209
Hi BlueMoon,
Do you have any additional questions?  Do any comments need clarification?

-- Dan
0
 

Author Comment

by:BlueMoon
ID: 6924787
Thanks for you help.
But…
I had some problem with your solution:
When the system is busy, the swap between the modes cause weird phenomenon. For a second I get the same problem that I got before adding PostMessage. This look like a refresh problem. Any suggestions?

Thanks
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6924848
Thanks for accepting my comment as an answer.

>>Any suggestions?

Yes.  What you are doing is very unusual.  I have never seen an application that contains an embedded CPropertySheet that switches between wizard and non-wizard mode.  It just isn't done and I can think of no real reason to do it.

You might be able to eventually make it work by doing a bunch of patches and convoluted logic.  But if I were you, I would rethink the user interface.  There is probably a more normal and stable way to present your data to the user with out this oddball effect.

-- Dan
0
 

Author Comment

by:BlueMoon
ID: 6924893
Hi,
I have three DB tables that have a share key. When I wish to add new record I need to create three records for those tables. Therefore I need the Wizard mode. When I don't add new record I wish to view the tables in the Tab mode. So I have one Tab for each table.

do you have other way to implement the UI?

Thanks
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

This is to be the first in a series of articles demonstrating the development of a complete windows based application using the MFC classes.  I’ll try to keep each article focused on one (or a couple) of the tasks that one may meet.   Introductio…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

707 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

16 Experts available now in Live!

Get 1:1 Help Now