|

Introduction
The History Combo class CHistoryCombo is based on CComboBox, and includes support for keeping item history, including reading from and saving to the registry. It can also be used to display the contents of an existing CRecentFileList object.
How to use it
Using the CHistoryCombo class is very straightforward. Follow the steps below to add one to an existing project:
- After putting the source files (HistoryCombo.cpp and HistoryCombo.h) into the directory you wish to use them from, add the files to your Visual Studio project.
- In the resource editor, add a combo where you wish.
- In Class Wizard, add a member variable for your combo control, selecting "Control" from the "Category" list, and selecting "
CHistoryCombo" from the "Variable Type" list. (If CHistoryCombo does not appear in the list, you may need to delete your class wizard file (.clw) and regenerate it). I will assume your control variable name is m_comboHistory.
- Add a handler for
WM_INITDIALOG in your dialog class if you don't already have one, and add the following code to it:
m_comboHistory.LoadHistory("Settings", "HistoryCombo");
(Note: you can specify any string you wish for the section, and key-prefix.)
- In your handler for the
IDOK button, or wherever you choose, add the following: m_comboHistory.SaveHistory();
That's all you need to do.
Serialization, and using a string for storage
For examples on how to use the CArchive support, and the loading and saving from and to a string, see the demo program.
Documentation
Each function is documented in the CPP file, but here is a list of functions in CHistoryCombo:
CHistoryCombo(BOOL bAllowSortStyle = FALSE);
CString LoadHistory(LPCTSTR lpszSection, LPCTSTR lpszKeyPrefix,
BOOL bSaveRestoreLastCurrent = TRUE, LPCTSTR lpszKeyCurItem = NULL);
CString LoadHistory(CRecentFileList* pListMRU, BOOL bSelectMostRecent = TRUE);
void SaveHistory(BOOL bAddCurrentItemtoHistory = TRUE);
virtual void Serialize(CArchive& ar);
void SaveHistory(CArchive& ar, BOOL bAddCurrentItemtoHistory = TRUE);
void LoadHistory(CArchive& ar);
CArchive& operator<<(CArchive& ar, CHistoryCombo& ob);
CArchive& operator>>(CArchive& ar, CHistoryCombo& ob);
CString SaveHistoryToText(CString& sHistory, BOOL bAddCurrentItemToHistory =
TRUE, LPCTSTR lpszDelims = _T("\r\n"));
void LoadHistoryFromText(LPCTSTR lpszHistory, LPCTSTR lpszLastSelected = NULL,
LPCTSTR lpszDelims = _T("\r\n"));
int AddString(LPCTSTR lpszString);
void SetMaxHistoryItems(int nMaxItems);
void ClearHistory(BOOL bDeleteRegistryEntries = TRUE);
void StoreValue(BOOL bIgnoreIfEmpty = TRUE);
void SetAutoComplete(BOOL bAutoComplete = TRUE);
BOOL GetAutoComplete();
static void SetAutoCompleteDefault(BOOL bAutoComplete = TRUE);
static BOOL GetAutoCompleteDefault()
History
Version 3.1 - 20 Apr 2007
Version 3.0 - 22 Jun 2004
- Added serialization to/from a
CArchive object (suggested by EPulse), which can be used by one of the three methods:
- Added loading/saving from/to a
CString object (suggested by Uwe Keim)
Version 2.1 - 09 Jul 2003
- Updated to support Unicode.
Version 2 - 01 May 2002
- Removed
CBS_SORT on creation, if specified.
- Added option to allow the sort style to be set, if required.
- Fixed
SetMaxHistoryItems, so it removes old entries from the list to ensure that there are no more than the maximum. Also, made SaveHistory remove redundant profile entries above the maximum.
- Uses
WriteProfileString to remove profile entries rather than CRegKey.
Version 1 - 12 Apr 2001
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 47 (Total in Forum: 47) (Refresh) | FirstPrevNext |
|
|
 |
|
|
 |
