Click here to Skip to main content
Click here to Skip to main content
Go to top

COptionTree

, 19 Sep 2002
Rate this:
Please Sign up or sign in to vote.
An article on an easy and neat way to include options, preferences, or settings into your applications

 

What's New with 2.0

The long awaited 2.0 version is here. Good news is you get new features, new options, new items, and new bugs. Bad news is, a lot has changed so you may need to change around your current code to implement the new version. (Thanks to Tom and Irfan for there fixes for bugs that haunted me at night when the lights are off. They deserve their names in bold.)

Contents

Features

Below are some of the many features that COptionTree has.

  • Static controls.
  • Edit controls.
  • Combo box controls.
  • Check box controls.
  • Radio controls.
  • Spinner controls.
  • Color controls.
  • Date and time controls.
  • Image controls.
  • Font controls.
  • Files and folders controls.
  • IP Address controls..
  • Hyperlink controls.
  • Automatic column resize limit.
  • Easy customized options for different GUI style.
  • Easy customized options for each control item.
  • And more...

Usage

You have two options for using COptionTree, you can use it in static form or as a DLL.

Add The following files to your project:

// DLL
/COptionTreeDemo/COptionTree DLL/DLL.h

// Static
/COptionTreeDemo/COptionTree DLL/*.*
/COptionTreeDemo/COptionTree DLL/res/*.*

Creating the control:

    #include "OptionTree.h"
    
    // Declare variables
    CRect rcClient;
    DWORD dwStyle, dwOptions;
    
    // Get the clients rectangle
    GetClientRect(rcClient);
    
    // Setup the window style
    dwStyle = WS_CHILD | WS_VISIBLE;

    // Setup the tree options 
    dwOptions =  OT_OPTIONS_SHADEEXPANDCOLUMN | OT_OPTIONS_SHADEROOTITEMS;

    // Create tree options
    if (m_otTree.Create(dwStyle, rcClient, this, dwOptions, 
        IDC_OPTIONSTREE_OPTIONS) == FALSE)
    {
        TRACE0("Failed to create options control.\r\n");
        return FALSE;
    }

    // Insert tree items
    // ...
Available Tree Options
OT_OPTIONS_SHOWINFOWINDOW        // Show information window
OT_OPTIONS_NOTIFY            // Send parent notifications        
OT_OPTIONS_DEFINFOTEXTNOSEL        // Show default info text for no 
                                     // selected item, otherwise blank
OT_OPTIONS_SHADEEXPANDCOLUMN    // Shade the expand column
OT_OPTIONS_SHADEROOTITEMS        // Shade the root items
Validating Tree Items
// Use COptionTreeItem::GetItemType() to get item type. Different item 
// definitions include.

OT_ITEM_STATIC
OT_ITEM_CHECKBOX
OT_ITEM_COLOR
OT_ITEM_COMBOBOX
OT_ITEM_DATE
OT_ITEM_EDIT
OT_ITEM_IMAGE
OT_ITEM_RADIO
OT_ITEM_SPINNER
OT_ITEM_FONT
OT_ITEM_FILE
OT_ITEM_IPADDRESS

API Reference

This is a list of the most common used functions of COptionTree's classes. There many more functions than this, but these give the average user enough to use the control.

COptionTree
BOOL COptionTree::Create(DWORD dwStyle, RECT rcRect, CWnd* pParentWnd, 
                         DWORD dwTreeOptions, UINT nID)
//Create the tree control and window.

void COptionTree::DeleteAllItems()
//Delete all items in the tree.

void COptionTree::DeleteItem(COptionTreeItem *otiItem)
//Delete an item from the tree.

void COptionTree::DisableInput(BOOL bDisable)
//Disable user input, use this instead of EnableWindow

COptionTreeItem * COptionTree::InsertItem(COptionTreeItem *otiItem, 
                                          COptionTreeItem *otiParent)
//Insert a new item into the tree. If otiParent is 
//NULL then the item becomes the root. InsertItem returns a pointer
//to the item.

COptionTreeItem
COptionTreeItem is a class used to handle items in a tree. Each options/preference is a different item. Depending on what type of item control is used, these functions are acquired by all control types.
void COptionTreeItem::SetLabelText(CString strLabel)
//Set the label text of an item.

CString COptionTreeItem::GetLabelText()
//Get the label text.

void COptionTreeItem::SetInfoText(CString strText)
//Set the information text that is displayed in the information window. 
//This can give the user a description of the item/option, and what it does.

CString COptionTreeItem::GetInfoText()
//Get the information text that is displayed in the information window. 

void COptionTreeItem::ReadOnly(BOOL bReadOnly)
//Set item to read only state.

BOOL COptionTreeItem::IsReadOnly()
//Returns TRUE if the item is read only.

void COptionTreeItem::OnSelect()
//Select the item.

BOOL COptionTreeItem::IsReadOnly()
//Returns TRUE if the item is selected.

COptionTreeItemStatic
COptionTreeItemStatic is a simple static control. The following are custom functions used to set the edit the static text.
void COptionTreeItemStatic::SetStaticText(CString strStaticText)
//Set the text for the static item.

CString COptionTreeItemStatic::GetStaticText()
//Get the text for the static item.

COptionTreeItemEdit
COptionTreeItemEdit handles like a regular edit control. You can also use all of the standard functions that you would normally use with a CEdit control.
BOOL COptionTreeItemEdit::CreateEditItem(BOOL bMultiline, BOOL bPassword, 
                                         BOOL bNumerical, DWORD dwAddStyle)
//Creates the edit window, returns TRUE if the window is created successful.

BOOL COptionTreeItemEdit::GetMultiline()
//Returns TRUE if edit is multiline.

BOOL COptionTreeItemEdit::GetNumerical()
//Returns TRUE if edit is numerical.

BOOL COptionTreeItemEdit::GetPassword()
//Returns TRUE if edit is password.

void COptionTreeItemEdit::SetMultiline(BOOL bMultiline)
//Set the edit multiline option.

void COptionTreeItemEdit::SetEditDouble(double dValue)
void COptionTreeItemEdit::SetEditDword(DWORD dwValue)
void COptionTreeItemEdit::SetEditFloat(float fValue)
void COptionTreeItemEdit::SetEditInt(int nValue)
void COptionTreeItemEdit::SetEditLong(long lValue)
//Set the edit text as a numerical value.

BOOL COptionTreeItemEdit::GetEditDouble(double &dReturn)
BOOL COptionTreeItemEdit::GetEditDword(DWORD &dwReturn)
BOOL COptionTreeItemEdit::GetEditFloat(float &fReturn)
BOOL COptionTreeItemEdit::GetEditInt(int &nReturn)
BOOL COptionTreeItemEdit::GetEditLong(long &lReturn)
//Get the edit text as a numerical value. If function returns FALSE the 
//text is not numerical.

COptionTreeItemComboBox
COptionTreeItemComboBox handles like a regular combo box control. You can also use all of the standard functions that you would normally use with a CComboBox control.
BOOL COptionTreeItemComboBox::CreateComboItem(DWORD dwAddStyle)
//Creates the edit window, returns TRUE if the window is created successful.

void COptionTreeItemComboBox::SetDropDownHeight(long lHeight)
//Set the height for the drop down window.

COptionTreeItemCheckBox
COptionTreeItemCheckBox is a check box control.
BOOL COptionTreeItemCheckBox::CreateCheckBoxItem(BOOL bChecked, BOOL bShowCheck, 
                                                 BOOL bShowText)
//Creates the check box window, returns TRUE if the window is created successful.

BOOL COptionTreeCheckButton::GetCheck()
//Returns TRUE if check box is checked.

void COptionTreeCheckButton::SetCheckText(CString strChecked, CString strUnChecked)
//Sets the checked text to be displayed when checked and unchecked.

COptionTreeItemRadio
COptionTreeItemRadio is a radio box control.
BOOL COptionTreeItemRadio::CreateRadioItem()
//Creates the radio box window. 

void COptionTreeItemRadio::InsertNewRadio(CString strText, BOOL bChecked)
//Insert a new radio item, this should be done in the order you wish the radio 
//items to be in.

int COptionTreeItemRadio::GetCheckedRadio()
//Gets the checked radio item in a 0 index form. Function returns -1 if error.

COptionTreeItemSpinner
COptionTreeItemSpinner is a spinner control that allows users to select numerical values..
BOOL COptionTreeItemSpinner::CreateSpinnerItem(BOOL bWrapAround, BOOL bUserEdit,
                          double dValue, double dRangeBottom, double dRangeTop)
//Creates the spinner window, returns TRUE if the window is created successful.

void COptionTreeSpinnerButton::SetEditDouble(double dValue)
void COptionTreeSpinnerButton::SetEditDword(DWORD dwValue)
void COptionTreeSpinnerButton::SetEditFloat(float fValue)
void COptionTreeSpinnerButton::SetEditInt(int nValue)
void COptionTreeSpinnerButton::SetEditLong(long lValue)
//Set the spinner value as a numerical value.

BOOL COptionTreeSpinnerButton::GetEditDouble(double &dReturn)
BOOL COptionTreeSpinnerButton::GetEditDword(DWORD &dwReturn)
BOOL COptionTreeSpinnerButton::GetEditFloat(float &fReturn)
BOOL COptionTreeSpinnerButton::GetEditInt(int &nReturn)
BOOL COptionTreeSpinnerButton::GetEditLong(long &lReturn)
//Get the spinner value as a numerical value. If function returns FALSE an error 
//occured.

void COptionTreeSpinnerButton::GetRange(double &dBottom, double &dTop)
//Gets the range for the spinner.

void COptionTreeSpinnerButton::SetRange(double dBottom, double dTop)
//Sets the range for the spinner.

COptionTreeItemColor
COptionTreeItemColor is a control to allow the user to select or set a custom color.
BOOL COptionTreeItemColor::CreateColorItem(COLORREF rcColor, 
                                           COLORREF rcAutomatic, BOOL bShowHex, 
                                           BOOL bLiveUpdate)
//Creates the color window, returns TRUE if the window is created successful.

COLORREF COptionTreeItemColor::GetColor()
//Gets the selected color.

void COptionTreeItemColor::SetColor(COLORREF rcColor)
//Sets the selected color.

void COptionTreeItemColor::SetShowHex(BOOL bShow)
//Sets the option to show hexadecimal or RGB values.

void COptionTreeItemColor::SetLiveUpdate(BOOL bLive)
//Sets the option to show live update colors.

COptionTreeItemDate
COptionTreeItemDate is a control to allow the user to select or set a date or time. You can also use all of the standard functions that you would normally use with a CDateTimeCtrl control.
BOOL COptionTreeItemDate::CreateDateItem(CString strFormat, DWORD dwDateStyle)
//Creates the date window, returns TRUE if the window is created successful.

COptionTreeItemImage
COptionTreeItemIcon is a control to allow the user to select an icon.
BOOL COptionTreeItemImage::CreateImageItem(DWORD dwOptions, CSize sImageSizes, 
                                           int nNumberColumns)
//Creates the icon window, returns TRUE if the window is created successful.

int COptionTreeItemImage::GetSelection()
//Gets the selected image in a 0 based index.

void COptionTreeItemImage::AddBitmap(CString strBitmap, COLORREF crMask, CString strText)
void COptionTreeItemImage::AddBitmap(UINT uBitmap, COLORREF crMask, CString strText)
void COptionTreeItemImage::AddBitmap(CBitmap &bBitmap, COLORREF crMask, CString strText)
void COptionTreeItemImage::AddIcon(UINT uIcon, CString strText)
void COptionTreeItemImage::AddIcon(HICON hIcon, CString strText)
//Insert a new image.
COptionTreeItemFont
COptionTreeItemFont is a control to allow the user to select an font.
BOOL COptionTreeItemFont::CreateFontItem(LOGFONT lfFont, COLORREF crFontColor, 
             LOGFONT lfDefaultFont, COLORREF crDefaultFontColor, DWORD dwOptions)
BOOL COptionTreeItemFont::CreateFontItem(LOGFONT lfFont, COLORREF crFontColor, 
             DWORD dwOptions)
BOOL COptionTreeItemFont::CreateFontItem(CHARFORMAT cfFont, COLORREF crFontColor, 
             CHARFORMAT cfDefaultFont, COLORREF crDefaultFontColor, DWORD dwOptions)
BOOL COptionTreeItemFont::CreateFontItem(CHARFORMAT cfFont, COLORREF crFontColor, 
             DWORD dwOptions)
//Creates the font window, returns TRUE if the window is created successful.

void COptionTreeItemIcon::SetCurFont(CHARFORMAT cfFont)
void COptionTreeItemIcon::SetCurFont(LOGFONT lf)
//Sets the font currently displayed.

void COptionTreeItemIcon::SetDefaultFont(CHARFORMAT cfFont)
void COptionTreeItemIcon::SetDefaultFont(LOGFONT lf)
//Sets the default font, that will be set when the default button is pushed.

void COptionTreeItemIcon::SetTextColor(COLORREF crColor)
//Sets the text color.

void COptionTreeItemIcon::SetDefaultTextColor(COLORREF crColor)
//Sets the text color for the default font.

void COptionTreeItemIcon::SetApplyWindow(CWnd *pWnd)
//Sets the window to be notified when the apply button is pressed.
COptionTreeItemFile
COptionTreeItemFile is a control to allow the user to select a file or folder.
//Creates the file window, returns TRUE if the window is created successful.
BOOL COptionTreeItemFile::CreateFileItem(CString strFile, CString strDefExt, 
                                         CString strFilter, DWORD dwOptions, 
                                         DWORD dwDlgFlags)

// gets information about the selected file(s).
CString COptionTreeItemFile::GetSelectedFolder()
CString COptionTreeItemFile::GetFileDrive()
CString COptionTreeItemFile::GetFileDir()
CString COptionTreeItemFile::GetFileExt()
CString COptionTreeItemFile::GetFileTitle()
CString COptionTreeItemFile::GetFileName()
CString COptionTreeItemFile::GetPathName()
CString COptionTreeItemFile::GetNextPathName(POSITION& pos)
POSITION GetStartPosition()
COptionTreeItemIPAddress
COptionTreeItemIPAddress is a control to allow the user to select a IP Address.
//Creates the ip address window, returns TRUE if the window is created successful.
BOOL COptionTreeItemIPAddress::CreateIPAddressItem(DWORD dwAddStyle)
COptionTreeItemHyperLink
COptionTreeItemHyperLink is a control to allow the user to click on a hyperlink.
// Creates the hyperlink window, returns TRUE if the window is created successful.
BOOL COptionTreeItemIPAddress::CreateHyperlinkItem(DWORD dwOptions, 
                   CString strLink, COLORREF crLink, COLORREF crHover = NULL, 
                   COLORREF crVisited = NULL)

Notifications

You can have your application be notified of certain events that happen in the tree control. Below is an example of how to be notified when an event happens, and all the notifications you can have.

YourDialog.h
//...
    //}}AFX_MSG_MAP
    ON_NOTIFY(OT_NOTIFY_ITEMCHANGED, IDC_OPTIONSTREE_OPTIONS, OnTreeItemChanged)
END_MESSAGE_MAP()
YourDialog.cpp

void CYourDialog::OnTreeItemChanged(NMHDR* pNotifyStruct, LRESULT* plResult)
{
    // Declare variables
    LPNMOPTIONTREE pNMOptionTree = (LPNMOPTIONTREE)pNotifyStruct;

    // Validate
    if (pNMOptionTree->pItem != NULL)
    {
        // -- Use item
        return 1;
    }

    *plResult = 0;
}

Available Notifications
OT_NOTIFY_INSERTITEM        // Insert item
OT_NOTIFY_DELETEITEM        // Delete item
OT_NOTIFY_DELETEALLITEMS    // Delete all items
OT_NOTIFY_ITEMCHANGED       // Item changed
OT_NOTIFY_ITEMBUTTONCLICK   // Item button click
OT_NOTIFY_SELCHANGE         // Selection changed
OT_NOTIFY_ITEMEXPANDING     // Item expanding
OT_NOTIFY_COLUMNCLICK       // Column click
OT_NOTIFY_PROPCLICK         // Property click

Creating Custom OptionTreeItem's

Creating custom tree controls is a difficult and drawn out process, but I do not want this to deter you from taking on the task. If you are an beginner program and have ideas for new controls, please let me know and I will see what I can do. If you are and advanced programmer and have created customized controls, please let me also know and I will include them in the next release. Some overrides you made need to know for OptionTreeItem are below, you can look at some of the code for the controls I have created to get an idea of how they are managed. Each control type, requires a different attack angle to implement it. A good place to start is to take a look at OptionTreeItemStatic, this is a simple control. OptionTreeItemEdit is a good place to go after you look at the static item.

void OptionTreeItem::DrawAttribute(CDC *pDC, const RECT &rcRect)
//Called when item needs to be painted.

void OptionTreeItem::OnActivate()
//Called when the item is clicked by mouse or enter key is pressed. This may 
//be where your control displays a window, or a menu.

void OptionTreeItem::OnCommit()
//Called when data has been committed, a confirmation that options have been
//changed. CommitChanges() is then called to handle the selection of data.

void OptionTreeItem::OnMove()
//Called when the item is moved.

void OptionTreeItem::OnMove()
//Called when item is refreshed.

void OptionTreeItem::OnSelect()
//Called when item is selected.

void OptionTreeItem::OnDeSelect()
//Called when item is deselected.

void OptionTreeItem::CleanDestroyWindow()
//Called when the Tree control is destroyed or when the item is deleted. This is 
//incase your item needs to destroy a window.

Acknowledgements

The COptionTree is inspired by code, ideas, and submissions from the following:

Version History

Below is the version history for COptionTree.

5/7/2002

1.0.0.0 Initial implementation.

5/10/2002 1.1.0.0 Fix problems with icon and color items. Also fixed problems with expanding columns and column size. Added TAB support, hit TAB to activate next item, and SHIFT TAB to activate previous item. NOTE: You must commit current item with the ENTER key before tabbing, this is because some controls use TAB i.e.) edit, control box, radio, and so on.

5/11/2002 1.1.1.0 Fixed IsWindow() issues, which are the causes for a lot of Win98 problems. Fixed IDC_HAND problem for non Win 2K or XP users. Fixed some resource and overhead problems. Also added expand all feature. Huge thanks go out to all bug reporters, especially YoSilver.

5/14/2002 1.1.2.0 Fixed icon and color bug. Also added GetItemType() to determine different item types.

9/1/2002 1.1.2.1 Beta release, beta is not stable by any means.

9/8/2002 2.0.0.0 The long awaited 2.0 version is here. Fixed a lot of bugs, created more bugs, added several more items, and a lot of other stuff that is way too much to list here. Oh plus I added fancy new version numbers, this I think was the biggest update.

Bug Reports

If you have a bug report, or a bug fix you can contact me through email or with the forums below. I would appreciate a notification of any bugs discovered or improvements that could be made to help the control grow for everyone.

License

This code is provided "as is" with no expressed or implied warranty.

You may use this code in a commercial product with or without acknowledgement. However you may not sell this code or any modification of this code, this includes commercial libraries and anything else for profit.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Matthew R. Miller
Web Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralWTL Version Pinmemberlsmart5-Mar-11 21:26 
Questionone little bug? Pinmemberxiang_yan12-Oct-10 19:22 
QuestionHow to use the dll Pinmemberxiang_yan8-Oct-10 22:35 
QuestionHow to create a Icon AND Radio/check box in same tree item ? PinmemberRunningThread27-Jul-10 2:52 
GeneralAFX_IDB_TRUETYPE is not defined in VS2010 Pinmemberbakatans24-Jul-10 19:05 
GeneralRe: AFX_IDB_TRUETYPE is not defined in VS2010 Pinmemberxurendong8-Sep-10 1:53 
I define it:
#define AFX_IDB_TRUETYPE 32384
hehe
GeneralBug on static LPCTSTR _MakeShortString(CDC* pDC, LPCTSTR lpszLong, int nWidth, int nOffset) PinmemberMember 117549129-Mar-10 5:15 
GeneralRe: Bug on static LPCTSTR _MakeShortString(CDC* pDC, LPCTSTR lpszLong, int nWidth, int nOffset) PinmemberMember 84588559-May-12 9:54 
GeneralExpand and ExpandAllItems [modified] Pinmemberbishopnator29a12-Jul-09 9:08 
Generalfound bug in different components of COptionTree PinmemberMember 155270111-Jul-09 22:09 
GeneralRe: found bug in different components of COptionTree Pinmembersaintigny10-Aug-10 22:41 
QuestionHow can I create checkBox at root level. Pinmemberiacxin24-Apr-09 5:36 
GeneralWeird thing when using on a tab page; [modified] Pinmemberdchris_med9-Jan-09 2:41 
QuestionSplitter cursor? Pinmembertermal28-Oct-08 2:08 
GeneralVisual Studio 2005 Pinmembervahid_m_200824-Aug-08 8:34 
GeneralSomething like that in C# Pinmemberantoschka22-Apr-08 6:19 
QuestionNo notification on checkbox changes? PinmemberPsychoChris20-Apr-08 17:00 
GeneralProposal: some fixes for Unicode and maybe an alternative to CommonRes.h PinmemberRedFraggle17-Feb-08 1:13 
GeneralRe: Proposal: some fixes for Unicode and maybe an alternative to CommonRes.h Pinmemberwheregone13-Mar-11 22:12 
Questiondoesn't work in release mode Pinmemberllllskywalker16-Jan-08 7:24 
Generalbug: COptionTreeItemHyperLink as child of collapsed item [modified] Pinmembercadripper12-Jan-08 10:48 
Generalrelease vs debug mode Pinmemberllllskywalker5-Oct-07 6:40 
GeneralCOptionTreeItemSpinner - Range Check PinmemberZumy Zakkir27-Jun-07 0:25 
QuestionCompilation errors of the downloaded code? PinmemberAlexander Mintz14-Jun-07 11:36 
AnswerSee "Enhancements of Matthew R. Millers COptionTree" for VC++ 7.1 PinmemberdomKing22-Jun-07 9:55 
GeneralLink to forum post (corrected due to link drift) PinmemberdomKing6-Feb-09 16:05 
GeneralDeleteGlobalResources Pinmemberandrewtruckle12-Jun-07 2:54 
GeneralResizing column cursor Pinmemberandrewtruckle7-Jun-07 20:22 
QuestionEditable combo? Pinmemberandrewtruckle6-Jun-07 9:45 
QuestionDisabling sub items Pinmemberandrewtruckle4-Jun-07 23:32 
Questionslider Pinmembermoerc14-May-07 2:26 
Questiondynamic add static item Pinmemberseas1199-May-07 16:35 
Answersupplement Pinmemberseas1199-May-07 16:41 
Generalbig data [modified] Pinmembermatrosso20-Mar-07 5:53 
NewsHeap Problem under VS2003/VS2005 - and how to fix it. Pinmemberknaster bax28-Jan-07 13:45 
GeneralUpdate option tree items programmatically PinmemberGolden Lee28-Jan-07 3:42 
GeneralRe: Update option tree items programmatically Pinmembersamppa197120-Feb-07 9:21 
QuestionChanging StaticItem Label width? Pinmembersamppa197115-Jan-07 12:39 
AnswerRe: Changing StaticItem Label width? Pinmemberseas1199-May-07 16:58 
AnswerRe: Changing StaticItem Label width? Pinmemberdunniu19-Nov-13 14:04 
QuestionA question about CComboBox Item Pinmemberpinger198117-Dec-06 15:39 
QuestionSave File Dialog? PinmemberFranken8-Dec-06 14:00 
GeneralPlease help me! [SOS] Pinmemberpinger19817-Dec-06 23:16 
GeneralRe: Please help me! [SOS] PinmemberFranken8-Dec-06 13:38 
GeneralSimply great! Pinmembercpallini23-Nov-06 11:40 
GeneralOption model, accessing option items the same way [modified] Pinmemberalaincrouzet26-Oct-06 3:44 
GeneralVery thanks for this great job! [modified] Pinmemberalaincrouzet25-Oct-06 21:45 
GeneralHyperLink Pinmemberkiranin18-Oct-06 19:51 
GeneralBinding data pointers to tree items Pinmemberiberg7-Sep-06 22:41 
GeneralDynamic Adding of Items PinmemberTydia-kun7-Sep-06 2:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 20 Sep 2002
Article Copyright 2002 by Matthew R. Miller
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid