|
Neat code, thanks!
However, there appears to be a small syncronisation problem with the MDI tab title compared with the document title
The OnUpdateFrameTitle(BOOL bAddToTitle) implementation is very neat however it can be tripped up.
If the document title is changed after the view is created the update routine is called correctly. However the text returned by GetWindowText() within the routine is the old title, rather than the new one.
You can demonstrate this in your demo app by simply saving one of the document views. You will see the window and app title change to the filename correctly, however the MDI tab is not updated (correctly) until a subsequent refresh of the tab of occurs (open another document / minimise the view / etc)
Alternatively you can create the fault by calling CDocument::SetTitle() any time after the document has been displayed.
Can anyone offer a solution? I've traced the code through and everything seems to be being called in the correct order
--
The Obliterator
|
|
|
|
|
One way to fix it is to overload CDocument::SetTitle() to update the tab control. It's not pretty, but it seems to work.
- Nate
|
|
|
|
|
Yes I considered this, thanks, but like you say its not very pretty!
Actually simply overriding CDocument::SetTitle() to call CDocument::SetTitle() twice in succession resolves the problem. Its slightly neater, but does cause a few unnecessary messages and refreshes. Not to mention you'd need to remember to do this in every document class you create
I'd prefer a more robust self-contained soltion within the MDITabs::Update() function if possible.
--
The Obliterator
|
|
|
|
|
I've encountered the same and has fixed it in MDITabs.cpp..
Search for the following function and look for my additions
void CMDITabs::Update()
{
SetRedraw(false);
HWND active = ::GetTopWindow(m_mdiClient);
typedef std::vector<HWND> TWndVec;
typedef TWndVec::iterator TWndIter;
TWndVec vChild;
for (HWND child = active; child; child = ::GetNextWindow(child, GW_HWNDNEXT))
{
vChild.push_back(child);
}
TCITEM item;
char text[256];
item.pszText = text;
for (int i = GetItemCount(); i--; )
{
item.mask = TCIF_PARAM;
GetItem(i, &item);
TWndIter it = std::find(vChild.begin(), vChild.end(), HWND(item.lParam));
if (it == vChild.end())
{
DeleteItem(i);
if (m_bImages) RemoveImage(i);
}
else
{
item.mask = TCIF_TEXT;
::GetWindowText(*it, text, 256);
CMDIChildWnd *pCF;
pCF = static_cast<CMDIChildWnd*>(FromHandlePermanent(*it));
if (pCF != NULL) {
CDocument *pDoc = pCF->GetActiveDocument();
if (pDoc != NULL) {
strncpy(text, pDoc->GetTitle(), sizeof(text));
}
}
if (m_bImages) m_images.Replace(i, (HICON)::GetClassLong(*it, GCL_HICONSM));
SetItem(i, &item);
if (*it == active) SetCurSel(i);
vChild.erase(it);
}
}
i = GetItemCount();
for (TWndIter it = vChild.begin(), end = vChild.end(); it != end; ++it)
{
item.mask = TCIF_TEXT|TCIF_PARAM|TCIF_IMAGE;
::GetWindowText(*it, text, 256);
CMDIChildWnd *pCF;
pCF = static_cast<CMDIChildWnd*>(FromHandlePermanent(*it));
if (pCF != NULL) {
CDocument *pDoc = pCF->GetActiveDocument();
if (pDoc != NULL) {
strncpy(text, pDoc->GetTitle(), sizeof(text));
}
}
if (m_bImages) m_images.Add((HICON)::GetClassLong(*it, GCL_HICONSM));
item.iImage = i;
item.lParam = LPARAM(*it);
InsertItem(i, &item);
if (*it == active) SetCurSel(i);
++i;
}
bool bShow = GetItemCount() >= m_minViews;
if ((!bShow && IsWindowVisible()) || (bShow && !IsWindowVisible()))
{
static_cast<CMDIFrameWnd*>(FromHandlePermanent(::GetParent(m_mdiClient)))->RecalcLayout();
}
RedrawWindow(NULL, NULL, RDW_FRAME|RDW_INVALIDATE|RDW_ERASE);
SetRedraw(true);
}
I think this solves it all
Some other genius may add the classtype checking
Greetings,
Albert 'The Mad Butcher!' van Peppen
PS. Happy birthday to me
EDIT: Some annoying things in the code snippet
EDIT2: Fixed some HTML coding problems
|
|
|
|
|
Thanks for the fix - works perfectly.
Tell me though what is the advantage of static_cast() over the normal:
CMDIChildWnd *pCF;
pCF = (CMDIChildWnd *)(FromHandlePermanent(*it));
if (pCF != NULL && pCF->IsKindOf(RUNTIME_CLASS(CMDIChildWnd))
{...}
--
The Obliterator
|
|
|
|
|
Nothing special with the static_cast()..
I'm running devstudio .NET 7.1 and it is promoted to use static_cast() or dynamic_cast()..
That's why.
Albert
|
|
|
|
|
Ok cheers.
Happy birthday BTW (now wheres that emoticon of the cake with the candles?!)
--
The Obliterator
|
|
|
|
|
BTW-
the static_cast operators parameter was obliterated by html tag chaos in the fix snippet.
Change the line:
pCF = static_cast(FromHandlePermanent(*it));
to:
pCF = static_cast<CCMDIChildWnd*>(FromHandlePermanent(*it));
(or at least this is what i assumed the author intended
Cheers,
EE
|
|
|
|
|
Indeed,
It should read like:
....
CMDIChildWnd *pCF;
pCF = static_cast<CMDIChildWnd*>(FromHandlePermanent(*it));
if (pCF != NULL) {
.....
TMB!
I've fixed it in my original posting..
|
|
|
|
|
I've been experimenting and trying to get a shorter-type tab control. Let me explain what I mean:
Say I have a view, it's fairly crowded and I need to display tabs. But I only need to display three or four tabs at a time. Now, I want these tabs on the top left of my page, and I have space there for them. Now, on the top right of the page I have some eidt lines and whatnot. So, I'm wondering how I can draw a tab control that's only a couple hundred pixels wide and then still have all that other stuff in the same position (rather than having the tab control push it down a level). The tab control goes all the way across the app, and I don't want that. I want to be able to display stuff on the same horizontal level as the tabs. I kind of want it to just overlay on the view. I've tried a bunch of stuff to get it to work, but things just don't seem to all come together. Any help would be appreciated.
btw, great work on the class. Good implementation.
|
|
|
|
|
How to deal with ampersand in file names?
I tried implementing owner draw, so I can use DT_NOPREFIX in DrawText, but the control is calculating the size of the tab without accounting for DT_NOPREFIX, and the tab size ends up too small.
I see no TCS_NOPREFIX style for tab controls, so is there another solution, other than inserting an extra '&' in the filename?
|
|
|
|
|
Could you not override/change the MDITabs::Update() function?
Where it calls GetWindowText() to get the window title, simply replace '&' with '&&' in the returned string which is assigned to the tab item.
--
The Obliterator
|
|
|
|
|
How can I get the Tabs to show inside my CMDIChildWnd's?
What if I need to have my Tabs inside the child window?
====================================== <- Application's window
|____________________________________|
|_--------------_<_Child_Doc 1_______|
|_|tab1 tab2___|_____________________|
|_|____________|_____________________|
|_|____________|_____________________|
|_--------------_____________________|
|________--------------_<_Child_Doc_2|
|________|tab1________|______________|
|________|____________|______________|
|________|____________|______________|
|________--------------______________|
|____________________________________|
======================================
... I wish we could post image files in our questions.
It would be easier to explain what I need to do.
Thanks,
Russ
|
|
|
|
|
This is not possible with CMDITabs. CMDITabs manages only views inside the main window.
Christian
---
Always expect the unexpected!
|
|
|
|
|
Dear Chris,
Thanks for posting your example
on codeproject. I liked you CMDITabs
so much I want to incorporate it
into my projects.
But when my appication is running
I get this Debug Assertion Failed
pop up. It tells me that it happedned
on Line 120 of file afxwin2.h.
and to click retry to debug the application
Can you help?
Thanks rbc
|
|
|
|
|
oops! correction that file
I mentioned is not afxwin2.h
it is afxwin2.in???
I am new to windows developement
as yuo can probably tell
|
|
|
|
|
Oops! I figured it out. I need to call the
Create method for the MDITab object.
Good code comments!
|
|
|
|
|
No problem
Christian
---
Always expect the unexpected!
|
|
|
|
|
First of all, I'd like to say this is a great and very useful class
We run our applications through Boundschecker (from Numega), and I found a small problem. Boundschecker complains when some of the ::SetPixel calls fail. This happens because some of the calls to to SetPixel are trying to draw outside of the clipping region (see the Microsoft docs on SetPixel)
Here is the fix I came up with:
1) add this function:
void CMDITabs::PtVisibleSetPixel(HDC dc, int x, int y, COLORREF crColor)
{
if(::PtVisible(dc, x,y)) { // if point is within clipping region
::SetPixelV(dc, x, y, crColor); // slightly faster than SetPixel
}
}
2) replace all of the calls to "::SetPixel" with "PtVisibleSetPixel"
Regards,
Warren
|
|
|
|
|
hi,
I would like to know how to add the grid control eg MsFlexGrid into the form. It shows the error "Failed to create empty document".
Thanks
Kevin
[Beginner]
|
|
|
|
|
Hi,
i'm experiencing problems using mditabs in print preview mode. It's still possible in this mode to change between views, confusing the mfc framework - showing strange artefacts.
|
|
|
|
|
We had the same problem. Here is how we solved it:
1) Override CMainFrame::OnSetPreviewMode
2) add a variable BOOL m_bTabsWereOn in CMainFrame
3) turn the tabs off when you enter print preview
(and remember the previous state in m_bTabsWereOn)
4) turn the tabs back on when you exit preview mode
here is some pseudo-code:
void CMainFrame::OnSetPreviewMode(BOOL bPreview, CPrintPreviewState* pState)
{
if (bPreview){
if (mdi_tabs.IsOn ()){
m_bTabsWereOn = true;
mdi_tabs.OnMainFrameTabs ();
}
else{
m_bTabsWereOn = false;
}
}
else{
if (m_bTabsWereOn){
m_bTabsWereOn = false;
mdi_tabs.OnMainFrameTabs ();
}
}
CMDIFrameWnd::OnSetPreviewMode(bPreview, pState);
}
|
|
|
|
|
I have updated CMDITabs to include the following features
- autohide the tabctrl when there are no (one, two) views open
- showing sysmenu as context menu by defaul, an individual context menu is possible
- double clicking maximizes the view
- changed CMDITabs::Create() to include more styles
Have fun
Always expect the unexcpected
|
|
|
|
|
I tried your class in my app, and it works well, but I have two suggestions:
1.) When there are no MDI windows open, could it be possible to hide your control (I have a bitmapped background in the workspace window, and a blank grey bar across the bottom looks rather dumb)
2.) could you add an option where all the tabs fit in the window (a la windows start bar), where the tabs shrink to fit, and use tooltips for the titles. (With my app maximized on a 1024x768 display, I could only get 2.5 tabs to show, so using your class was no easier than the windows menu)
thanks.
---
Blessed are those who can laugh at themselves, for they shall never cease to be amused
|
|
|
|
|
1.) No problem, will be done. If you can't wait, just modify OnSizeParent() so, that the window becomes invisible in case that GetItemCount() == 0
2.) Hmm. If you can get only 2 and a half tabs on 1024x768 screen your title must be veeeryyyy large Ever thought about a differen naming convention?
Anyway, tooltips are a good idea and if the underlying tabctrl supports 'shrink to fit' I will implement it. This will be better than the spin ctrl at the right border
Always expect the unexpected!
|
|
|
|