Multistring List Control






3.86/5 (6 votes)
Oct 24, 2002
3 min read

135083

1426
A list control, that supports multistring text items, drag & drop and item's editing
Actuality
The standard CListCtrl
is very good instrument for creating lists. It can be present as icon's set, simple list or report, can have many columns. But it can't have text items with many strings per one, i.e. items with different heights; the maximum lenghth of CListCtrl
's items is 512 characters (I haven't intend virtual lists). So I decide to create a class, that have over-passed this problem - the CLPPageCtrl
. In first, I planed to inherit it from CListCtrl
, but it has one unpleasant characteristic: monoheights of items, at that there is no way to set this height as you want (the height depends on selected font of the control). At any case I didn't find it. In addition I wanted to reserve possibility to add non only-text items. Pictures, tables and so on. And I wanted to realize Drag & Drop and editing for items. Of course, the CHTMLView
is good decision for it. In this case there is need to write on DHTML, to write scripts and may be to generate HTML pages dynamically. Finally I have wrote CLPageCtrl
as child of CWnd
.
Properties of control
CLPPageCtrl
has these properies:
- Multistring text items
- One views - list (not report, icon view)
- All items of one type have one style
- Text(
CLPPara
) items can have mark on the left - radios, one for all (to select) - Drag&drop for items
- Editing for text items with saving user's new lines ('\v' instead of '\n')
- Text items have aligning: left-, right-, center- and wide-justify
- Items have const width - screen width
- The control can be scrolled
Inner struct
CLPPageCtrl
is a child of CWnd
and CLPPage
. CLPPage
carry out most work (it suppose possibility to create CLPPageView
). As long as items can be various, CLPPage
doesn't know many about items. It just know, that they have a placement rectangle and may have mark. Besides CLPPage
is a manager, that store statically parameters for items - text color, offset from border, distance between items, font. And items have rectangle, there they do everything, that they want, and state field to store own conditions. They know about CLPPage
well. CLPPage
communicates with CLPField
s using LPFMSG
. This is analogue of MSG struct of Microsoft Platform SDK. I tried to make items like as child window of main - CLPPage
. In the end, most functions in CLPPage
call CLPPage::OnWmMessage()
, that call CLPField::WmMessage()
for one's part.
Futures of using
Using of CLPPageCtrl
is very simple. You can define a variable of CLPPageCtrl
type or inherit new class from it. Then setup styles by function SetStyles
and structure LPFSTLE
with various flags that are described in lpage.h. After creation of window (standard CWnd::Create(...)
), add one ore more items as initializing by AddItem
with pointer to CLPField
. You need not be afraid of memory leak - AddItem()
will copy item and destroy at end correctly. After this you will have a list with items, that can be drag & dropped and edited. One AddItem
method has a point on input. It's suit to add an item by mouse's click. This way it allow you to insert an item between other items. To get text from a text item, call GetItem()
, the text will be placed in CLPField::text
. Don't forget about '\v'. This char appears when user press <Enter> key in edit. Selected ("marked") item is determined by GetMarkedItem()
, edited by GetFocusedItem()
. All of them returns CLPField
pointer. You can translate it to index using GetItemIndex()
. Also you can change text's font and color and background color by calling SetFont()
(this method is absent in CLPPage
interface, because I intercept WM_SETFONT
window message), SetTextColor()
and SetBkColor()
.
Sample code
To present my work I created an SDI project with MFC-exe wizard. Here I don't use document/view paradigm. The CChildView
class is inherited from CLPPageCtrl
. The ChildView.cpp is to be of interest of analysis. Here I cite its fragment:
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CLPPageCtrl::OnCreate(lpCreateStruct) == -1) return -1; // List's initialization CString str; for (int i=0; i<15; i++){ str.Format("Multistring\vitem\v%i", i+1); // item with many strings CLPPara para(this, str); AddItem(-1, ¶, 0); // add to end } return 0; } // change color of text void CChildView::OnViewColors() { CColorDialog dlg(text_color, CC_ANYCOLOR | CC_FULLOPEN | CC_PREVENTFULLOPEN | CC_ENABLEHOOK, this); if (dlg.DoModal()==IDOK){ SetTextColor(dlg.m_cc.rgbResult); InvalidateRect(NULL, 0); } } // change text's font void CChildView::OnSetfont() { CFontDialog dlg(NULL, CF_EFFECTS | CF_SCREENFONTS | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_NOOEMFONTS, NULL, this); GetFont()->GetLogFont(&dlg.m_lf); if (dlg.DoModal()==IDOK){ CFont fnt; fnt.CreateFontIndirect(&dlg.m_lf); SetFont(&fnt); } } // display context menu with insert,(delete) items void CChildView::OnContextMenu(CWnd* pWnd, CPoint ptScreen) { CPoint ptClient = ptScreen; ScreenToClient(&ptClient); CMenu *pmnu = m_mnuContext.GetSubMenu(0); ASSERT(pmnu); if (GetItem(ptClient)) // if on item pmnu->EnableMenuItem(ID_CONTEXTMENU_DELETE, MF_ENABLED|MF_BYCOMMAND); else pmnu->EnableMenuItem(ID_CONTEXTMENU_DELETE, MF_GRAYED|MF_BYCOMMAND); pmnu->TrackPopupMenu(TPM_RIGHTBUTTON, ptScreen.x, ptScreen.y, this); } // delete item void CChildView::OnContextmenuDelete() { // GetHotItem() returns an item under mouse cursor DeleteItem(GetItemIndex(GetHotItem())); } // add item void CChildView::OnContextmenuAdd() { // don't forget translate point from screen coordinates CPoint pt; GetCursorPos(&pt), ScreenToClient(&pt); CLPPara para(this, ""); // place edit on new item SetFocusedItem(AddItem(pt, ¶)); }