CMainFrame
class
(usually found in MainFrm.h
) to include a member variable:
public:
CSplitterWnd m_SplitterWindow;
Name: CMyListView
Base Class: CListView
Name: CMyTreeView
Base Class: CTreeView
Class name:
combobox.
Messages:
listbox.
Add Function
button.
Member functions:
listbox.
return CFrameWnd::OnCreateClient(lpcs, pContext);
with the following code:
m_SplitterWindow.CreateStatic( this, 1, 2, WS_CHILD | WS_VISIBLE );
CWnd * desktop_window_p = GetDesktopWindow();
CRect rectangle;
if ( desktop_window_p == NULL )
{
rectangle.SetRect( 0, 0, 640, 480 );
}
else
{
desktop_window_p->GetWindowRect( rectangle );
}
SIZE size;
// Make the tree control pane 25% of the window size
size.cx = (long) ( rectangle.Width() * 0.25 );
size.cy = rectangle.Height();
m_SplitterWindow.CreateView( 0, 0,
RUNTIME_CLASS( CMyTreeView ),
size,
pContext );
// Make the list control 75% of the window size
size.cx = rectangle.Width() - size.cx;
m_SplitterWindow.CreateView( 0, 1,
RUNTIME_CLASS( CMyListView ),
size,
pContext );
SetActiveView( (CView *) m_SplitterWindow.GetPane( 0, 0 ) );
return( TRUE );
CMainFrame.cpp
file (or
whatever filename has CMainFrame in it) and find the line that
looks like: #include "CMainFrame.h"
Now add these lines immediately after it:
#include "MyTreeView.h"
#include "MyListView.h"
stdafx.h
file and delete the following lines:
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#ifndef _AFX_NO_AFXCMN_SUPPORT
#endif // _AFX_NO_AFXCMN_SUPPORT
Now add this line to the rest of the includes:
#include <afxcview.h>
public:
CMyTreeView * TreeView;
CMyListView * ListView;
#include "MyTreeView.h"
#include "MyListView.h"
Documents and views are very incestuous.
Each has to know way too much about the other in order to function.
This means the document class is a great place to put information
about all of the views of that document. As long as your document
doesn't represent data, everything is fine. Just think of your document
class as the guy that manages all of the views (a View Manager
if you will).
You can have pointers
to data in the document class but I do not recommend putting any real
data in there.
public:
CImageList ImageList;
HTREEITEM RootItem;
HTREEITEM CurrentItem;
IDB_IMAGELIST
.
Make your grid settings 17 pixels high by 16 wide (via the Image->Grid Settings...)
Use the darker purple on the color palette bar for your transparent color (the background
color).
16
17
1
in the first tile.
2
in the second tile.
3
in the third tile. You will overwrite these ugly
numbers with pretty little drawings later on in the life of your project.
This usually happens when a graphic artist gets hired by your company.
IDB_BITMAP1
in the ResourceView pane
to highlight it.
IDB_IMAGELIST
then close the window
CMyTreeView
in the Class name: combobox.
CMyTreeView
in the Object IDs: listbox.
WM_CREATE
in the Messages: listbox.
if (CTreeView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
return 0;
with this code:
lpCreateStruct->style |= ( TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT );
if ( CTreeView::OnCreate( lpCreateStruct ) == (-1) )
{
return( -1 );
}
MyDocument * document_p = GetDocument();
ASSERT( document_p != NULL );
document_p->TreeView = this;
ImageList.Create( IDB_IMAGELIST, 16, 0, RGB( 255, 0, 255 ) );
ImageList.SetBkColor( GetSysColor( COLOR_WINDOW ) );
CTreeCtrl& tree_control = GetTreeCtrl();
tree_control.SetImageList( &ImageList, TVSIL_NORMAL );
UpdateWindow();
RootItem = tree_control.InsertItem( "Root", IMAGE_ROOT, IMAGE_ROOT );
return( 0 );
#include "MyDocument.h"
MyTreeView.h
header file to start adding defined
constants for the images in the tree control (the tiles in the image list).
#define IMAGE_ROOT 0
#define IMAGE_TILE_2 1
#define IMAGE_TILE_3 2
CMyListView
in the Class name: combobox.
CMyListView
in the Object IDs: listbox.
WM_CREATE
in the Messages: listbox.
if (CListView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
return 0;
with this code:
lpCreateStruct->style |= LVS_REPORT;
if ( CListView::OnCreate( lpCreateStruct ) == (-1) )
{
return( -1 );
}
CMyDocument * document_p = (CMyDocument *) GetDocument();
document_p->ListView = this;
return( 0 );
#include "MyDocument.h"
public:
void DeleteChildren( HTREEITEM parent_item );
Implemented thusly:
void CMyTreeView::DeleteChildren( HTREEITEM parent_item )
{
CTreeCtrl& tree_control = GetTreeCtrl();
HTREEITEM child_item = (HTREEITEM) NULL;
child_item = tree_control.GetChildItem( parent_item );
while( child_item != NULL )
{
tree_control.DeleteItem( child_item );
child_item = tree_control.GetChildItem( parent_item );
}
}
public:
BOOL AddColumn( LPCTSTR column_name,
int column_number,
int width = (-1),
int nSubItem = (-1),
int mask = LVCF_FMT | LVCF_WIDTH |
LVCF_TEXT | LVCF_SUBITEM,
int format = LVCFMT_LEFT );
BOOL AddItem( int row_number,
int column_number,
LPCTSTR text,
int image_index = (-1) );
void Empty( void );
Implemented thusly:
BOOL CMyListView::AddColumn( LPCTSTR column_name,
int column_number,
int width,
int sub_item,
int mask,
int format )
{
CListCtrl& list_control = GetListCtrl();
LV_COLUMN list_view_column;
list_view_column.mask = mask;
list_view_column.fmt = format;
list_view_column.pszText = (LPTSTR) column_name;
if ( width == (-1) )
{
list_view_column.cx = list_control.GetStringWidth( column_name ) + 15;
}
else
{
list_view_column.cx = width;
}
if ( mask & LVCF_SUBITEM )
{
if ( sub_item != (-1) )
{
list_view_column.iSubItem = sub_item;
}
else
{
list_view_column.iSubItem = column_number;
}
}
return( list_control.InsertColumn( column_number, &list_view_column ) );
}
BOOL CMyListView::AddItem( int row_number,
int column_number,
LPCTSTR text,
int image_index )
{
LV_ITEM list_view_item;
list_view_item.mask = LVIF_TEXT;
list_view_item.iItem = row_number;
list_view_item.iSubItem = column_number;
list_view_item.pszText = (LPTSTR) text;
if ( image_index != (-1) )
{
list_view_item.mask |= LVIF_IMAGE;
list_view_item.iImage = image_index;
}
CListCtrl& list_control = GetListCtrl();
if ( column_number == 0 )
{
return( list_control.InsertItem( &list_view_item ) );
}
else
{
return( list_control.SetItem( &list_view_item ) );
}
}
void CMyListView::Empty( void )
{
CListCtrl& list_control = GetListCtrl();
list_control.DeleteAllItems();
while( list_control.DeleteColumn( 0 ) )
{
;
}
UpdateWindow();
}
Generally speaking, when the user selects branches in the tree view, you want the list view (I prefer report view) to show details of that item. This means you should add a function to the list view class to show the details of each type of item. We also need a way to associate tree items with objects that will be displayed in these functions. In general, the document class is responsible for loading the object (and sub objects). The tree view is then passed this object and populates the tree items. When the user selects a tree item, the tree view figures out (via mapping) what the object selected was and tells the list view to display it.
CDocument
as
a view manager and data router. It gets told by the framework to
load new data (via the OpenDocument
method).
I suggest putting a pointer to your real data-centric object in
the document class.
Here's the steps you need to take to add your own code to load a file:
CMyDocument
in the Class name: combobox.
CMyDocument
in the Object IDs: listbox.
OnOpenDocument
in the Messages: listbox.
To map what the user click on to what it represents, do the following:
MyTreeView.h
header file (this is the
one that contains the image identifiers IMAGE_xxx
defines).
CMyTreeView
class. These
enumerations will represent the objects.
class CMyTreeView : public CTreeView
{
public:
enum _what_it_is
{
IT_IS_THE_ROOT = 1,
IT_IS_AN_XML_DOCUMENT,
IT_IS_AN_ELEMENT,
IT_IS_A_LIST_OF_ATTRIBUTES,
IT_IS_AN_ATTRIBUTE
};
CMyTreeView
to convert
image id's to what they represent:
DWORD ImageToWhatItIs( DWORD image_id ) const;
implemented thusly:
DWORD CMyTreeView::ImageToWhatItIs( DWORD image_id ) const
{
switch( image_id )
{
case IMAGE_ROOT:
return( IT_IS_THE_ROOT );
case IMAGE_TILE_2:
return( IT_IS_AN_XML_DOCUMENT );
case IMAGE_TILE_3:
return( IT_IS_AN_ELEMENT );
default:
return( 0 );
}
}
OnOpenDocument()
) and other methods
to add individual items. For example:
void CMyTreeView::Populate( CExtensibleMarkupLanguageDocument * xml_p )
{
// Start with an empty tree
DeleteChildren( RootItem );
if ( xml_p == NULL )
{
return;
}
CTreeCtrl& tree_control = GetTreeCtrl();
HTREEITEM document_item;
document_item = tree_control.InsertItem( TEXT( "XML" ), IMAGE_TILE_2, IMAGE_TILE_2, RootItem );
tree_control.SetItemData( document_item, (DWORD) xml_p );
CExtensibleMarkupLanguageElement * element_p = xml_p->GetRootElement();
AddElement( tree_control, document_item, element_p );
}
To make things easy (and keep them straight in my little head), I put
a comment block at the top of the file to give me a nice map
to what everything means.
/*
** Image Identifier to What It Is to Item Data mappings
**
** IMAGE ID | WHAT IT IS | GetItemData() returns
** -------------+-----------------------+------------------------------------
** IMAGE_ROOT | IT_IS_THE_ROOT |
** IMAGE_TILE_2 | IT_IS_AN_XML_DOCUMENT | CExtensibleMarkupLanguageDocument *
** IMAGE_TILE_3 | IT_IS_AN_ELEMENT | CExtensibleMarkupLanguageElement *
*/
Here's where you will explain how to handle change notifications.