|
Download the SimpleProperties sample project associated with this article (zipped source code, 50 kb)
|

Figure 1.Sample applications demonstrating the Prof-UIS property grid
Contents
Introduction
The Prof-UIS property grid control provides you with a flexible and elegant
tool for implementing a modern property grid like that available in Visual
Studio .NET/2005. It is designed for C++/MFC developers and can be used both
in Prof-UIS-based projects (most of the windows are controlled by Prof-UIS
classes) or in MFC projects. The major features that distinguish the Prof-UIS
property grid are as follow:
- Carefully considered design and active use of Prof-UIS grid classes makes the property grid an easily customizable and extensible tool.
- Support for browsing and editing properties of more than one object at same time. If the property grid is bound to several objects and a particular property has different values for these objects, the property is displayed as indeterminate. You can set a new value for this property in this mode and all these objects will have the same value for this property.
- A set of property types that meet the requirements of most of the projects. Due to the flexible architecture, this list can be easily extended.
- GUI Consistency. The property grid is consistent with other GUI themes available in Prof-UIS like Office 2000/XP/2003 and Visual Studio 2005.
- A great number of ready-to-use settings that you can adjust to suit your preferences.
Prof-UIS provides two sample projects that demonstrate how to use dynamic
control bars: PropertyGrid and
CompoundProperties. Besides you can download
a little SimpleProperties project that is a
result of step-by-step instructions provided in this article (includes intemediate projects for each step).
Design and implementation
The Prof-UIS property grid control window is instantiated and controlled
with the CExtPropertyGridCtrl class. The visible parts of
the property grid window are associated with their corresponding
classes as it is depicted in Figure 2 and Figure 3.

Figure 2. Property grid window elements and their corresponding Prof-UIS classes

Figure 3. Compound properties
The class hierarchy, which reflects all the classes used in the property
grid up to CWnd , is shown in Figure 4.

