Solved

Property sheet: selecting pages programatically - problems

Posted on 2006-11-24
10
1,778 Views
Last Modified: 2013-11-20
Ah hello.

I have a CPropertySheet.  On this sheet I have two pages.  When the first page gets hit, I want to activate the second immediately.  Consider this:

BOOL CFirstPage::OnSetActive()
{
      ((CMyPropertySheet*)GetParent())->SetActivePage ( 1 );
      return CPropertyPage::OnSetActive();
}

This fails; it results in the first page being displayed.  However, this works:

BOOL CFirstPage::OnSetActive()
{
      GetParent()->PostMessage(PSM_SETCURSEL, 1);
      return CPropertyPage::OnSetActive();
}

Now, I stepped into the code for SetActivePage, and found the following:

BOOL CPropertySheet::SetActivePage(int nPage)
{
      if (m_hWnd == NULL)
      {
            m_psh.nStartPage = nPage;
            return TRUE;
      }
      return (BOOL)SendMessage(PSM_SETCURSEL, nPage);
}

So the only difference between what works and what doesn't is the use of PostMessage over SendMessage.

I have uploaded a simple project to http://mrwad99.fortunecity.com/ demonstrating this.

Can someone explain what is going on to me please ?


TIA
0
Comment
Question by:mrwad99
  • 5
  • 4
10 Comments
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 18007472
PostMessage - message appended to queue
SendMessage - queue is bypassed and message handler called directly.


ps. your code that falis works here (when used on another tab - not the first one)


Also do you want to disable the first tab or do you want the prop sheet to start with the second tab as the default page being displayed?
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 18007504
Further to the ps - it also works here one first page (just tried it on a standard (non-customised) CPropertyPage/CPropertySheet)
0
 
LVL 48

Assisted Solution

by:AlexFM
AlexFM earned 125 total points
ID: 18007508
Well, asynchronous PostMessage is always better than synchronous SendMessage, it should be default choice unless pointer is sent or return value is required.
Maybe SetActivePage doesn't work because CFirstPage::OnSetActive is called as part of mouse or some other message handler synchronously, and its result is cancelled by code which is executed after this.
What is return value of GetParent())->SetActivePage? What is call stack in this function - maybe it is called from another instance of SetActivePage.

It is easy to explain why PostMessage is working - it is handled after current message handling finished. Stopping in debugger in CFirstPage::OnSetActive can give addinional information why it doesn't work. However, I always ask in such situations "What to do?" and not "Why?", MFC is not math or physics. And "What to do" is clear: use PostMessage if you don't have special reason to use SendMessage.
Translating this to general programming rules: always use asynchronous operations if you don't have special reason to make synchronous operation.
0
Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

 
LVL 19

Author Comment

by:mrwad99
ID: 18007668
Hi Andy

>>  PostMessage - message appended to queue
>> SendMessage - queue is bypassed and message handler called directly.

Yeah I am aware of the differences between the two.  

>> ps. your code that falis works here (when used on another tab - not the first one)

I don't get that.  I add four pages, and on OnSetActive within page 2, I add the line

((CMyPropertySheet*)GetParent())->SetActivePage ( 3 );

Nothing happens; the third page (which of course has index 2) is still displayed.

If I change it to

GetParent()->PostMessage(PSM_SETCURSEL, 3);

it works.

>> Also do you want to disable the first tab or do you want the prop sheet to start with the second tab as the default page being displayed?

Not at all; I have a bigger app that uses some logic to determine the page to be displayed.  For example, say the user has to make a choice on page 2, if they have specified this choice via the command line, we can skip page 2 and goto page 3.

Thanks.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 18007676
- Sorry AlexFM, I did not receive notification of your post - reading your comment now -
0
 
LVL 19

Author Comment

by:mrwad99
ID: 18007811
The second page's OnSetActive code contains the code to programatically select the *fourth* page.  This is why I start the callstacks with the second page becoming active.

