|
David Horner wrote:
modified the TabbedSDISplitter to use the CLOSEBUTTON style.
// like this
m_tabbedChildWindow.SetTabStyles(CTCS_TOOLTIPS|CTCS_CLOSEBUTTON);
I also added
NOTIFY_CODE_HANDLER(CTCN_CLOSE, OnTabClose)
to the message map.
And defined:
LRESULT OnTabClose(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
LPNMCTCITEM pnmCustomTab = (LPNMCTCITEM)pnmh;
MessageBox("hullo");
bHandled = FALSE;
return 0;
}
What am I missing? None of the notification messages are coming through!
Thanks,
Dave
If you watch things with Spy++, you'll see that CTCN_CLOSE (0xFFFFFDCB) goes to the CTabbedChildWindow window. However, getting the close button to work properly takes a little more than just handling that notification. Take a look at CMDITabOwnerImpl to see what else can be involved.
You could have your own version of CTabbedChildWindow (say CTabbedChildWithCloseWindow), that handled CTCN_CLOSE. You would also probably need to implement OnAddFirstTab() and OnRemoveLastTab() to show/hide the tab control based on if there are any tabs. When you handle CTCN_CLOSE, you could send WM_CLOSE to the child window - but that doesn't always work (for example, when it is a modeless child dialog). You'd also need to keep track if the child got closed another way (if possible).
With CMDITabOwnerImpl, there are a couple of differences that make it easier to get a close button working. First of all, direct MDI child windows are expected to be frame windows. With CMDITabOwnerImpl, MDI child frames are also expected to inherit from CTabbedMDIChildWindowImpl (which inherits from CMDIChildWindowImpl). An MDI child frame also sends messages like WM_MDIDESTROY to tell you when it is being destroyed. Also note that CMDITabOwnerImpl has some differences because of getting it work work right with the MDIClient window (its a sibling to the child views and the MDI Client rather than a parent).
You can definitely come up with a way to make this work for your particular case, but its a little harder for a general solution. For example, if all of the child windows can only be closed by the close button with the tabs, you could just make sure that they all handle WM_CLOSE appropriately (for example, a dialog would call DestroyWindow on itself, since DefWindowProc doesn't). If they can close another way, you can make up a registered message that the child sends the tab owner when they're getting destroyed, so that you can remove the corresponding tab. Or you come up with your own "frame" intermediate window that handles things that the tab want.
Here's one way you could handle CTCN_CLOSE if you can assume that WM_CLOSE is the appropriately handled by all possible child windows:
LRESULT OnTabClose(int , LPNMHDR pnmh, BOOL& bHandled)
{
LPNMCTCITEM pnmCustomTab = (LPNMCTCITEM)pnmh;
if(pnmCustomTab)
{
if(pnmCustomTab->iItem >= 0)
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(pnmCustomTab->iItem);
if(pItem)
{
HWND hWndChild = pItem->GetTabView();
if(0 == ::SendMessage(hWndChild, WM_CLOSE, 0, 0L))
{
this->RemoveTab(hWndChild);
}
}
}
}
bHandled = FALSE;
return 0;
}
-Daniel
|
|
|
|
|
Daniel,
Actually, I was not so concerned about the handling of the CLOSE notification but all the notifications.
I would really like to have the parent of the CTabbedChildWindow handle all the WM_NOTIFYs. Like the onTabChange, Close,FirstTab,LastTab, etc. I don't really want to have to create a class that derieves from CTabbedFrameImpl to handle WM_NOTIFY messages for the tab control.
The message map of CTabbedChildWindow is defined:
BEGIN_MSG_MAP(thisClass)
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
This doesn't seem to forward the WM_NOTIFY messages on to the parent.
If I define the message map like so:
BEGIN_MSG_MAP(thisClass)
NOTIFY_CODE_HANDLER(CTCN_CLOSE, OnTabClose)
NOTIFY_CODE_HANDLER(CTCN_SELCHANGE, OnTabChange)
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
I get the notifications like you said, however, that means I gotta create another class or modify your class to get the notifications. I can't get the notifications in the parent.
So I thought that I could just define:
BEGIN_MSG_MAP(thisClass)
FORWARD_NOTIFICATIONS()
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
However, the display gets all messed up and things don't work correctly. I don't know why this is.
What do you suggest?
Should I modify the CTabbedChildWindow to contain NOTIFY_CODE_HANDLER for each of the NOTIFICATIONS I want? Defining in each handler to run the defProc and forward on to PARENT window for handling by parent?
Is there another way to forward the notifications without having to create handlers for each... and without eating up the messages before CTabbedChildWindow gets a crack at them?
Thanks,
Dave
|
|
|
|
|
Well,
I sorta have a solution. But let me know if there is a better way.
I looked at the FORWARD_NOTIFICATIONS define and it was setting the bHandled to true and returning.
bHandled = TRUE; \
lResult = Atl3ForwardNotifications(); \
if(bHandled) \
return TRUE; \
I defined a new FORWARD_NOTIFICATIONS_NOHANDLE define that doesn't return so that it will fall through to the CTabbedChildWindow messagemap.
#define FORWARD_NOTIFICATIONS_NOHANDLE() \
{ \
bHandled = TRUE; \
lResult = Atl3ForwardNotifications(m_hWnd, uMsg, wParam, lParam, bHandled); \
}
This seems to work fine, however, I don't know if I should be setting the bHandled to TRUE still.... Or if I want to forward all the messages that the Alt3ForwardNotifications forwards. (WM_COMMAND,WM_NOTIFY,etc)
Is this a good solution?
In any case, I have my code working.
Thanks,
Dave
|
|
|
|
|
David Horner wrote:
So I thought that I could just define:
BEGIN_MSG_MAP(thisClass)
FORWARD_NOTIFICATIONS()
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
However, the display gets all messed up and things don't work correctly. I don't know why this is.
...
David Horner wrote:
I defined a new FORWARD_NOTIFICATIONS_NOHANDLE define that doesn't return so that it will fall through to the CTabbedChildWindow messagemap.
Most of the time, you either do FORWARD_NOTIFICATIONS or you do REFLECT_NOTIFICATIONS . You usually have one of these as the last entry in the message map. If you want to do both, then the first one has to not return and not set bHandled to TRUE. CTabbedFrameImpl depends on some notifications to do its job. It also lets you choose whether or not to reflect notifications by calling SetReflectNotifications . If set, then it will do REFLECT_NOTIFICATIONS . So if you want to forward notifications also, then having something like FORWARD_NOTIFICATIONS_NOHANDLE is fine. Your implementation is geared more towards VC 6 specifically though (ATL 7+ has FORWARD_NOTIFICATIONS and ForwardNotifications natively now).
David Horner wrote:
I would really like to have the parent of the CTabbedChildWindow handle all the WM_NOTIFYs. Like the onTabChange, Close,FirstTab,LastTab, etc. I don't really want to have to create a class that derieves from CTabbedFrameImpl to handle WM_NOTIFY messages for the tab control.
That's fine if you want to do it that way, but it doesn't make it very encapsulated or self sustained. If you look through TabbedFrame.h and TabbedMDI.h , you'll see that out of the box, there are essentially 3 different "tab owner" implementations - CTabbedChildWindow and CTabbedPopupFrame (which derive from CTabbedFrameImpl, which derives from CCustomTabOwnerImpl) and CMDITabOwnerImpl (which derives directly from CCustomTabOwnerImpl).
It seems like it'd be better to encapsulate your handling of the tab notifications in another CTabbedFrameImpl derived class. If you need some external information, you could always add different member variables and accessor functions, or even come up with your own notifications or registered messages to communicate with the window's parent.
-Daniel
|
|
|
|
|
Any simple how to hide the tabs, especially in MDI application? (say you give the user the choice between using or not using the tabs support).
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Paul Selormey wrote:
Any simple how to hide the tabs, especially in MDI application? (say you give the user the choice between using or not using the tabs support).
OK, I might change how you do this in my next updates, but here's what you can do for the short term:
Updates to CMDITabOwner
- Add a member variable
m_bKeepTabsHidden - Initialize
m_bKeepTabsHidden to false in the constructor - Add the following method after
ModifyTabStyle
void KeepTabsHidden(bool bKeepTabsHidden = true)
{
if(m_bKeepTabsHidden != bKeepTabsHidden)
{
m_bKeepTabsHidden = bKeepTabsHidden;
this->CalcTabAreaHeight();
if(m_bKeepTabsHidden)
{
this->OnRemoveLastTab();
}
else if(this->GetTabCtrl().GetItemCount() > 0)
{
this->OnAddFirstTab();
}
}
}
- In the implementation of SetTabAreaHeight, change:
if(m_nTabAreaHeight != nNewTabAreaHeight)
{
... to
if(m_bKeepTabsHidden)
{
m_nTabAreaHeight = 0;
}
else if(m_nTabAreaHeight != nNewTabAreaHeight)
{
...
Then somewhere in your main frame, you can call
m_tabbedClient.GetTabOwner().KeepTabsHidden(bToHideOrNotToHide);
You could also add a KeepTabsHidden method to the tabbed client class which called the tab owner's KeepTabsHidden. Then in your main frame you'd call m_tabbedClient.KeepTabsHidden(bToHideOrNotToHide);
HTH,
-Daniel
|
|
|
|
|
Thanks. When should we expect the next update?
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Paul Selormey wrote:
Thanks. When should we expect the next update?
When I have it ready I thought I'd have it by now, but it should be soon. Its a matter of finding the time to work on it
-Daniel
|
|
|
|
|
Hi Danien, me again
I added this code to my project, HydraIRC, and it works fine, thanks very much!
www.HydraIRC.com
|
|
|
|
|
Hello Dan,
How about support for the new Office 2003 tab style?
Thanks for the great work.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Paul Selormey wrote:
How about support for the new Office 2003 tab style?
Good idea. I'll put it on my TODO list
If you want to take a shot at doing it, make a new file, and make a new class for it. You can follow how the classes in DotNetTabCtrl.h inherit from CCustomTabCtrl and do their thing. Send it to me, and I'll include it in future updates
-Daniel
|
|
|
|
|
Daniel Bowen wrote:
If you want to take a shot at doing it, make a new file, and make a new class for it. You can follow how the classes in DotNetTabCtrl.h inherit from CCustomTabCtrl and do their thing. Send it to me, and I'll include it in future updates
I am planning a WTL project similar to the BCGSoft/CodeJock libraries and will use your codes. So I thought if I could get my request honored, I will save some time
The WTL docking library is not being updated and I plan to seek the author's permission to use it as part of the library. For now, I am trying to find time to study that. If you could spare a bit of time and knock it for me, I will be glad. Otherwise, I will surely have to work on it.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Hello
At first thanks for this framework! It looks really useful.
I'm beginner in Win32(MDI related) programming.
I've downloaded MDISplit demo http://www.codeproject.com/wtl/mdisplit.asp
and have tried to append tabbed MDI UI.
I've changed CMDIFrameWindowImpl to CTabbedMDIFrameWindowImpl,
CMDIChildWindowImpl to CTabbedMDIChildWindowImpl in all files.
But I can't see the tab bar - just empty non-drawn (with artefacts from other windows/controls) space. If you may please help - what should I do for fixing it?
There is a code that create splitter window - I tried to sligtly modify it:
HWND CMainFrame::CreateClient()
{
// Get the Client RECT for the entire window as a starting size
RECT rcClient;
GetClientRect(&rcClient);
// Create the vertical splitter. This is the main window
m_splitter.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
// Create the About dialog in the left pane
m_about.Create(m_splitter.m_hWnd);
m_splitter.SetSplitterPane(SPLIT_PANE_LEFT, m_about.m_hWnd);
// Create the MDI client in the right pane
m_hWndMDIClient = CreateMDIClient();
m_splitter.SetSplitterPane(SPLIT_PANE_RIGHT, m_hWndMDIClient);
// IMPORTANT! Make the splitter the parent of the MDI client
::SetParent(m_hWndMDIClient, m_splitter.m_hWnd);
//NEW CODE
m_tabbedClient.SetParent(m_splitter.m_hWnd); //am I right?
m_tabbedClient.SetTabOwnerParent(m_splitter.m_hWnd);
m_splitter.SetSplitterPos(132); // from left
// Splitter is ultimately the client of Main Frame (m_hWndClient)
return m_splitter.m_hWnd;
}
|
|
|
|
|
Vladimir Vunukov wrote:
I'm beginner in Win32(MDI related) programming.
I've downloaded MDISplit demo http://www.codeproject.com/wtl/mdisplit.asp
and have tried to append tabbed MDI UI.
I've changed CMDIFrameWindowImpl to CTabbedMDIFrameWindowImpl,
CMDIChildWindowImpl to CTabbedMDIChildWindowImpl in all files.
But I can't see the tab bar - just empty non-drawn (with artefacts from other windows/controls) space. If you may please help - what should I do for fixing it?
Sorry for not getting back sooner. I took a look at the MDISplit code. I was able to get it to work with my TabbedMDI stuff by doing the following:
- Add the necessary includes to get TabbedMDI.h to compile.
- Do not change CMDIFrameWindowImpl to CTabbedMDIFrameWindowImpl like you'd normally do.
- Declare
CTabbedMDIClient< CDotNetTabCtrl<CTabViewTabItem> > m_tabbedClient; as a member of CMainFrame. - In the
CreateClient function you quoted, add the line
m_tabbedClient.SubclassWindow(m_hWndMDIClient); after the line
::SetParent(m_hWndMDIClient, m_splitter.m_hWnd); - Find all instances of
CMDIChildWindowImpl and replace with CTabbedMDIChildWindowImpl (should be in ChildFrm.h). - You can also probably replace
CMDICommandBarCtrl in CMainFrame with CTabbedMDICommandBarCtrl (although I didn't test this much).
You might also want to consider using Sergey Klimov's docking window framework with my tabbing classes. See the DockingDemo from the samples with this article.
HTH,
-Daniel
|
|
|
|
|
|
Hi,
First of all thank you very much for writing such a fabulous control.
I found something wierd in your docking tab demo app,
To replay the bug:
First drag the docked tab window to make it float, and then drag a docked tab out of the tab window and make it float. Then double click the floating tab or drag it back to tab window. The tab will dock again but it is not correctly sized. One more funny thing is that the sizing problem only happens if the docking window is small enough. And once a tab is re-docked and sized together with the tab window, it will dock correctly from then on, no matter how small the tab window is.
I guess there must be something wrong with the initial coordinates of some rectangle.;)
Best Regards
Lei
|
|
|
|
|
jiang-lei wrote:
Hi,
First of all thank you very much for writing such a fabulous control.
You're welcome!
jiang-lei wrote:
To replay the bug:
First drag the docked tab window to make it float, and then drag a docked tab out of the tab window and make it float. Then double click the floating tab or drag it back to tab window. The tab will dock again but it is not correctly sized. One more funny thing is that the sizing problem only happens if the docking window is small enough. And once a tab is re-docked and sized together with the tab window, it will dock correctly from then on, no matter how small the tab window is.
I guess there must be something wrong with the initial coordinates of some rectangle.
Best Regards
Lei
Not to pass the buck , but this sounds like a bug with Sergey Klimov's docking window code. I've noticed several quirks along the same lines, and have sent him several suggestions. Be sure and read the replies on his article at http://www.codeproject.com/wtl/wtldockingwindows.asp.
Also take a look at this message I posted earlier to see if it helps: http://www.codeproject.com/wtl/TabbingFramework.asp?msg=429425&searchkw=docking&sd=4%2F11%2F2001&ed=7%2F10%2F2003#xx429425xx
Thanks,
-Daniel
|
|
|
|
|
Daniel (or anyone),
Beginning with the DockingDemo MDI sample app as the basis for my work, I am constructing a utility app that currently maintains two CTabbedFrameImpl derived Frame windows; Each frame window hosts multiple views embedded in the CTabbedFrameImpl frame using dotnet flat buttons. The frames are hosted in a CTabbedMDICommandBarCtrl object to provide frame switching via tabs;
In one of the Child Frame Windows I am using the opensource CodeMax (2.1) sytnax highlighting code editor in multiple views to allow simultaneous editing of configuration files (ala sysedit). These views are created dynamically in the ChildFrame OnCreate() and also even more dynamically via ID_FILE_OPEN handling. ID_FILE_OPEN is handled in the Child Frame and causes a new View to be created and added to the CTabbedFrameImpl tab bar.
This all works fine and is just background info to set the stage.
The problem I have is related to handling messages in the views associated with the Child Frame window.
In the Child Frame, my message map looks like this:
<br />
BEGIN_MSG_MAP_EX ( CFrameCodeMax )<br />
MSG_WM_CREATE ( OnCreate ) <br />
MSG_WM_SETFOCUS ( OnSetFocus )<br />
MSG_WM_FORWARDMSG ( OnForwardMsg )<br />
MSG_WM_CLOSE( OnClose )<br />
<br />
COMMAND_ID_HANDLER( ID_FILE_OPEN, OnFileOpen )<br />
<br />
CHAIN_CLIENT_COMMANDS ()<br />
CHAIN_MSG_MAP ( baseClass ) <br />
<br />
REFLECT_NOTIFICATIONS ()<br />
END_MSG_MAP ()<br />
baseClass (i.e., the Child Frame) is declared as:
<br />
typedef CTabbedFrameImpl< CFrameCodeMax, CDotNetButtonTabCtrl< CTabViewTabItem >, CTabbedMDIChildWindowImpl< CFrameCodeMax, CMDIWindow, CFrameCodeMaxWinTraits > > baseClass;<br />
and my winTraits are:
<br />
typedef CWinTraits< WS_OVERLAPPEDWINDOW | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZE, WS_EX_MDICHILD > CFrameCodeMaxWinTraits;<br />
In my Views, I have the following message map; reduced for clarity...
<br />
BEGIN_MSG_MAP_EX ( CViewCodeMax ) <br />
<br />
MSG_WM_CREATE ( OnCreate ) <br />
<br />
<br />
COMMAND_ID_HANDLER_EX ( ID_FILE_RELOAD, OnFileReload )<br />
COMMAND_ID_HANDLER_EX ( ID_FILE_SAVE, OnFileSave )<br />
COMMAND_ID_HANDLER_EX ( ID_FILE_SAVE_AS, OnFileSaveAs )<br />
<br />
...<br />
... lines removed for brevity ...<br />
... <br />
<br />
CHAIN_MSG_MAP_ALT ( CodeMaxControlNotifications < CViewCodeMax >, CMAX_REFLECTED_NOTIFY_CODE_HANDLERS )<br />
CHAIN_MSG_MAP_ALT ( CodeMaxControlCommands < CViewCodeMax >, CMAX_BASIC_COMMAND_ID_HANDLERS ) <br />
<br />
END_MSG_MAP () <br />
Okay, now let's see if I can explain my problem clearly...
In several of the COMMAND_ID_HANDLER_EX() macros you can see that I am handling messages that originate from the main menu, like ID_FILE_SAVE and others. In addition there are several notification messages that originate via the CodeMax
The problem behavior is that, depending on which tab in the dotnet button bar I have clicked on to activate a particular edit view, I would expect that View to process the messages from the UI - i.e., the Active view should handle the messages; But this is not the case; instead, the first view that I add to the Child Frame seems to handle the messages that originate in the UI, like Save File, while the last view that I add to the Child Frame seems to handle the control notifications from the CodeMax edit control. Both situations are not ideal; Instead I would prefer that the active view handle the messages;
One solution I have thought of would have me handling the message in the Child Frame, identifying the active view and then specifically calling public methods in the active view; i.e.,
int nActiveTab = this->GetTabCtrl().GetCurSel();
for( unsigned int i=0; i < m_vTabs.size(); i++ ) {
if( m_vTabs[i].nTab = nActiveTab )
m_vTabs[i].pView->SomeMethodCall();
}
While I can certainly do this, I tend to think that I am missing some aspect of ATL / WTL message maps that would allow this dynamic message handling to function properly.
It appears that the CHAIN_CLIENT_COMMANDS() in my Child Frame is not aware of the active view - do I have to set a the m_hWndClient variable somewhere when a Tab click causes a different view to become 'active'? If so, is there a message generated by the dotnet tab control that I can add to my message map in the Child Frame to enable this behaviour? I supposed I could do it during Idle processing somehow.
Apologies if I have not included enough code - I can provide more if that would help.
Thanks in advance for any tips / pointers.
Regards,
Scott Burkhalter
Principal Consultant
SoulTech Solutions
w: www.soulsolu.com e: scott@soulsolu.com
|
|
|
|
|
soultech wrote:
The problem behavior is that, depending on which tab in the dotnet button bar I have clicked on to activate a particular edit view, I would expect that View to process the messages from the UI - i.e., the Active view should handle the messages
If I understand correctly, there's a small bit of code that currently isn't there that needs to be added.
If you invoke a menu item from the main frame's menu, then there's a WM_COMMAND message sent to your main frame window. In WTL, you usually have the entry
CHAIN_MDI_CHILD_COMMANDS() in your main frame's message map. This asks for the active MDI child, and re-sends (or "forwards") the message on to that window. The MDI child is the "Child Frame". For an out of the box WTL MDI application, the child frame has a "client" or "view" window as a child. So when the frame gets a WM_COMMAND, it then forwards the message onto the view (a child of the frame). This is usually done with CHAIN_CLIENT_COMMANDS() in the frame window's message map.
soultech wrote:
It appears that the CHAIN_CLIENT_COMMANDS() in my Child Frame is not aware of the active view - do I have to set a the m_hWndClient variable somewhere when a Tab click causes a different view to become 'active'?
Close. CHAIN_CLIENT_COMMANDS comes from WTL, so it wouldn't know about the tabbing classes. Its meant for CFrameWindowImpl which has a member variable m_hWndClient for the one and only active view. With a CTabbedFrameImpl derived class, what you have instead of a single child as the view, you have one view (of possibly several) as the "active" one.
In your CTabbedFrameImpl derived class, you have access to all the base methods and properties. CTabbedFrameImpl tracks its active view with the member variable m_hWndActive . What you could do is have your own version that looked something like:
#define CHAIN_ACTIVETABVIEW_COMMANDS() \
if(uMsg == WM_COMMAND && m_hWndActive != NULL) \
::SendMessage(m_hWndActive, uMsg, wParam, lParam);
Also, if you look in the message map for CTabbedFrameImpl , you'll see that I already pass keyboard messages down to the active view (if they are sent to the frame). If there are other messages that need to make it down to the active view, you could do something similar. You don't want to just forward everything to the active view, because that would make a lot of things not work right (for example, you wouldn't want to forward WM_CREATE sent to the frame forwarded to the active view). That means, be very careful whenever you're using CHAIN_MSG_MAP, CHAIN_MSG_MAP_ALT, etc. - it's usually what you want when chaining to the message map of a base class, but often wrong if you use it to forward all messages meant for one window to go to another window.
I'll add the CHAIN_ACTIVETABVIEW_COMMANDS() macro to my next update, and have CTabbedFrameImpl use it. Forwarding WM_COMMAND messages on to the active view seems like it should always be OK (I'll add it just before the REFLECT_NOTIFICATIONS).
Also note that if you want the HWND corresponding to a tab, you can call GetItem(index) , and then GetTabView (assuming that the tab item type is CTabViewTabItem).
All this window messaging stuff can be made to work quite well with the tabbing stuff, but with an extra window in between, sometimes you have to think a little more about what's really going on Spy++ can help debug where messages are really going. Having the "PreTranslateMessage" / WM_FORWARDMSG stuff also takes special consideration, but it can work correctly.
If I missed something that you're still having a problem with, let me know.
-Daniel
|
|
|
|
|
Well, I'm glad I asked the question! I'll give the macro a try and see if that does the trick.
Thanks for your tip and once again, thanks for your efforts with the WTL community.
Care to share your thoughts on the features and fun you're adding to the next release? Inquiring minds want to know
Regards
Scott Burkhalter
Principal Consultant
SoulTech Solutions
w: www.soulsolu.com e: scott@soulsolu.com
|
|
|
|
|
FYI for anyone following this thread in the future: Dan's suggestion works - I added the CHAIN_ACTIVETABVIEW_COMMANDS() macro to the CTabbedFrameImpl base class implementation and then inserted it into the message map just above REFLECT_NOTIFICATIONS(). i.e.,
BEGIN_MSG_MAP(thisClass)
.
.
.
CHAIN_ACTIVETABVIEW_COMMANDS()
if(m_bReflectNotifications)
{
REFLECT_NOTIFICATIONS()
}
END_MSG_MAP()
The only caveat is that I did have to remove the CHAIN_CLIENT_COMMANDS() entry from the view's parent MDI Child Frame window mesage map. Otherwise, in addition to the now correct processing due to the suggested macro highlighted above, the CHAIN_CLIENT_COMMANDS() handler ends up sending the WM_COMMAND messages to the view it 'thinks' is active (whatever HWND m_hWndActive contains) which tends to be wrong most of the time.
I.e., if you have both macros in your implementation the command messages are processed two times:
1) by the child frame CFrameWindowImpl implementation, which passes it to the wrong active view via CHAIN_CLIENT_COMMANDS() in your Child Frame message map
2) by the tabbed frame CTabbedFrameImpl implementation, which properly passes to the current active view via CHAIN_ACTIVETABVIEW_COMMANDS() in the Child Frame's Tab base implementation
So, if you need to enable this functionality, and you start to see double message processing and odd behaviour, be sure to check that you have commented out the CHAIN_CLIENT_COMMANDS() handler in the views parent frame window.
Ciao,
Scott Burkhalter
Principal Consultant
SoulTech Solutions
w: www.soulsolu.com e: scott@soulsolu.com
|
|
|
|
|
soultech wrote:
.e., if you have both macros in your implementation the command messages are processed two times:
1) by the child frame CFrameWindowImpl implementation, which passes it to the wrong active view via CHAIN_CLIENT_COMMANDS() in your Child Frame message map
...
Just a small point of clarification. CFrameWindowImpl and its base class CFrameWindowImplBase don't do CHAIN_CLIENT_COMMANDS() , but often the class that derives from CFrameWindowImpl does. I'm assuming that its from the derived class where you pulled out CHAIN_CLIENT_COMMANDS - which is the correct thing to do
Also, I should have mentioned this before, but for an example of an MDI child window that is also a tabbed frame, see the "TabDemo", and the CHtmlFrame class. The current implementation of CHtmlFrame handles a couple WM_COMMAND messages itself, but with the new CHAIN_ACTIVETABVIEW_COMMANDS() in CTabbedFrameImpl, commands will make it down to the active view auto-magically
Thanks,
-Daniel
|
|
|
|
|
soultech wrote:
Care to share your thoughts on the features and fun you're adding to the next release? Inquiring minds want to know
I've meant to have an update much earlier than now, but I've been quite busy with deadlines and such. I have a list of outstanding issues brought up by comments to the article. I've also had my own TODO list since releasing the article.
Hopefully within a couple weeks I'll have an update. There are a number of things that will be included, and a couple that I would like to have, but won't this time around.
When I post the updates, I'll have history at the top of each file with what has changed. I'd give a list of what I'm hoping to get in, but for now, I hope you'll forgive me if I don't give that list
If you have suggestions, please e-mail then to me, or post them to the article.
-Daniel
|
|
|
|
|
Hi Daniel
Thanx for your good work
I wanna use CTabbedFrameImpl but without a tab control , I want to programmatically switch between views ( really with other controls )
good luck
sdsf
|
|
|
|
|
Sahbi wrote:
... without a tab control , I want to programmatically switch between views ...
I'd start out with a "frame" type window (which might just be a child window of another window). I'd then have other windows that are children of this frame window, and track which one is the "active" one. Essentially, you show the "active" view, and hide the other views. You fill the client area of the frame window with the active view. (BTW, if the frame window has no border, etc., you won't ever even see any part of the frame window).
CFrameWindowImpl (from WTL) does part of this, but it only has one window as the "client" window. It does the "fill the client area of the frame" with its UpdateLayout method, which gets called when handling WM_SIZE . UpdateLayout could also be called at other times (i.e., when switching active views).
Try examining the code for CTabbedFrameImpl, and debug through it (set breakpoints, etc.) to see how I do this with the tabs. Also run Spy++ to see the window hierarchy. The way I have it work with the tabs is that the tabs are drawn in a child window (sibling to the tab views) that is at the top or bottom of the "frame", and the active view is resized to the remaining client area of the frame. You could probably copy CTabbedFrameImpl, name your new class something else, remove the inheritance from CCustomTabOwnerImpl, remove all the other tab related stuff, and be pretty close to what you need
HTH,
-Daniel
|
|
|
|
|