Figure 4. Class hierarchy for the Prof-UIS property grid control
Below we will describe the classes that are typically used when working with the property grid.
The CExtPropertyItem class implements the functionality common to
property categories (CExtPropertyCategory ) and property
values (CExtPropertyValue ) and it is a parent class
for these two classes. CExtPropertyItem should never be
instantiated in your code directly.
The CExtPropertyValue class implements the property
value in the hierarchical list of data. Visually, the property value
is divided into the left part, which shows the property value name,
and the right part, which displays the value itself and allows the
user to edit the value. CExtPropertyValue is inherited
from CExtPropertyItem and additionally provides the
functionality specific to property values. It keeps two
CExtGridCell objects corresponding to active and
default values of the property. Storing these two values allows
the user to reset the active value to the default one by clicking
the Reset item in the context menu that is invoked over
the name part of the property value.
CExtGridCell is a base class for the number of
cell classes implementing particular cell types, which can be
used for property values:
Grid cell class |
Cell type |
CExtGridCellBool |
Boolean values |
CExtGridCellCheckBox |
Check box control |
CExtGridCellColor |
Color selection control |
CExtGridCellComboBox |
Combo box control |
CExtGridCellDateTime |
Date/time selection control |
CExtGridCellDropListComboBox |
Combo box control without in-place activated edit; left button clicking on any cell area shows a pop-up list box window |
CExtGridCellFile |
File selection control |
CExtGridCellFileImage |
Icon selection control |
CExtGridCellFolder |
Folder selection control |
CExtGridCellFont |
Font selection control |
CExtGridCellFontFaceName |
Font name only selection control |
CExtGridCellHyperLink |
Hyperlink control |
CExtGridCellIPAddress |
IP address control |
CExtGridCellPassword |
Password control |
CExtGridCellRadioButton |
Radio button control |
CExtGridCellString |
Edit control |
CExtGridCellUpDown |
Up-down control for numeric values |
CExtGridCellUpDownColorPart |
Up-down control for the red, green or blue color attribute |
CExtGridCellUpDownFontWeight |
Up-down control for the font weight attribute |
CExtGridCellUpDownRectPart |
Up-down control for rectangle attributes |
CExtGridCellVariant |
Universal cell type that can be use for storing and editing data of different types |
Typically you create a CExtPropertyValue -derived class
in which you store a pointer to the bound object (whose properties
are displayed and set up with the property grid), specify the value
name, create a grid cell of the required type in the constructor,
and implement the Apply method that should do some specific action
related to the bound object.
The CExtPropertyValueCompound class implements the
compound property value in the hierarchical list of data. Visually,
the compound property value looks like a simple property value but
it serves as the parent item for a set of child property values
describing its parts. CExtPropertyValueCompound is
inherited from CExtPropertyValue . It keeps an array
of child property values which can be either simple property values
or compound property values.
Mixed property value
The CExtPropertyValueMixed class implements a property
value in the combined property store. This property value contains an
array of pointers to other property values which are stored in other property
stores whose values are displayed in the property grid. Applying a cell
value to the mixed property value causes all property values from the
property stores that are involved in the combine operation will have the
equal value.
Mixed combined property value
The CExtPropertyValueMixedCombined class is derived from
the CExtPropertyValueMixed class and implements the mixed
property value which contains a reference to combined property values.
Property category
The CExtPropertyCategory class implements a property
category. It is derived from CExtPropertyItem and additionally
supports an unspecified number of child elements, which can be either
property categories and/or property values. This allows you to design
the property grid with a category tree of any complexity.
Property store
The CExtPropertyStore class implements the root item in a
tree data structure with CExtPropertyItem objects (which describe
property categories and values). You can compare a property store with a document,
and the property grid window (which is based in the CExtPropertyGridWnd
class) as a view in terms of the document-view architecture.
Typically you declare an instance of the CExtPropertyStore class
in the class that represents one or more objects whose properties are displayed
in the property grid. Then you simply add the required categories and values
to this instance somewhere in the code before a pointer to the property store
is requested for the first time. You also need to define pairs of ...Get()
and ...Set() methods (only ...Get() methods for read-only
properties), for instance, TextColorGet() and TextColorSet() .
There are two ways to display properties of the particular object
in the property grid:
- Attach the property store using the
CExtPropertyGridCtrl::PropertyStoreSet()
method and update the property grid window with
the CExtPropertyGridCtrl::PropertyStoreSynchronize() method;
- Add a pointer to the property store to the built-in combo box
(the
CExtPropertyGridComboBoxBar class) and select this item
with the SetCurSel() method of the combo box.
The Prof-UIS property grid features displaying and editing properties
of more than one object simultaneously. Some properties whose values are
different for at least one object are specially marked: for most cell types,
the property value cell gets blank but the color selection cell is marked
with the question sign inside the gray rectangle. By modifying a property,
you modify the same property for all objects whose properties are displayed
in the property grid. The operation of combining properties of several
objects is performed with the Combine() method of the property store:
- declare a separate property grid in the same class where the property grid is used;
- you may need to clear this combined property grid from the previously added property
stores by using the
ItemRemove() method;
- use
Combine() method as many times as necessary;
- set the combined property store to the grid with one of the above described ways.
Property grid window
The CExtPropertyGrid class implements a property grid
window for displaying property categories and property values.
It is base for the CExtPropertyGridCategorized class, which
displays categories and values, and for the CExtPropertyGridSorted
class, which displays only the property values sorted alphabetically and all
the property categories are ignored. In most cases, you do not need to do
something special with regard to these classes.
Property grid toolbar
The CExtPropertyGridToolBar class implements the built-in
toolbar window in the property grid control. It automatically initializes
toolbar buttons which correspond to the tree grid windows created in the
property grid control by default, which are controlled by the
CExtPropertyGridCategorized and CExtPropertyGridSorted
classes.
The property grid toolbar can be hidden or shown with the following code:
CExtPropertyGridToolBar * pWnd =
STATIC_DOWNCAST(
CExtPropertyGridToolBar,
m_PGC.GetChildByRTC(
RUNTIME_CLASS(CExtPropertyGridToolBar)
)
);
if( pWnd == NULL )
return;
pWnd->ShowWindow( SW_HIDE ); // or SW_SHOW
m_PGC.RecalcLayout(); // obligatory to call this method of the property grid
Property grid combo box
The CExtPropertyGridComboBoxBar class implements the
combo box at the top of the property grid control. Each item in the
combo box corresponds to a property store. The item text is the display
name of the property store. When the selected item is changed, the
content of all the grids in the property grid control is reinitialized
from the content of the newly selected property store.
The property grid combo box can be hidden or shown with the following code:
CExtPropertyGridComboBoxBar * pWnd =
STATIC_DOWNCAST(
CExtPropertyGridComboBoxBar,
m_PGC.GetChildByRTC(
RUNTIME_CLASS(CExtPropertyGridComboBoxBar)
)
);
if( pWnd == NULL )
return;
pWnd->ShowWindow( SW_HIDE ); // or SW_SHOW
m_PGC.RecalcLayout(); // obligatory to call this method of the property grid
Tip panel
The CExtPropertyGridTipBar implements the tip panel at the
bottom of the property grid control. You set the tip text for the property
category and property value with the DescriptionSet() method
and the tip text is displayed automatically for the selected category or
value automatically.
The property grid combo box can be hidden or shown with the following code:
CExtPropertyGridTipBar * pWnd =
STATIC_DOWNCAST(
CExtPropertyGridTipBar,
m_PGC.GetChildByRTC(
RUNTIME_CLASS(CExtPropertyGridTipBar)
)
);
if( pWnd == NULL )
return;
pWnd->ShowWindow( SW_HIDE ); // or SW_SHOW
m_PG.RecalcLayout(); // obligatory to call this method of the property grid
Property grid control
The CExtPropertyGridCtrl class is designed to be a container
for other windows making up the property grid control
(See Figure 2). It automatically
detects the CExtPropertyGridWnd windows inside and aligns them
in the central part of the container, which is free of other windows.
The property grid control assumes all its windows have unique dialog
control identifiers. It also handles the WM_COMMAND messages
and updates the UI state of the command items with the identifiers equal
to the dialog control identifiers of the grid windows inside the container.
This makes it possible to hide and show the CExtPropertyGridWnd
windows with the CExtPropertyGridToolBar toolbar commands
automatically.
Step-by-step instructions on how to use it
Setting up and browsing properties of a single object
For the purpose of simplicity we will create a simple MFC dialog-based
application in which the Prof-UIS property grid is used for displaying and
editing a few properties of the MFC static control. The explanation will be
given in terms of the Visual Studio 2003 IDE.
- With the MFC Application Wizard, create a start-up dialog application
(Use SimpleProperties as the project name, select Dialog-based
as Application type and click Finish.
- Add a new
CStatic -based class (Select the SimpleProperties
project in the Class View window, select Project | Add Class...
from the main menu, double click the MFC Class template icon, specify
CMyStatic as the class name and CStatic as the base class name,
and click Finish).
- Add two
COLORREF variables and a boolean variable to the
protected section of the CMyStatic declaration:
protected:
COLORREF m_clrText, m_clrBackground;
bool m_bIniFont:1;
and initialize them in the constructor:
CMyStatic::CMyStatic():
m_clrText( RGB( 255, 173, 91 ) ),
m_clrBackground( RGB( 125,158, 192) ),
m_bIniFont ( true )
{
}
- Declare and implement
...Get() and ...Set() methods
for the two color properties and the font property of the static control.
Declare these methods in the public section of the CMyStatic
declaration (the MyStatic.h file):
public:
COLORREF TextColorGet() const;
void TextColorSet( COLORREF clr, bool bDefaultColor );
COLORREF BackgroundColorGet() const;
void BackgroundColorSet( COLORREF clr, bool bDefaultColor );
void FontGet( LOGFONT & _lf );
void FontSet( const LOGFONT & _lf );
Implement these methods in the MyStatic.cpp file:
COLORREF CMyStatic::TextColorGet() const
{
ASSERT_VALID( this );
return m_clrText;
}
void CMyStatic::TextColorSet( COLORREF clr, bool bDefaultColor )
{
ASSERT_VALID( this );
if( m_clrText == clr )
return;
m_clrText = clr;
INT nIndexC = -1, nIndexV = -1;
( STATIC_DOWNCAST(
CExtGridCellColor,
GetPropertyStore()->
ItemGetByName( _T("Colors"),
nIndexC )->
ItemGetByName( _T("TextColor"),
nIndexV )->
ValueActiveGet()
)
) -> m_PackedColor.SetColor( clr );
nIndexC = nIndexV = -1;
if ( bDefaultColor )
{
( STATIC_DOWNCAST(
CExtGridCellColor,
GetPropertyStore()->
ItemGetByName( _T("Colors"),
nIndexC )->
ItemGetByName( _T("TextColor"),
nIndexV )->
ValueDefaultGet()
)
) -> m_PackedColor.SetColor( clr );
}
if( GetSafeHwnd() == NULL )
return;
Invalidate();
UpdateWindow();
}
COLORREF CMyStatic::BackgroundColorGet() const
{
ASSERT_VALID( this );
return m_clrBackground;
}
void CMyStatic::BackgroundColorSet( COLORREF clr, bool bDefaultColor )
{
ASSERT_VALID( this );
if( m_clrBackground == clr )
return;
m_clrBackground = clr;
INT nIndexC = -1, nIndexV = -1;
( STATIC_DOWNCAST(
CExtGridCellColor,
GetPropertyStore()->
ItemGetByName( _T("Colors"),
nIndexC )->
ItemGetByName( _T("BackgroundColor"),
nIndexV )->
ValueActiveGet()
)
) -> m_PackedColor.SetColor( clr );
if ( bDefaultColor )
{
nIndexC = nIndexV = -1;
( STATIC_DOWNCAST(
CExtGridCellColor,
GetPropertyStore()->
ItemGetByName( _T("Colors"),
nIndexC )->
ItemGetByName( _T("BackgroundColor"),
nIndexV )->
ValueDefaultGet()
)
) -> m_PackedColor.SetColor( clr );
}
if( GetSafeHwnd() == NULL )
return;
Invalidate();
UpdateWindow();
}
void CMyStatic::FontGet( LOGFONT & _lf )
{
ASSERT_VALID( this );
if ( m_bIniFont == false )
{
(GetFont())->GetLogFont( &_lf );
}
else
{
(GetFont())->GetLogFont( &_lf );
_tcscpy( _lf.lfFaceName, _T("Tahoma") );
_lf.lfWeight = 800;
{
CWindowDC dc(0);
_lf.lfHeight =
- MulDiv(
dc.GetDeviceCaps(LOGPIXELSY), 28, 72
);
}
FontSet ( _lf );
}
}
void CMyStatic::FontSet( const LOGFONT & _lf )
{
ASSERT_VALID( this );
CFont font;
font.CreateFontIndirect( &_lf );
SetFont(&font);
font.Detach();
}
- Add a property store variable to the private section of the
CMyStatic
declaration and initialize it to NULL in the constructor:
// MyStatic.h
CExtPropertyStore * m_pPS;
// MyStatic.cpp
CMyStatic::CMyStatic():
m_pPS( NULL ), // IT'S IMPORTANT TO INITIALIZE IT TO NULL
m_clrText( RGB( 255, 173, 91 ) ),
m_clrBackground( RGB( 125,158, 192) )
{
}
Do not forget to include the Prof-UIS library in the StdAfx.h file:
#if (!defined __PROF_UIS_H)
#include <Prof-UIS.h>
#endif // (!defined __PROF_UIS_H)
NOTE: To successfully compile this project, you should compile
the Prof-UIS library first (e.g., using the MBCS Debug configuration)
and set up paths to the library files in the Visual Studio IDE,
which is described in the article Getting Started with Prof-UIS.
- Before creating the property store and filling it with categories
and values, let us create correspondent classes for property values. It is
handy to create a base class (
CMyStaticPropertyValueBase ) for
property value classes, in which the common methods and properties are
implemented, for example, keeping a pointer to the object (whose properties
are displayed with the property grid, i.e., an instance of CMyStatic )
and the property value name:
// MyStatic.h
class CMyStaticPropertyValueBase : public CExtPropertyValue
{
public:
CExtPropertyGridCtrl m_PGC;
CMyStatic * m_pStatic;
DECLARE_SERIAL( CMyStaticPropertyValueBase );
CMyStaticPropertyValueBase(
LPCTSTR strPropertyName = NULL,
CMyStatic * pStatic = NULL
);
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump( CDumpContext & dc ) const;
#endif
};
// MyStatic.cpp
IMPLEMENT_SERIAL( CMyStaticPropertyValueBase,
CExtPropertyValue, VERSIONABLE_SCHEMA|1 );
CMyStaticPropertyValueBase::CMyStaticPropertyValueBase(
LPCTSTR strPropertyName, // = NULL
CMyStatic * pStatic // = NULL
)
: CExtPropertyValue( strPropertyName )
, m_pStatic( pStatic )
{
#ifdef _DEBUG
if( m_pStatic != NULL )
{
ASSERT_VALID( m_pStatic );
}
#endif // _DEBUG
}
#ifdef _DEBUG
void CMyStaticPropertyValueBase::AssertValid() const
{
CExtPropertyValue::AssertValid();
if( m_pStatic != NULL )
{
ASSERT_VALID( m_pStatic );
}
}
void CMyStaticPropertyValueBase::Dump( CDumpContext & dc ) const
{
CExtPropertyValue::Dump( dc );
}
#endif
Here are two classes for implementing the text color property value and the background color property value:
// MyStatic.h
class CMyStaticProperty_TextColor : public CMyStaticPropertyValueBase
{
public:
DECLARE_SERIAL( CMyStaticProperty_TextColor );
CMyStaticProperty_TextColor(
CMyStatic * pStatic = NULL
);
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump( CDumpContext & dc ) const;
#endif
virtual void Apply(
CExtGridCell * pValue = NULL
);
};
class CMyStaticProperty_BackgroundColor :
public CMyStaticPropertyValueBase
{
public:
DECLARE_SERIAL( CMyStaticProperty_BackgroundColor );
CMyStaticProperty_BackgroundColor(
CMyStatic * pStatic = NULL
);
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump( CDumpContext & dc ) const;
#endif
virtual void Apply(
CExtGridCell * pValue = NULL
);
};
// MyStatic.cpp
IMPLEMENT_SERIAL( CMyStaticProperty_TextColor,
CMyStaticPropertyValueBase,
VERSIONABLE_SCHEMA|1 );
CMyStaticProperty_TextColor::CMyStaticProperty_TextColor(
CMyStatic * m_pStatic // = NULL
)
: CMyStaticPropertyValueBase(
_T("TextColor"),
m_pStatic
)
{
DescriptionSet( _T("Specifies the text color.") );
CExtGridCellColor * pValue =
STATIC_DOWNCAST(
CExtGridCellColor,
ValueActiveGetByRTC( RUNTIME_CLASS(CExtGridCellColor) )
);
ASSERT_VALID( pValue );
if( m_pStatic != NULL )
pValue->m_PackedColor.SetColor(
m_pStatic->TextColorGet()
);
ValueDefaultFromActive();
}
#ifdef _DEBUG
void CMyStaticProperty_TextColor::AssertValid() const
{
CMyStaticPropertyValueBase::AssertValid();
}
void CMyStaticProperty_TextColor::Dump( CDumpContext & dc ) const
{
CMyStaticPropertyValueBase::Dump( dc );
}
#endif
void CMyStaticProperty_TextColor::Apply(
CExtGridCell * pValue // = NULL
)
{
ASSERT_VALID( this );
#ifdef _DEBUG
if( pValue != NULL )
{
ASSERT_VALID( pValue );
ASSERT_KINDOF( CExtGridCellColor, pValue );
}
#endif // _DEBUG
CMyStaticPropertyValueBase::Apply( pValue );
if( m_pStatic == NULL )
return;
CExtGridCellColor * pValueColor =
STATIC_DOWNCAST(
CExtGridCellColor,
( ( pValue == NULL ) ? ValueActiveGet() : pValue )
);
m_pStatic->TextColorSet(
pValueColor->m_PackedColor.GetColor(), false
);
} // CMyStaticProperty_TextColor
IMPLEMENT_SERIAL( CMyStaticProperty_BackgroundColor,
CMyStaticPropertyValueBase,
VERSIONABLE_SCHEMA|1 );
CMyStaticProperty_BackgroundColor::
CMyStaticProperty_BackgroundColor(
CMyStatic * m_pStatic // = NULL
)
: CMyStaticPropertyValueBase(
_T("BackgroundColor"),
m_pStatic
)
{
DescriptionSet( _T("Specifies the background color.") );
CExtGridCellColor * pValue =
STATIC_DOWNCAST(
CExtGridCellColor,
ValueActiveGetByRTC( RUNTIME_CLASS(CExtGridCellColor) )
);
ASSERT_VALID( pValue );
if( m_pStatic != NULL )
pValue->m_PackedColor.SetColor(
m_pStatic->BackgroundColorGet()
);
ValueDefaultFromActive();
}
#ifdef _DEBUG
void CMyStaticProperty_BackgroundColor::AssertValid() const
{
CMyStaticPropertyValueBase::AssertValid();
}
void CMyStaticProperty_BackgroundColor::Dump( CDumpContext & dc ) const
{
CMyStaticPropertyValueBase::Dump( dc );
}
#endif
void CMyStaticProperty_BackgroundColor::Apply(
CExtGridCell * pValue // = NULL
)
{
ASSERT_VALID( this );
#ifdef _DEBUG
if( pValue != NULL )
{
ASSERT_VALID( pValue );
ASSERT_KINDOF( CExtGridCellColor, pValue );
}
#endif // _DEBUG
CMyStaticPropertyValueBase::Apply( pValue );
if( m_pStatic == NULL )
return;
CExtGridCellColor * pValueColor =
STATIC_DOWNCAST(
CExtGridCellColor,
( ( pValue == NULL ) ? ValueActiveGet() : pValue )
);
m_pStatic->BackgroundColorSet(
pValueColor->m_PackedColor.GetColor(), false
);
}
Here is a class for implementing the text font property value:
// MyStatic.h
class CMyStaticProperty_Font : public CMyStaticPropertyValueBase
{
public:
DECLARE_SERIAL( CMyStaticProperty_Font );
CMyStaticProperty_Font(
CMyStatic * pStatic = NULL
);
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump( CDumpContext & dc ) const;
#endif
virtual void Apply(
CExtGridCell * pValue = NULL
);
};
// MyStatic.cpp
IMPLEMENT_SERIAL( CMyStaticProperty_Font,
CMyStaticPropertyValueBase,
VERSIONABLE_SCHEMA|1 );
CMyStaticProperty_Font::CMyStaticProperty_Font(
CMyStatic * pStatic // = NULL
)
: CMyStaticPropertyValueBase(
_T("Font"),
pStatic
)
{
DescriptionSet( _T("Specifies the text font.") );
CExtGridCellFont * pValue =
STATIC_DOWNCAST(
CExtGridCellFont,
ValueActiveGetByRTC( RUNTIME_CLASS(CExtGridCellFont) )
);
ASSERT_VALID( pValue );
if( m_pStatic != NULL )
{
LOGFONT _lf;
m_pStatic->FontGet( _lf );
pValue->DataSet( _lf );
}
ValueDefaultFromActive();
}
#ifdef _DEBUG
void CMyStaticProperty_Font::AssertValid() const
{
CMyStaticPropertyValueBase::AssertValid();
}
void CMyStaticProperty_Font::Dump( CDumpContext & dc ) const
{
CMyStaticPropertyValueBase::Dump( dc );
}
#endif
void CMyStaticProperty_Font::Apply(
CExtGridCell * pValue // = NULL
)
{
ASSERT_VALID( this );
#ifdef _DEBUG
if( pValue != NULL )
{
ASSERT_VALID( pValue );
ASSERT_KINDOF( CExtGridCellFont, pValue );
}
#endif // _DEBUG
CMyStaticPropertyValueBase::Apply( pValue );
if( m_pStatic == NULL )
return;
CExtGridCellFont * pValueFont =
STATIC_DOWNCAST(
CExtGridCellFont,
( ( pValue == NULL ) ? ValueActiveGet() : pValue )
);
LOGFONT _lf = pValueFont->DataGet();
m_pStatic->FontSet( _lf );
} // CMyStaticProperty_Font
- It is convenient to create the property store object when
it is requested for the first time. So, here is the declaration
and definition of the
GetPropertyStore method:
// MyStatic.h
public:
CExtPropertyStore * GetPropertyStore();
// MyStatic.cpp
CExtPropertyStore * CMyStatic::GetPropertyStore()
{
ASSERT_VALID( this );
if( m_pPS != NULL )
return m_pPS;
m_pPS = new CExtPropertyStore;
CExtPropertyCategory * pCategoryWindow =
new CExtPropertyCategory( _T("Colors") );
pCategoryWindow->DescriptionSet( _T("Window colors.") );
VERIFY( m_pPS->ItemInsert( pCategoryWindow ) );
pCategoryWindow->ItemInsert(
new CMyStaticProperty_TextColor( this )
);
pCategoryWindow->ItemInsert(
new CMyStaticProperty_BackgroundColor( this )
);
VERIFY( m_pPS->ItemInsert(
new CMyStaticProperty_Font( this )
)
);
return m_pPS;
}
Since memory for the property store is allocated dynamically, do not forget to delete its pointer in the destructor:
CMyStatic::~CMyStatic()
{
if( m_pPS != NULL ) m_pPS->Delete();
}
- Since some run-time information about
CMyStatic will
be accessed, make sure that MFC's DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC
macros are declared and implemented:
//MyStatic.h
public:
DECLARE_DYNAMIC(CMyStatic)
//MyStatic.cpp
IMPLEMENT_DYNAMIC(CMyStatic, CStatic)
- The last thing we need to add to the
CMyStatic class is
the WM_CTLCOLOR reflection handler. Right-mouse click the
CMyStatic node in the Class window, select Properties,
click the Messages button in the toolbar built into the Properties
window and select <Add> OnCtlColor next to the =WM_CTLCOLOR
message. Fill in the method's body:
HBRUSH CMyStatic::CtlColor(CDC* pDC, UINT nCtlColor)
{
LOGFONT _lf;
FontGet ( _lf );
m_bIniFont = false;
CFont _fnt;
_fnt.CreateFontIndirect( &_lf );
pDC->SelectObject(&_fnt);
_fnt.Detach();
pDC->SetTextColor( m_clrText );
pDC->SetBkMode(TRANSPARENT);
CBrush br ( m_clrBackground );
return (HBRUSH)br.Detach();
}
- Open the dialog resource in the Visual Studio editor. You can see the
default static control labeled with TODO: Place dialog controls here.
Change its id to
IDC_STATIC1 and modify its caption to My Text.
Adjust its size and location as it is depicted in
Figure 5. Declare a variable of the
CMyStatic class in the public section of the
CSimplePropertiesDlg class:
public:
CMyStatic m_wndStatic1;
Do not forget to include the declaration of this class first:
// SimplePropertiesDlg.h : header file
//
#include "MyStatic.h"
Add a DDX_Control entry for the m_wndStatic1 to
subclass the resource control:
void CSimplePropertiesDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATIC1, m_wndStatic1);
}
- Add a Custom control to the dialog resource and change its id to
IDC_PROPERTY_GRID_CTRL . Also set its class to
ProfUIS-PropertyGridCtrl. Adjust its size and location
as it is depicted in Figure 5.
Declare a variable of the CExtPropertyGridCtrl type
in the public section of the CSimplePropertiesDlg class:
public:
CExtPropertyGridCtrl m_PGC;
Subclass this window with DDX_Control() in the
CSimplePropertiesDlg::DoDataExchange() handler:
DDX_Control(pDX, IDC_PROPERTY_GRID_CTRL, m_PGC);
- Somewhere at the end of the
CSimplePropertiesDlg::DoDataExchange() handler,
add the following code:
m_wndStatic1.GetPropertyStore()->NameSet(_T("Static1"));
CExtPropertyGridComboBoxBar * pCombo =
STATIC_DOWNCAST(
CExtPropertyGridComboBoxBar,
m_PGC.GetChildByRTC(
RUNTIME_CLASS(CExtPropertyGridComboBoxBar)
)
);
ASSERT_VALID( pCombo );
pCombo->PropertyStoreInsert( m_wndStatic1.GetPropertyStore() );
pCombo->SetCurSel(0);
- Compile the project and run the application.
You should get a picture like in the
Figure 5.

