以前搜到的一篇很好的关于outlookbar的资料,包括其中的bug和一些扩展,现在网上已经很难找到了,感谢原文作者以及转载者,特此分享。
这是在 2007年12月27日 02:56:26 GMT 检索到的 http://telestarnotes.blogspot.com/2004/05/revised-version-of-outlook98-bar-like.html 的 G o o g l e 缓存内容。
G o o g l e 已先预览各网页,拍下网页的快照存档。
这网页可能有更新的版本,请按此查看最新版。
本缓存网页可能引用了已经不存在的图片。单击此处,只查看缓存文本。
请使用网址 http://www.google.com/search?q=cache:arbAbyTxymMJ:telestarnotes.blogspot.com/2004/05/revised-version-of-outlook98-bar-like.html+Cgfxoutbarctrl+bug&hl=zh-CN&ct=clnk&cd=50&gl=cn&st_usg=ALhdy29zuYs7VKyyHA1pzB6lTzF_krWeiw 链接此页或将其做成书签。
Google 和网页作者无关,不对网页的内容负责。
这些搜索字词都已标明如下: cgfxoutbarctrl bug
--------------------------------------------------------------------------------
TeleStar's Notes
This blog backups some of my notes on programming and related issues. It also contains some poems and lyrics that express my feelings. Since Chinese and Japanese words are used in many posts, you'd better to install Asian Language Package to read them smoothly. If you think this blog is useful, please help click the Google Ads on the left.
Monday, May 03, 2004
A revised version of the Outlook98 bar-like control
An Outlook98 bar-like control
Iuri Apollonio
December 5, 1998
http://www.codeguru.com/Cpp/controls/controls/article.php/c2155/
OutBar98 is a well known MFC Control, which was published in CodeGuru on December 5, 1998. However, Iuri Apollonio disappeared from CodeGuru soon after that. Here are some modifications that I adopted. Thanks for Iuri Apollonio and all the guys that gave comments.
1. Use 256 color Icon
m_imagelist.Create(64, 64, ILC_COLOR32, 0, 6);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
m_imagelistCaptureType.Add(&bitmap, RGB(0, 0, 0));
2. Adding a simple mouse over message for items
When using the Outbar control for a program i wanted a way to know if the user is hovering over a item in one of the folders. To solve this i came up with a very simple solution (probable not the best one).
In GfxOutBarCtrl.h add:
#define NM_OB_ITEMHOVER 6
And then in GfxOutBarCtrl.cpp at the end of HighlightItem add:
GetOwner()->SendMessage(WM_OUTBAR_NOTIFY,NB_OB_ITEMHOVER,index);
Then in your program add to the WM_OUTBAR_NOTIFY handler:
case NM_OB_ITEMHOVER
// Cast lParam to get item which mouse is hovering over
{
// Do your stuff here
}
return 0;
I hope this is of help to people. It is probably not the best way to
do it but it works for me.
Cheers
Submitted By: Richard
3. Adding Right-click notification
To add right-click notification, so I can use my own menu
instead of getting the control to do its own context menu,
I have modified the code as below:
Add the code to GfxOutBarCtrl.h:
#define NM_OB_ITEMRCLICK 8
Add a new member (bFolder) to OUTBAR_INFO, so it looks like:
typedef struct OUTBAR_INFO
{
int index;
const char * cText;
int iDragFrom;
int iDragTo;
bool bFolder;
}*LPOUTBARINFO;
then add a handler for WM_RBUTTONUP:
void CGfxOutBarCtrl::OnRButtonUp(UINT nFlags, CPoint point)
{
OUTBAR_INFO obi = { 0 };
int ht = HitTestEx(point, obi.index);
if (ht != htItem)
{
obi.bFolder = TRUE;
if (ht != htFolder)
{
obi.index = -1;
}
}
LRESULT lResult = GetOwner()->SendMessage(WM_OUTBAR_NOTIFY, NM_OB_ITEMRCLICK, (LPARAM)&obi);
if (lResult == 0)
{
// add the code from RButtonDown here
}
CWnd::OnRButtonUp(nFlags, point);
}
The OnRButtonDown handler can now be removed.
The owner window can now add a handler for the notification
and show its own context menu.
To prevent the bar using its own menu, the handler should return non-zero.
Submitted By: Paul S. Vickery
4. Adding tooltips
If you want to display tooltips for items in CGfxOutBarCtrl,
you only need to derive your class and add 2 functions:
1) Create your own class COutBarCtrlEx, derived from CGfxOutBarCtr;
2) Add next 2 declarations:
virtual int OnToolHitTest(CPoint point, TOOLINFO * pTI)const;
afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, RESULT * pResult );
3)Add two lines into Message map of your class
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
4)Add 2 definitions:
/* OnToolTipText() well described in codeguru article by Stuart Carter & Kory Becker about CFileDropListCtrl. I simply copy & paste it. */
BOOL COutBarCtrlEx::OnToolTipText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
// need to handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
CString strTipText;
UINT nID = pNMHDR->idFrom;
if( nID-- == 0 ) // Notification in NT from automatically
return FALSE; // created tooltip
//int row = ((nID-1) >> 10) & 0x3fffff ;
//int col = (nID-1) & 0x3ff;
int folder = nID >> 8;
int index = nID & 0xFF;
// Use Item's name as the tool tip. Change this for something different.
// Like use its file size, etc.
strTipText = GetItemText( index );
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, strTipText, 80);
else
_mbstowcsz(pTTTW->szText, strTipText, 80);
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, strTipText, 80);
else
lstrcpyn(pTTTW->szText, strTipText, 80);
#endif
*pResult = 0;
return TRUE; // message was handled
}
/* What about OnToolHitTest(), you may define it like this: */
int COutBarCtrlEx::OnToolHitTest(CPoint point, TOOLINFO *pTI) const
{
int ht, index =0;
COutBarCtrlEx *pBar = const_cast(this);
ht = pBar->HitTestEx(point,index);
if (ht != htItem)
{
return -1;
}
int folder = iSelFolder;
CRect rect;
pBar->GetItemRect(folder,index,rect);
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)(folder <<8) | (index&0xFF) + 1;
pTI->lpszText = LPSTR_TEXTCALLBACK;
pTI->rect = rect;
return pTI->uId;
}
5) Now you can use your own class with tooltips instead of CGfxOutBarCtrl. For this just replace declaration of CGfxOutBarCtrl object with COutBarCtrlEx in your app.
Submitted By: Kirill Klementiev
5. Getting notification of items/folders being deleted
In order to simplify knowing what action to perform when
the user clicks on an item, I add item data to each item,
which is then retrieved when I need to carry out, or query,
the action.
The data is new'd/malloc'd, so needs freeing when an item
or folder is deleted, and also when the control is destroyed.
I have added notification of deletion to the OutBar class
so I can free any allocated data when needed.
To add this functionality, add the following notification
codes to GfxOutBarCtrl.h (use different numbers if you have
already added some codes here):
#define NM_OB_DELETEITEM 6
#define NM_OB_DELETEFOLDER 7
To get the notifications sent, add the following code to
CGfxOutBarCtrl::RemoveFolder:
CBarFolder * pbf = (CBarFolder*)arFolder.GetAt(index);
ASSERT(pbf);
// new code start
// PSV: tell owner of deletion of each item
int nSelFolderCur = iSelFolder; // save current selected folder
iSelFolder = index; // temporarily set sel folder to folder being deleted
int nItems = pbf->arItems.GetSize();
for (int item = 0; item < nItems; item++)
GetOwner()->SendMessage(WM_OUTBAR_NOTIFY, NM_OB_DELETEITEM, (LPARAM)item);
iSelFolder = nSelFolderCur; // restore selected folder
// new code end
if (pbf->pChild != NULL)
...
Invalidate();
// new code start
// PSV: tell owner of deletion
GetOwner()->SendMessage(WM_OUTBAR_NOTIFY, NM_OB_DELETEFOLDER, (LPARAM)index);
// new code end
and add to CGfxOutBarCtrl::RemoveItem:
if (IsValidItem(index))
{
// new code start
// PSV: tell owner of impending item deletion
GetOwner()->SendMessage(WM_OUTBAR_NOTIFY, NM_OB_DELETEITEM, (LPARAM)index);
// new code end
CBarItem * i = (CBarItem *) pbf->arItems.GetAt(index);
delete i;
That caters for getting a notication when an item/folder is
deleted. Add the next code to make sure it happens when the
control is destroyed. Add a new message handler for WM_DESTROY as shown below:
void CGfxOutBarCtrl::OnDestroy()
{
// tell the owner that each folder, and each of its items is deing destroyed
for (int t = 0; t < arFolder.GetSize(); t++)
{
iSelFolder = t;
CBarFolder* pbf = (CBarFolder*) arFolder.GetAt(t);
if (pbf != NULL)
{
int nItems = pbf->arItems.GetSize();
for (int index = 0; index < nItems; index++)
GetOwner()->SendMessage(WM_OUTBAR_NOTIFY, NM_OB_DELETEITEM, (LPARAM)index);
GetOwner()->SendMessage(WM_OUTBAR_NOTIFY, NM_OB_DELETEFOLDER, (LPARAM)t);
}
}
CWnd::OnDestroy();
}
Submitted By: Paul S. Vickery
6. How to open a modal dialog from the OnOutbarNotify funtion
Since may have written to me reporting trouble when trying to execute a modal dialog box from within the OnOutbarNotify function, here's the trick;
- add ReleaseCapture(); before opening your dialog
The trouble is related to how the bar control capture the mouse to get the hoover look; if the control has capture (as is always the case in this function), no modal will be correctly executed until you release the mouse capture, with the function above.
Enjoy!
Submitted By: Iuri
7. Removing Folder Error
First, we have
///
void CGfxOutBarCtrl::RemoveFolder(const int index)
{
ASSERT(index >= 0 && index < GetFolderCount());
CBarFolder * pbf = (CBarFolder *)
arFolder.GetAt(index);
if (pbf->pChild != NULL)
{
pbf->pChild->DestroyWindow() ;
pbf->pChild = NULL;
} delete pbf;
arFolder.RemoveAt(index);
if (iSelFolder >= index) iSelFolder = index - 1;
if (iSelFolder < 0 && GetFolderCount() > 0)
iSelFolder = 0;
if (iSelFolder >= 0)
SetSelFolder(iSelFolder);
Invalidate();
}
Submitted By: BokSoo Yun
Then we have the following change
Still a bug when deleting last folder
If the last folder is deleted, by right-clicking and
selecting 'Remove', then the control asserts when trying
to redraw. This is because the painting code is trying to
draw the last highlighted folder, as set by the class's
member variable iLastFolderHighLighted. This is set to the
last folder, which has just been deleted, and is therefore
invalid.
The solution to this is to add the line to BokSoo Yun's
code just before the Invalidate(); thusly:
iLastFolderHighlighted = -1;
and to add, to CGfxOutBarCtrl::OnPaint(), the following test to see if there any any folders to draw:
DrawFolder(pDC, t, frc, false);
}
if (!GetFolderChild() && max > 0) // <-- modified code
{
int f,l;
Submitted By: Paul S. Vickery
8. Removing Folder Error
///
void CGfxOutBarCtrl::RemoveFolder(const int index)
{
ASSERT(index >= 0 && index < GetFolderCount());
CBarFolder * pbf = (CBarFolder *)
arFolder.GetAt(index);
if (pbf->pChild != NULL)
{
pbf->pChild->DestroyWindow() ;
pbf->pChild = NULL;
} delete pbf;
arFolder.RemoveAt(index);
if (iSelFolder >= index) iSelFolder = index - 1;
if (iSelFolder < 0 && GetFolderCount() > 0)
iSelFolder = 0;
if (iSelFolder >= 0)
SetSelFolder(iSelFolder);
Invalidate();
}
Submitted By: BokSoo Yun
9. Remove Flicker
// non flickering painting fix
IMAGEINFO ii;
ima->GetImageInfo(pi->iImageIndex, &ii);
CSize szImage = CRect(ii.rcImage).Size();
CPoint pt;
if (IsSmallIconView())
{
pt.x = rc.left + 2;
pt.y = rc.top + (rc.Height() - szImage.cy) / 2;
}
else
{
pt.x = rc.left + (rc.Width() - szImage.cx) / 2;
pt.y = rc.top;
}
CRect rcBck(pt.x-1, pt.y-1, pt.x + szImage.cx+2, pt.y + szImage.cy+2);
/* PREPARE THE MEMORY DC */
CClientDC dc(this);
HDC hMemDC = CreateCompatibleDC(dc);
HBITMAP hBmp = CreateCompatibleBitmap(dc, rcBck.right - rcBck.left, rcBck.bottom - rcBck.top );
SelectObject(hMemDC, GetStockObject(DEFAULT_GUI_FONT));
SelectObject(hMemDC, hBmp);
SetViewportOrgEx(hMemDC, rcBck.left * -1, rcBck.top * -1, NULL);
/* FILL THE DC WITH THE BACKGROUND COLOUR TO REMOVE THE OLD ICON POSITION */
CBrush brushFill( crBackGroundColor );
FillRect(hMemDC, &rcBck, (HBRUSH)brushFill.m_hObject);
/* DRAW THE ICON AT THE NEW POSITION */
pt.x += xoffset;
pt.y += yoffset;
ima->Draw(CDC::FromHandle(hMemDC), pi->iImageIndex, pt, ILD_NORMAL);
/* APPLY THE MEMORY DC TO THE ORIGINAL DC */
BitBlt(dc, rcBck.left, rcBck.top, rcBck.right - rcBck.left,
rcBck.bottom - rcBck.top, hMemDC, rcBck.left, rcBck.top, SRCCOPY);
(按:I forget the name of the author who proposed this modification)
10. Forbid a Folder
int CCuteFolderBarCtrl::HitTestEx(const CPoint & point, int &index)
{
if (bUpArrow && rcUpArrow.PtInRect(point)) return htUpScroll;
if (bDownArrow && rcDownArrow.PtInRect(point)) return htDownScroll;
int max = arFolder.GetSize(), t;
if (max <= 0) // prevent assertion failure on deleting last folder
return htNothing;
// Add this to enable / disable
CBarFolder * pbf = (CBarFolder *) arFolder.GetAt(iSelFolder);
if (pbf->bEnabled == false) // if disabled, then silently return
return htNothing;
11. Order of drawing items
you need to draw the scollbutton finally. otherwise, it may be covered by the labels.
12. Some other comments
While this is a great control, it differs from Outlook in various ways:
1. Outlook displays folder labels centrally, but uses an ellipsis
on the end of the line if the label is too wide.
This can be acheived by adding DT_END_ELLIPSIS to the flags
specified to DrawText in CGfxOutBarCtrl::DrawFolder thusly:
pDC->DrawText(CString(pbf->cName), rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
2. Editing the folder labels in Outlook, the text is left-justified,
and the edit control is single-line, and scrolls horizontally.
This can be acheived by altering the line where the in-place
edit control is created to read:
pEdit->Create(WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL,rc,this,1);
3. Outlook draws its item labels (in large icon view) no more
than two lines high. I'm still working on a fix for this - watch this space!
4. Switching between large and small icons is only on the
current folder, so one folder can show as small icons,
while another shows as large icons. This can be acheived by
the following changes:
Add a new bSmallIcons member to class CBarFolder:
CWnd * pChild;
bool bSmallIcons; // <-- new line
};
this holds whether the folder is showing in small icon view
Replace the CGfxOutBarCtrl::IsSmallIconView/SetSmallIconView
function definitions with the functions below:
bool CGfxOutBarCtrl::IsSmallIconView(const int iFolder/*=-1*/) const
{
int folder = iFolder;
if (GetFolderCount() <= 0 || folder == -1)
return dwFlags&fSmallIcon;
CBarFolder* pbf = (CBarFolder*)arFolder.GetAt(folder);
return pbf->bSmallIcons;
}
void CGfxOutBarCtrl::SetSmallIconView(const bool bSet, const int iFolder/*=-1*/)
{
iFirstItem = 0;
if (iFolder != -1)
{
if (iFolder >= 0 && iFolder < GetFolderCount())
{
CBarFolder* pbf = (CBarFolder*)arFolder.GetAt(iFolder);
pbf->bSmallIcons = bSet;
}
}
else
{
// do all current folders, and set flag so new folders
// have the chosen style
int nFolders = GetFolderCount();
for (int i = 0; i < nFolders; i++)
{
CBarFolder* pbf = (CBarFolder*)arFolder.GetAt(i);
pbf->bSmallIcons = bSet;
}
if (bSet && ! IsSmallIconView())
dwFlags |= fSmallIcon;
else if (! bSet && IsSmallIconView())
dwFlags &= ~fSmallIcon;
}
CRect rc;
GetInsideRect(rc);
InvalidateRect(rc, false);
}
and replace the same functions' declarations with:
void SetSmallIconView(const bool bSet, const int iFolder = -1);
bool IsSmallIconView(const int iFolder = -1) const;
Insert a line into CGfxOutBarCtrl::AddFolder, to set the new
folder's view style based on the current global setting:
arFolder.Add((void *)pbf);
pbf->bSmallIcons = dwFlags & fSmallIcon; // <-- new line
return arFolder.GetSize() - 1;
Modify both calls to SetSmallIconView (found in OnGfxLargeIcon
and OnGfxSmallIcon) to take an extra argument of iSelFolder.
Now the tricky bit! - modify all calls to IsSmallIconView()
in GfxOutBarCtrl.cpp to take an argument of the folder to
check. This will be the folder variable used near the function
call (iSelFolder or iFolder). The only calls to the function
which take no arguments should be inside SetSmallIconView() (2 occurances)
Calls to Is/SetSmallIconView outside of GfxOutBarCtrl will
continue to behave exactly as previously, unless the app is
modified to pass in a folder index.
Submitted By: Paul S. Vickery
List of bugs I found
1) When I tried to scroll the iconic view with mouse wheel the program crashed.(My mouse has 4 button and 2 wheels.)
2) The old name stays at the background while editing the labels in small icon mode.(I fixed it and added some code to make the editbox window as a overlapped child of main frame to prevent the clipping at right of outbar and to defaultly select the whole text wgich is the most usual case).
3) Removing the actual folder causes Displayal error.But removing inactive bars correctly works So this problem can be solved by activating the next folder (or the previous if the active folder is the last one) and then removing the folder.
4) The label edit boxes aren't multiline. Or maybe the multiline code doesn't work. I couldn't examine it.
5) Item dragging isn't smart. It's possible to drag an item to its own place. And there are some more mistakes that I can't remember.
I will try to correct these bugs and then send it to the site.
The code is very readable but I think can be shortened without reducing the readability. And It's so strange to see bugs at top level while other detailed levels are completely mistakeless.
Thanks very much to the programmer whom I don't know his name for sharing his work with us.
Submitted By: Aykut KILI?
OutBar98 inspired several other controls including
CXTOutlookBar
http://www.codejock.com/developer/article03.asp
The CXTOutlookBar class is a CListBox derived class that implements a Outlook style control. The CXTPagerCtrl class is used to contain and scroll the CXTOutlookBar window. This class wraps the windows pager API. Both classes are fairly easy to use and can be used the same way you would any standard MFC control.
Themed Windows XP style Explorer Bar
By Mathew Hall
http://www.codeproject.com/cs/miscctrl/XPTaskBar.asp (I really prefer this one most)
Windows XP style Collapsible Panel Bar
By Derek Lakin
http://www.codeproject.com/cs/miscctrl/collapsiblepanelbar.asp
Just another C# Collapsing Group Control
By Daren May
http://www.codeproject.com/cs/miscctrl/xpgroupbox.asp
Full-featured XP Style Collapsible Panel
By Tom Guinther
http://www.codeproject.com/cs/miscctrl/TgXPPanel.asp
# posted by TeleStar @ 2:09 PM
<< Home
My Other Website
My Book Shelf
Links
Blogs.Msdn.Com
Blogs.Asp.Net
Blogs.DotNetJunkies
博客堂
博客园
Blogs.Csdn
Archives
December 2002January 2003April 2003June 2003July 2003October 2003November 2003December 2003January 2004February 2004May 2004June 2004July 2004August 2004September 2004October 2004November 2004December 2004January 2005February 2005March 2005April 2005May 2005June 2005July 2005August 2005September 2005October 2005November 2005December 2005January 2006February 2006March 2006April 2006June 2006September 2006December 2006January 2007February 2007May 2007
Recent Posts
How to implement a Word like Multi-SDI program
How to implement a TabView
旅中卧病,梦里,奔驰在荒凉的原野
New York, New York
Brod und Wein
蒿里曲
Please Do Not Let Me Be Misunderstood
The Psychoed
Bundles
港都夜雨
Search in This Blog
Google Ads