| auteur : Nourdine Falola | Par défaut ce bouton est inactif.
Pour l'activer, il faut initialiser l'indicateur de modification de la page via SetModified(TRUE) lorsque l'utilisateur a modifié une page.
On surcharge pour cela la fonction OnCommand de chaque CPropertyPage.
BOOL CMyPropertyPage:: OnCommand (WPARAM wParam, LPARAM lParam)
{
SetModified (TRUE);
return CPropertyPage:: OnCommand (wParam, lParam);
}
|
Ensuite on surcharge la fonction OnApply de la classe dérivée de CPropertyPage dans laquelle on a défini la fonction CWndReceived (voir Q/R précédente).
Il n'est pas besoin de redéfinir OnApply pour les autres pages.
Deux possibilités :
soit on traite l'action engendrée par le clic sur le bouton "Appliquer" (et "OK") dans OnApply (ce qui suppose de caster le CWnd* en la classe dérivée de CWnd qui gère la propertysheet et d'inclure le .h correspondant), solution moche
soit on utilise le message personnalisé que peut envoyer la propertypage à la classe dérivée de CWnd réceptrice.
On va utiliser la 2ème solution et laisser le soin à la classe dérivée de CWnd de gérer ce qui doit être fait après que l'utilisateur a appuyé sur "Appliquer" ou "OK".
On définit le message personnalisé dans le source de la propertypage
# define WM_USERAPPLY WM_USER + 5
|
et on surcharge OnApply pour cette même propertypage
BOOL CMyPropertyPage:: OnApply ()
{
m_pReceiver- > SendMessage (WM_USERAPPLY);
return CPropertyPage:: OnApply ();
}
|
On intercepte ensuite le message personnalisé WM_USERAPPLY dans la classe dérivée de CWnd qui gère la propertysheet (par exemple une view)
BEGIN_MESSAGE_MAP (CMyView, CView)
ON_COMMAND (ID_MENU_MACHIN, OnMenuMachin)
ON_MESSAGE (WM_USERAPPLY, OnUserApply)
END_MESSAGE_MAP ()
|
et on ajoute le gestionnaire OnUserApply correspondant
protected :
afx_msg void OnMenuMachin ();
afx_msg LRESULT OnUserApply (WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP ()
|
LRESULT CMyView:: OnUserApply (WPARAM wParam, LPARAM lParam)
{
TRACE (" CMyView::OnUserApply -- wParam = %x\n " , wParam);
...
return 0 ;
}
|
et c'est gagné!
|
| auteur : Nourdine Falola | Une feuille de propriétés est une boîte de dialogue à onglets (pages de propriétés) possédant 3 boutons "OK", "Annuler" et "Appliquer".
Pour construire une feuille de propriétés il faut :
utiliser l'éditeur de ressources pour créer un modèle de dialogue par onglet. Le titre des boîtes de dialogue (Caption) sera le titre des onglets.
générer une classe dérivant de CPropertyPage pour chaque modèle de dialogue, ajouter des membres pour les contrôles. Si on compte implémenter l'action du bouton "Appliquer" alors ajouter la fonction CWndReceiver et un membre CWnd* dans l'une des classes pages.
private :
CWnd* m_pReceiver;
public :
void CWndReceiver (CWnd* pReceiver) { m_pReceiver = pReceiver; }
|
générer une classe dérivant de CPropertySheet, lui ajouter une variable membre pour chaque page et appeler AddPage dans le constructeur pour chaque page.
public :
CMyPropertyPage1 m_page1;
CMyPropertyPage2 m_page2;
|
CMyPropertySheet:: CMyPropertySheet (LPCTSTR pszCaption, CWnd * pParentWnd = NULL , UINT iSelectPage = 0 )
{
AddPage (& m_page1);
AddPage (& m_page2);
m_page1.CWndReceiver (pParentWnd);
}
|
dans l'application, construire une instance de la classe dérivée de CPropertySheet et appeler DoModal (préférer un pointeur afin de pouvoir passer l'adresse de l'objet dérivé de CWnd appelant).
Par exemple dans une classe vue CMyView
private :
CMyPropertySheet * m_pPS;
|
CMyView:: CMyView (...):CView (...)
{
m_pPS = new CMyPropertySheet (" Titre " ,this );
}
|
CMyView:: OnMenuMachin ()
{
...
m_pPS - > SetTitle (" Eventuellement, modification du titre " );
m_pPS - > m_page1.m_attr1 = ... ;
m_pPS - > m_page1.m_attr2 = ... ;
m_pPS - > m_page2.m_attr1 = ... ;
m_pPS - > m_page2.m_attr2 = ... ;
m_pPS- > DoModal ();
}
|
CMyView:: ~ CMyView ()
{
delete m_pPS;
}
|
Eventuellement, implémenter le comportement du bouton "Appliquer" (inactif par défaut). Le clic sur "OK" quant à lui est déjà géré et actualise toutes les variables membres des pages.
|
| auteur : Farscape | Exemple:
BOOL CMyPropSheet:: OnInitDialog ()
{
BOOL bResult = CPropertySheet:: OnInitDialog ();
int ids [] = { IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP } ;
CWnd * pWnd;
for (int i= 0 ;i< sizeof (ids)/ sizeof (int );i+ + )
{
pWnd = GetDlgItem ( ids[i] );
if (pWnd)
{
pWnd- > EnableWindow (FALSE);
pWnd- > ShowWindow ( SW_HIDE );
}
}
|
pour désactiver le bouton Appliquer au démarrage on interviendra directement sur le style du contrôle:
dlgPropertySheet.m_psh.dwFlags | = PSH_NOAPPLYNOW;
dlgPropertySheet.DoModal ();
|
|
| auteur : Farscape | Le lancement d'une boîte d'une CPropertySheet ne permet pas de spécifier l'emplacement de celle-ci à l'écran.
La solution consistera à surcharger la fonction DoModal pour positionner notre fenêtre à l'emplacement voulu.
Exemple sur une classe dérivée de CPropertySheet:
# include <afxpriv.h>
# include <AFXIMPL.H> // attention ce header est definit dans les sources des MFC ,on peut rajouter le chemin des src MFC dans la recherche des includes...
int CMyPropertySheet:: DoModal ()
{
ASSERT_VALID (this );
ASSERT (m_hWnd = = NULL );
VERIFY (AfxDeferRegisterClass (AFX_WNDCOMMCTLS_REG));
AfxDeferRegisterClass (AFX_WNDCOMMCTLSNEW_REG);
BuildPropPageArray ();
CWinApp* pApp = AfxGetApp ();
if (pApp ! = NULL )
pApp- > EnableModeless (FALSE);
HWND hWndTop;
HWND hWndParent = CWnd:: GetSafeOwner_ (m_pParentWnd- > GetSafeHwnd (), & hWndTop);
AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader ();
psh- > hwndParent = hWndParent;
BOOL bEnableParent = FALSE;
if (hWndParent ! = NULL & & :: IsWindowEnabled (hWndParent))
{
:: EnableWindow (hWndParent, FALSE);
bEnableParent = TRUE;
}
HWND hWndCapture = :: GetCapture ();
if (hWndCapture ! = NULL )
:: SendMessage (hWndCapture, WM_CANCELMODE, 0 , 0 );
m_nModalResult = 0 ;
m_nFlags | = WF_CONTINUEMODAL;
AfxHookWindowCreate (this );
psh- > dwFlags | = PSH_MODELESS;
m_nFlags | = WF_CONTINUEMODAL;
HWND hWnd = (HWND):: PropertySheet ((PROPSHEETHEADER* )psh);
if (m_nx | | m_ny)
:: SetWindowPos (hWnd,NULL ,m_nx,m_ny,0 ,0 ,SWP_NOSIZE);
# ifdef _DEBUG
DWORD dwError = :: GetLastError ();
# endif
psh- > dwFlags & = ~ PSH_MODELESS;
AfxUnhookWindowCreate ();
if (hWnd = = NULL | | hWnd = = (HWND)- 1 )
{
TRACE1 (" PropertySheet() failed: GetLastError returned %d\n " , dwError);
m_nFlags & = ~ WF_CONTINUEMODAL;
}
int nResult = m_nModalResult;
if (ContinueModal ())
{
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle () & DS_NOIDLEMSG)
dwFlags | = MLF_NOIDLEMSG;
nResult = RunModalLoop (dwFlags);
}
if (m_hWnd ! = NULL )
{
SetWindowPos (NULL , 0 , 0 , 0 , 0 , SWP_HIDEWINDOW|
SWP_NOSIZE| SWP_NOMOVE| SWP_NOACTIVATE| SWP_NOZORDER);
}
if (bEnableParent)
:: EnableWindow (hWndParent, TRUE);
if (hWndParent ! = NULL & & :: GetActiveWindow () = = m_hWnd)
:: SetActiveWindow (hWndParent);
DestroyWindow ();
if (pApp ! = NULL )
pApp- > EnableModeless (TRUE);
if (hWndTop ! = NULL )
:: EnableWindow (hWndTop, TRUE);
return nResult;
}
|
l'include de la classe :
class CMyPropertySheet : public CPropertySheet
{
DECLARE_DYNAMIC (CMyPropertySheet)
public :
CMyPropertySheet (CWnd* pWndParent = NULL );
virtual int DoModal ();
void SetCoord (int x,int y){ m_nx= x;m_ny= y;}
public :
CMyPropertyPage1 m_Page1;
CMyPropertyPage2 m_Page2;
CMyPropertyPage3 m_Page3;
public :
int m_nx,m_ny;
public :
virtual ~ CMyPropertySheet ();
virtual void PostNcDestroy ();
protected :
DECLARE_MESSAGE_MAP ()
} ;
|
Attention n'oubliez pas d'initialiser les variables m_nx et m_ny dans le constructeur...
Utilisation dans le cadre de mon exemple:
CMyPropertySheet dlg;
dlg.SetCoord (500 ,200 );
dlg.DoModal ();
|
|
Consultez les autres F.A.Q.
|
|