Figure 5. Default settings for the static control
Working with multiple objects of the same type
The Prof-UIS property grid allows you to display and edit properties of
more than one object simultaneously and it is really easy.
- Open the dialog resource in the Visual Studio editor and add one more
static control. Adjust its size and position as it is depicted in
Figure 6. Change its id to
IDC_STATIC2 . Declare a variable of the CMyStatic
class in the public section of the CSimplePropertiesDlg class:
public:
CMyStatic m_wndStatic1, m_wndStatic2;
Add a DDX_Control entry for the m_wndStatic2
to subclass the resource control:
void CSimplePropertiesDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATIC1, m_wndStatic1);
DDX_Control(pDX, IDC_STATIC2, m_wndStatic2);
}
- Let us change the default colors of the newly added control
and leave the default text color intact. Find the below line in
CSimplePropertiesDlg::OnInitDialog() :
m_wndStatic1.GetPropertyStore()->NameSet(_T("Static1"));
and insert the following code after it:
m_wndStatic2.GetPropertyStore()->NameSet(_T("Static2"));
m_wndStatic2.BackgroundColorSet( RGB( 255, 173, 91 ), true );
m_wndStatic2.TextColorSet( RGB( 125,158, 192), true );
- Add the property store corresponding to the second static
control to the combo box:
pCombo->PropertyStoreInsert( m_wndStatic2.GetPropertyStore() );
- Declare a new property store variable in the public section
of the
CSimplePropertiesDlg class:
CExtPropertyStore m_PropertyStoreCompoundSelection;
- You can now combine the properties of the two static
text controls in this property store:
m_PropertyStoreCompoundSelection.Combine(
m_wndStatic1.GetPropertyStore()
);
m_PropertyStoreCompoundSelection.Combine(
m_wndStatic2.GetPropertyStore()
);
m_PropertyStoreCompoundSelection.NameSet( _T("Static1 & Static2") );
pCombo->PropertyStoreInsert( &m_PropertyStoreCompoundSelection );
- Compile the project, run the application, and select
Static1 & Static2 in the combo box. The application should look like in the picture below:

