Home
Code & Snippets
Tutorials
GUI Docs
Projects
Drawings & Sketches
Links
Bio & Resume

Kindly hosted by


Support This Project

 

Properties Dialog (ala MFC)



Table of Contents


Introduction

Well, this tutorial is going to be about... You are right, about property dialog!  To build it, just follow the steps
outlined here and you will be OK!

Author' Web page

The source for the property dialog wasn't originally written by me, so giving to the author (Francisco Campos) a
credit is my responsibility.
You can find the full source with all the latest updates at his website, Beyond Data.

This is an example screenshot of the program you can make using his code. I only has taken it
to give my MFC programs the kind of look that he have in that screenshot.

How it all works

The basic workings of the Property Dialog are pretty straightforward and easy to understand. You overload
two functions, in one of which you bind your variables, and in second you're waiting for a moment when any
of those variables (or any of the others!) is changed, filter the ones you want to change by whatever you
want to (for example, you want to limit an integer variable to have values between -5 and 5, so you just do an
'if' statement on the new value of that variable to see if it is within -5 and 5) and away you go!

Creating a Dialog

To create a dialog we're going to use a a property dialog, simply create a new dialog in Visual Studio' editor and add
a plain static control to it (or any other control) with the dimensions of the dialog.  After you've added the resource,
create a new class based off it and add the following variables to it, so that your class looks like the code below:

class CSettingsDlg : public CDialog
{
	DECLARE_DYNAMIC(CSettingsDlg)

public:
	CSettingsDlg(CWnd* pParent = NULL);   // standard constructor
	virtual ~CSettingsDlg();

	EPropCtrl m_PropCtrl;
	
	IPropertyHost* m_pCurrentHost;
	
	void SetPropPointer(IPropertyHost *pHost);
// Dialog Data
	enum { IDD = IDD_SETTINGS };

protected:


	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	virtual BOOL OnInitDialog();
	afx_msg void OnSize(UINT nType, int cx, int CY);

	DECLARE_MESSAGE_MAP()
};
Well, after you've done it, make sure you have the SetPropPointer function like the one below:
void CSettingsDlg::SetPropPointer(IPropertyHost *pHost)
{
	if(pHost !=NULL)
	{
		m_pCurrentHost = pHost;
		m_PropCtrl.SetPropertyHost(pHost);
	}
	else
	{
		m_pCurrentHost = NULL;
		m_PropCtrl.SetPropertyChangeListener(NULL);
		m_PropCtrl.SetPropertyHost(NULL);
	}
}

This function will be looking for variable updates and we will call it when the property pointer has been changed.
Remember, that m_PropCtrl is basically a container for properties controls you're going to use to bind your values to,
so you need to assign it a control ON the dialog it can use as a window. So, let's assign a static control that's on the dialog
for it to use! To do that, in CSettingsDlg::OnInitDialog add the next line:

m_PropCtrl.Create(this, IDC_PROP);

Binding Variables

The best and only way to bind variables inside any class is to derive it from IPropertyHost and implement the following functions:

/*!
		Method details:
		@param pvProperty - Pointer to the property
		@param pvNewValue - Pointer to the new value
	*/
	virtual bool PropertyChanging(const void* pvProperty , void* pvNewValue);
	/*!
		Method details:
		@param PropList - Const. reference to the property list
	*/
	virtual void GetProperties(EPropList& PropList);
The first one will be called when a variable has changed it's value inside Property Dialog, and the other one when
you need to update or populate that dialog with new values/binds. Let's take a look at the CEntity' class implementation of those functions:
bool CEntity::PropertyChanging( const void* pvProperty , void* pvNewValue )
{
	if(pvProperty == &m_strName)
		m_strName = *(CString *)pvNewValue;

	return true;
}

void CEntity::GetProperties( EPropList& PropList )
{
	PropList.AddTab("Properties");
	PropList.AddPropSeparator(this, "Entity");
	PropList.AddPropString(this, "Name", &m_strName, true)->SetComment("Entity' name");
	PropList.AddPropInt(this, "ID", &m_uiID, "", false)->SetComment("Auto assigned Unique Identifier");
}
As you can see,  in PropertyChanging, the first value passed to the function  is the pointer to the property
that has been changed and the second is the new value for that property. If you want your binded variable to
change to the new value, simply return true from the function. If not, return false. Easy!
You also can use a more advanced method of checking whether the new value has the value/format you are looking
for,and, basing off that, allow or disallow the variable' change.
In the second function, GetProperties, you are simply defining the way you are going to allow the user to
change the variable. For example, if it is of an integer type, you can present it as textbox. If it's a color
(COLORREF) you can present the user with the Color Picker dialog. There are many different controls
corresponding to different situations, you take your time and explore some or all of them. The one and only parameter
this function provides you with is the list that holds all the controls binded to your values.

Adding Properties

To add a property, call EPropList' functions such as AddPropInt or AddPropCombo. Some of them may return
a pointer to a custom structure - for example, an AddPropCombo returns a pointer to combo control, into which you
can add your choices based on integer values. You can also set comments for any property to help user to understand
what the value is for and what formats it may have, for example.

Postmortem

Now what you do is create a dialog just as usual:

m_SettingsDlg.Create(IDD_SETTINGS, this);
m_SettingsDlg.ShowWindow(SW_SHOW);

and call m_SettingsDlg.SetPropPointer with the pointer to the class of the type of IPropertyHost. And that's it. You will
see the dialog with the values you've put into CEntity::GetProperties function appeared on the dialog. Here's an example
screenshot of what the Property Dialog looks like for the CEntity class:

Extra Notes

To run this tutorial, you need to download and compile PropertyLib software from Beyond Data' website.
Follow the middle link to get the source. Then, after compilation, copy LIB and DLL Files (Release or Debug) into same
directory as the application and rename them to PopLib.DLL and PropLib.LIB.

Download PropertyLib