Here is the callstack when I programatically select the fourth page with SetActivePage(), i.e.

BOOL CSecondPage::OnSetActive()
{
      ((CMyPropertySheet*)GetParent())->SetActivePage ( 3 );
      return CPropertyPage::OnSetActive();
}

When activating the second of four pages, the callstack is as follows:

CPropertyPage::OnNotify - PSN_SETACTIVE message.  'this' is a CSecondPage object

CSecondPage::OnSetActive() - contains code ((CMyPropertySheet*)GetParent())->SetActivePage ( 3 );

CPropertyPage::OnNotify - PSN_SETACTIVE message.  'this' is a CFourthPage object, which is what I would expect

CPropertyPage::OnSetActive() - (I did not override this for CFourthPage).  Ends up returning TRUE

I end up with the second page still being shown.

Here is the callstack when I programatically select the fourth page with PostMessage, i.e.

BOOL CSecondPage::OnSetActive()
{
      GetParent()->PostMessage(PSM_SETCURSEL, 3);
      return CPropertyPage::OnSetActive();
}

CPropertyPage::OnNotify - PSN_SETACTIVE message.  'this' is a CSecondPage object

CSecondPage::OnSetActive() - contains code GetParent()->PostMessage(PSM_SETCURSEL, 3);

CPropertyPage::OnSetActive() - 'this' is a CSecondPage object.  Ends up returning TRUE.  This is reached as a consequence of immediately dropping through the PostMessage() call in CSecondPage::OnSetActive().

CPropertyPage::OnNotify - PSN_SETACTIVE message.  'this' is a CFourthPage object, which is what I would expect.

CPropertyPage::OnSetActive() - I did not override OnSetActive() for CFourthPage, hence this base class version got called.  'this' is a CFourthPage object.

Now, I have spent time tracing through, but am missing the crucial point between these.  Can someone explain the differences and the resultant behaviour please ?
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 18007902
<Yeah I am aware of the differences between the two.  >

That is a major difference between the order in which actions are performed.  Often a reason why one works and the other occasionally fails.  <So the only difference between what works and what doesn't is the use of PostMessage over SendMessage>



The code snippet.
BOOL CFirstPage::OnSetActive()
{
     ((CMyPropertySheet*)GetParent())->SetActivePage ( 1 );
     return CPropertyPage::OnSetActive();
}

This fails; it results in the first page being displayed.  

Erm - here it works fine using a CPropertySheet/CPropertyPage without any customisation (well any that I *think* should interfere)

<For example, say the user has to make a choice on page 2, if they have specified this choice via the command line, we can skip page 2 and goto page 3>
Sounds like a wizard - have you looked at the virtual OnWizardNext function?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 18008094
>> Sounds like a wizard - have you looked at the virtual OnWizardNext function?

Yes, it is a wizard.  I could look at that function, but that would not answer the question I have posted.

Did you download and try the code Andy ?
0
 
LVL 44

Accepted Solution

by:
AndyAinscow earned 125 total points
ID: 18008304
I don't understand exactly why it isn't working with your code.
However I suspect it is because you have a mismatch caused by how PostMessage and SendMessage operate (as I said initially and as Alex explained in more detail).

You may have to let the base class have first stab, then use your SetActivePage then retunr from the function.


ps.OnWizardNext will allow you to bypass this hack.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 18027372
Thanks.  I ended up using OnWizardNext/OnWizardBack to do what I required.
0

Featured Post

Live: Real-Time Solutions, Start Here

Receive instant 1:1 support from technology experts, using our real-time conversation and whiteboard interface. Your first 5 minutes are always free.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Looking for a specific application/software 2 109
sum28 challenge 31 111
twoTwo  challenge 35 101
unix example issues 18 89
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
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.
Established in 1997, Technology Architects has become one of the most reputable technology solutions companies in the country. TA have been providing businesses with cost effective state-of-the-art solutions and unparalleled service that is designed…

815 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

11 Experts available now in Live!

Get 1:1 Help Now