Figure 6. Properties of both static text controls displayed in the property grid
Since the background color and the text color are different for both static text controls, the color icons display the question mark.
Serializing properties to the registry
- Declare three methods in the public section of the
CSimplePropertiesDialog class:
// SimplePropertiesDlg.h
public:
void _Load();
void _Save();
CExtSafeString _GetStateRegKeyPath();
The first two methods load and save the properties from/to the registry and the third one is a helper method:
// SimplePropertiesDlg.cpp
void CSimplePropertiesDlg::_Load()
{
try
{
m_PropertyStoreCompoundSelection.ItemRemove();
CMemFile _file;
if( ! CExtCmdManager::FileObjFromRegistry(
_file,
_GetStateRegKeyPath()
)
)
{
//ASSERT( FALSE );
return;
}
_file.Seek( 0, CFile::begin );
CArchive _ar( &_file, CArchive::load );
m_wndStatic1.GetPropertyStore()->Serialize( _ar );
m_wndStatic2.GetPropertyStore()->Serialize( _ar );
}
catch( CException * pException )
{
ASSERT( FALSE );
pException->Delete();
}
catch( ... )
{
ASSERT( FALSE );
}
}
void CSimplePropertiesDlg::_Save()
{
try
{
CMemFile _file;
CArchive _ar( &_file, CArchive::store );
m_wndStatic1.GetPropertyStore()->Serialize( _ar );
m_wndStatic2.GetPropertyStore()->Serialize( _ar );
_ar.Flush();
_ar.Close();
_file.Seek( 0, CFile::begin );
if( ! CExtCmdManager::FileObjToRegistry(
_file,
_GetStateRegKeyPath()
)
)
{
ASSERT( FALSE );
return;
}
}
catch( CException * pException )
{
ASSERT( FALSE );
pException->Delete();
}
catch( ... )
{
ASSERT( FALSE );
}
}
CExtSafeString CSimplePropertiesDlg::_GetStateRegKeyPath()
{
return
CExtCmdManager::GetSubSystemRegKeyPath(
_T("Property Grid Control State"),
_T("Property Grid Control State"),
_T("Foss"),
_T("SimpelProperties")
);
}
- Call the
_Load() method somewhere in
CSimplePropertiesDlg::OnInitDialog() before
you add property stores to the property grid control:
// SimplePropertiesDlg.cpp
_Load(); // <- Add this line
CExtPropertyGridComboBoxBar * pCombo =
STATIC_DOWNCAST(
CExtPropertyGridComboBoxBar,
m_PGC.GetChildByRTC(
RUNTIME_CLASS(CExtPropertyGridComboBoxBar)
)
);
ASSERT_VALID( pCombo );
Override the OnOK() and OnCancel() methods
of the CDialog class and invoke the _Save()
method in the overridden versions of these methods:
// SimplePropertiesDlg.h
protected:
virtual void OnOK();
virtual void OnCancel();
// SimplePropertiesDlg.cpp
void CSimplePropertiesDlg::OnOK()
{
_Save();
CDialog::OnOK();
}
void CSimplePropertiesDlg::OnCancel()
{
_Save();
CDialog::OnCancel();
}
The _Save() method saves the current properties
of the static text windows (stored in their property stores) to the registry.
- At this moment, if you compile and run the application, you will notice
that the properties kept in both property stores are loaded and saved
successfully from/to registry. By changing properties in the property grid
control for any static text control or both, clicking OK and running
the application again, you can see that although properties in the property
grid control are restored successfully, the appearance of both controls
remains default. To renew the control properties, inherit the
CSimplePropertiesDlg() class from
CExtPropertyItem::IPropertyItemEnumSite class
CSimplePropertiesDlg : public CDialog
, public CExtPropertyItem::IPropertyItemEnumSite
and implement the OnPropertyItemEnum() virtual method:
// SimplePropertiesDlg.h
private:
virtual bool OnPropertyItemEnum(
CExtPropertyItem * pItem,
LPVOID pCookie = NULL
);
// SimplePropertiesDlg.cpp
bool CSimplePropertiesDlg::OnPropertyItemEnum(
CExtPropertyItem * pItem,
LPVOID pCookie // = NULL
)
{
ASSERT_VALID( this );
ASSERT_VALID( pItem );
CMyStaticPropertyValueBase * pValue =
DYNAMIC_DOWNCAST(
CMyStaticPropertyValueBase,
pItem
);
if( pValue != NULL )
{
CMyStatic * pStatic =
(CMyStatic *)pCookie;
ASSERT_VALID( pStatic );
ASSERT_KINDOF( CMyStatic, pStatic );
pValue->m_pStatic = pStatic;
pValue->Apply();
} // if( pValue != NULL )
return true;
}
- To invoke the
OnPropertyItemEnum() method, find
these two lines in the CSimplePropertiesDlg::_Load() method
m_wndStatic1.GetPropertyStore()->Serialize( _ar );
m_wndStatic2.GetPropertyStore()->Serialize( _ar );
and add the following two lines right after them:
m_wndStatic1.GetPropertyStore()->Enum( this, &m_wndStatic1 );
m_wndStatic2.GetPropertyStore()->Enum( this, &m_wndStatic2 );
- Compile and run the application. Change some properties,
close the application and run it again. You will see that the
static text controls are serialized properly.
FAQ about the Prof-UIS property grid
What is the Prof-UIS property grid?
The Prof-UIS property grid is a set of C++ classes that allows you
implement a modern, feature-rich Visual Studio .Net like property grid
window for browsing and editing properties of objects of any complexity.
What is the combine operation with regard to the property grid?
The Prof-UIS property grid features displaying and editing properties
of more than one object. Each object has its own tree of properties that
is described by its property store (the CExtPropertyStore class).
You can create a separate instance of CExtPropertyStore and combine
properties of several objects in it so that you can subsequently display
the combined property store in the property grid:
m_PropertyStoreCompoundSelection.Combine(
m_wndStatic1.GetPropertyStore()
);
m_PropertyStoreCompoundSelection.Combine(
m_wndStatic2.GetPropertyStore()
);
m_PropertyGrid.PropertyStoreSet(m_PropertyStoreCompoundSelection );
m_PropertyGrid.PropertyStoreSynchronize();
Is it possible to use the Prof-UIS
property grid as a control in a MFC project that does
not use other Prof-UIS controls?
Yes, you can use the Prof-UIS property grid as a standalone control.
Keywords
The sample application attached to this article demonstrates the following keywords:
Technology, toolkit, programming language, IDE
|
MFC, C++, Prof-UIS, Visual Studio NET, Visual Studio 6
|
Graphical User Interface (GUI)
|
GUI sample, dialog-based application, property grid control,
displaying and editing properties, categorized view, alphabetic view, combining properties,
grid cells
|
Prof-UIS and MFC classes
|
CDialog, CExtPropertyItem, CExtPropertyValue, CExtGridCell, CExtPropertyValueCompound,
CExtPropertyValueMixed, CExtPropertyValueMixedCombined, CExtPropertyCategory, CExtPropertyStore,
CExtPropertyGrid, CExtPropertyGridToolBar, CExtPropertyGridComboBoxBar, CExtPropertyGridTipBar,
CExtPropertyGridCtrl
|
|