|
|
Nice one. To make it even better I've added in the autocompletion code from Chris Maunder's CComboCompletion class. 
The next task will be to save and load the list of entries via SQL...
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Thanks for the compliment. It's a bit late for you now, but I've just uploaded an update which includes Chris Maunder's auto-completion code, as well as making it compilable in VS2003/2005.
If you add SQL support, be sure to tell me! 
"The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
It's really a good combobox, but a little pity.
The author used CString to store all history, but If I want to delete what I have add to list, so it's hard to do.
Maybe a CArray or CList is pretty to store the history, it's easy to addList, deletelist!!
Benny fun
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
I think you've misunderstood the implementation and usage of my control.
I don't store the history in a CString. The history is simply the text stored in the combo's items.
In essence, apart from the extra functions to load and store the history, it behaves exactly like any other combo-box, so to add items you can use AddString, InsertString etc, and to delete items you can use the normal DeleteString function.
The class uses a new version of AddString to make sure that items are always stored in reverse order to that in which they are added. There is also a StoreHistory function which takes the current edit control contents and adds it to the list (as the top-most item).
In trying to allow flexibility, I have made it such that you can populate the combo from the user's profile, a CRecentFileList or an archive. You can save the history to any of these three also, or to a CString for you to store as you wish.
I agree that it would be useful to load/store from/to a CArray/CList, and I may consider adding that to a future update.
"The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,thanks for you kindly reply.
I want to implement the function such as: when I dropdown the list, and press DEL key, so it'll delete the highlighted item. Would you give me some advice?
Benny fun
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
hi ther!
is it possible to implement more than 1 history comboboxes in the same dialog? if yes, please show me how to do so? thank you very much for your time n effort! 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I tried the folowing code: [code] // CDialogBar m_wndSearchBar; // create m_wndSearchBar, pass to ReBar::AddBar, etc ... CHistoryCombo *m_pcomboSearchQuery = (CHistoryCombo *)m_wndSearchBar.GetDlgItem(IDC_COMBO_SEARCH_QUERY); m_pcomboSearchQuery->LoadHistory("Settings", "SearchQueryHistory"); [/code] The last string cause assertion in CString code. So I think I acually get CComboBox pointer not CHistoryCombo. Is there any way to get a CHistoryCombo pointer? I know that I can constuct CHistoryCombo directly and then pass to CReBar::AddBar() but is I think to build all toolbars from dialog resources is more good code style.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
If your dialog bar does not have the combo explicitly declared (and subclassed) as a CHistoryCombo then it will not be one. In fact GetDlgItem() will probably return it as a CTempWnd*. You can find out by adding a line of code like: TRACE("Combo class = %s\n", m_pcomboSearchQuery->GetRuntimeClass()->m_lpszClassName);To get it to be a CHistoryCombo you will at some point need to subclass it to one. You could either create your own class derived from CDialogBar and a member variable to it like: CHistoryCombo m_combo and then override the OnCreate in your class and do something like: m_combo.SubclassDlgItem(IDC_COMBO_SEARCH_QUERY, this); or, you could avoid having your own CDialogBar-derived class by handling it in the main frame, by adding the CHistoryCombo member to the CMainFrame class, and sometime after creating the dialog-bar do something like: m_combo.SubclassDlgItem(IDC_COMBO_SEARCH_QUERY, &m_wndSearchBar); Once you have done one of the above, you can then use m_combo to access the combo as a CHistoryCombo.
(The example in the MSDN documentation for CWnd::SubclassDlgItem shows something similar to this)
"The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi,I found that your CHistoryCombo control has some problem:When the text in the edit box of the combobox is large, the more text exceed the edit box width doesn't appear at all. Can you tell me why? Is it a bug?
liuzz
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
To allow text to be entered in the edit box of a combo that is longer than the width of the combo the CBS_AUTOHSCROLL must be used on the combo box. This is the case for all combo boxes. (My demo doesn't have this style and so text will be limited to the combo's width.)
--- "The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | 3.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
Is it be possible to scroll the text on the right automatically?
For example if you have long paths (e.g. D:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\ConnectionManager\Bin) while 'navigating' to it, would be much more useful to show the right hand side of the text (e.g. ...\CompactFrameworkSDK\ConnectionManager\Bin), rather than left part (e.g. D:\Program Files\Microsoft Visual Studio .NET 2003\...).
Can your class do this?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The standard combo box does this when you click on the item, or select it from the drop-list. My class is no different in this respect.
"The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Right, ok.
I was thinking more, when the drop-down is actually showing, before making the selection, the text to be moved on the right, so the right hand side of the text will be visible.
However since then the only soultion I could find was to have an owner-drawn list box, unless you have any other ideas?
Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Is it possible to have the same functionality here using serialization? I mean deriving a class from CComboBox and implementing serialization for the ComboBox elements. I tried to do this but my serialize() function doesn't get called and CObject's get called instead. Although, the IMPLEMENT_SERIAL() and DECLARE_SERIAL() were there.
Can any one tell my why, thanx...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
It would help if you could post some of your code (or email it to me) so I can see how you're trying to do it. Are you using the >>/<< operators, or Serialize(ar), or ReadObject/WriteObject? I would have thought that the simplest way is to either have a CHistoryCombo::Serialize() function, and call it directly, or to have overrides of LoadHistory/SaveHistory that take a CArchive&.
--- "The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hello Paul,
This is how i wrote the CSerialComboBox:
/////////////////////////////////////////// class CSerialComboBox : public CComboBox {
DECLARE_SERIAL(CSerialComboBox) // Construction public: CSerialComboBox();
// Attributes public:
// Operations public:
// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CSerialComboBox) //}}AFX_VIRTUAL
// Implementation public: void Serialize(CArchive& ar); virtual ~CSerialComboBox();
// Generated message map functions protected: //{{AFX_MSG(CSerialComboBox) // NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG
DECLARE_MESSAGE_MAP() };
// SerialComboBox.cpp : implementation file //
#include "stdafx.h" #include "logFilter.h" #include "SerialComboBox.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
IMPLEMENT_SERIAL(CSerialComboBox, CComboBox, VERSIONABLE_SCHEMA ) ///////////////////////////////////////////////////////////////////////////// // CSerialComboBox
CSerialComboBox::CSerialComboBox() { }
CSerialComboBox::~CSerialComboBox() { }
void CSerialComboBox::Serialize(CArchive &ar) { CComboBox::Serialize(ar); CString strTemp;
int nCount = CComboBox::GetCount();
if(ar.IsStoring()) { for (int i=0; i <= nCount; i++) { CComboBox::GetLBText( i, (char*) LPCTSTR(strTemp)); ar << strTemp; } } else { }
}
BEGIN_MESSAGE_MAP(CSerialComboBox, CComboBox) //{{AFX_MSG_MAP(CSerialComboBox) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP()
and here how it is used in my dialog class:
void CMainDialog::OnExit() { CFile fSerial; CFileException fileEx; // fSerial.Open("presist.mus",CFile::Read if (!fSerial.Open("presist.mus", CFile::modeCreate | CFile::modeWrite, &fileEx)) { PrintFileException(fileEx.m_cause); TRACE( "File Exception: Can't creat presistance file"); exit(2); //Can't creat output file }
CArchive arcSerial(&fSerial,CArchive::store);
CSerialComboBox *FileNames, *FilterCriteria; FileNames = (CSerialComboBox*) GetDlgItem(IDC_FILENAME); FilterCriteria = (CSerialComboBox*) GetDlgItem(IDC_CRITERIA); //FileNames->Serialize(fserial); FilterCriteria->Serialize(arcSerial); exit(0); }
the problem is that the Serialize() in CSerialComboBox class doesn't get called and the one in CObject which only returns. One more thing, the visual studio recognizes the CSerialComboBox but when i want to go to the funtion definitions it says that they are not defined. what might be the reason for that...
Thanx alot for your help, Bye...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The main problem isn't with the serialization. It is this:FilterCriteria = (CSerialComboBox*) GetDlgItem(IDC_CRITERIA); You can't simply cast a CWnd* to a CSerialComboBox*. Whatever you cast it to it's still really a CWnd*. If you break in a debugger after the line above, and look at what it says FilterCriteria is, it will tell you it's a CTempWnd*.
You need to subclass the control to a CSerialComboBox to be able to use it as that. This is normally done by setting up a member variable mapping using the ClassWizard. This subclasses the window handle.
When you call the MFC GetDlgItem(), the function first gets the HWND for the control, and then it looks it up to find the CWnd* it is currently mapped to (see documentation for CWnd::FromHandle). In your case it's not mapped to anything so you get a CTempWnd object. The function then always returns it as a CWnd*.
If you had already subclassed it to a CSerialComboBox, then the pointer returned would be a CSerialComboBox* (albeit upcast to a CWnd*), and the debugger would show it as that. (Using something like DYNAMIC_DOWNCAST can help with making sure you have the correct cast.)
So, to fix your problem, map the control to a CSerialComboBox.
As for the functions not being found, I assume you mean in the ClassView. The best remedy for this is to delete your workspace's ncb file. You will need to close the workspace first.
Perhaps I could make a suggestion about your Serialize() function. When serializing a list of items where the number of items is dynamic, it s a good idea to store the number of items first, then store each item in a loop. Then, on loading, you will then first read the number of items, then you know how many times to loop and read the actual items.
I hope this helps!
--- "The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
hi Paul,
Your are right about that, I gotta subclass my combobox for it to work.
Thanx alot for your advice and happy coding..
MM.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I have now posted an update which includes serialising to and from a CArchive object.
"The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Doing MFC for years, I feel like a totally beginner now:
Also I get the notifications (CBN_XXX), when I select a history from the droplist, every time I query the edit control-part of the combobox for the current text (via GetWindowText) in response to such a CBN_XXX notification, it returns me the content of the edit control, as it was BEFORE the entry from the history droplist was selected.
I'm still investigating, but I don't know, whether it's related to your class or to combobox in general.
Any suggestions?
-- See me: www.magerquark.de
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
While in the handlers for the CBN_XXX notifications, the window text has not yet been updated. Therefore any call to GetWindowText() will only ever return the 'old' text. To get the newly selected text you must use:
CString sText; int nIndex = combo.GetCurSel(); if (nIndex >= 0) combo.GetLBText(nIndex, sText); The only exceptions to this are for the CBN_EDITCHANGE and CBN_EDITUPDATE notifications, where you should use GetWindowText(), and not GetLBText().
(This is not related to my class; it is the same for comboboxes in general.)
--- "The way of a fool seems right to him, but a wise man listens to advice" - Proverbs 12:15 (NIV)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|