|
Hi Daniel !
I'm very new in WTL programming and can't find what is wrong with my app. Pleeeeeeeease help !!!
In Your Docking demo example I put CListViewCtrl enstead of CPlainTextView and trying to get notify messages such as NM_RCLICK in the MainFrame message map. The messages seems not coming thru. If I try to catch them in class derivied from CListViewCtrl, I get the same result. So please please can U help me? If there a way to catch them in main frame it will be perfect...
Thanx
|
|
|
|
|
Notifications go to the window parent (from a windows hierarchy point of view). The main frame isn't your direct parent.
If you want the list view as an MDI child, then try creating an out of the box WTL MDI app, and choose the list view as the view type. You'll get a "frame" class, and the view itself. The frame class will be the direct parent of the list view in that case. You can then handle the notifications in the frame using NOTIFY_CODE_HANDLER and the like. Or have the frame reflect the notifications back to the list view (REFLECT_NOTIFICATIONS), and use REFLECTED_NOTIFY_CODE_HANDLER to handle the notifications, and also have DEFAULT_REFLECTION_HANDLER at the end of the list view's message map.
If you want the list view as a docked window, then you need to use the option to tell the pane window "frame" to reflect notifications (see the SetPaneView call), then handle things like above with REFLECTED_NOTIFY_CODE_HANDLER and DEFAULT_REFLECTION_HANDLER.
-Daniel
|
|
|
|
|
Thank you Daniel for the answer, but I didn't make it to work.
So if I understand it right in DockingDemo the member m_OutputFrame in MainFrame is a parent to CPlainTextView (CListViewCtrl in my case) members:
m_TaskListView.Create(m_OutputFrame, rcDefault, NULL, ...);
m_OutputFrame must reflect notifications and we must call something like
m_OutputFrame.SetReflectNotifications(true); somewhere after creation of m_OutputFrame. Right?
The CListViewCtrl class is derivied from CWindow, not CWindowImplRoot where DefaultReflectionHandler is. So DEFAULT_REFLECTION_HANDLER() will fail to compile if we put it in CListViewCtrl. Do I have to create a new one in my class?
If I compile without DEFAULT_REFLECTION_HANDLER(), like this:
BEGIN_MSG_MAP(MyListView)
char b[100];
sprintf(b, "%d", uMsg);
::MessageBox(0,b,"",0);
END_MSG_MAP()
...still no messages or notifications
If you want the list view as a docked window, then you need to use the option to tell the pane window "frame" to reflect notifications (see the SetPaneView call), then handle things like above with REFLECTED_NOTIFY_CODE_HANDLER and DEFAULT_REFLECTION_HANDLER.
I couldn't find SetPaneView functions anywhere (maybe it doesn't metter, because I just want it work like I described above).
|
|
|
|
|
It's working now !!!
My problem was in derivieding of CListViewCtrl, I had the following:
class MyListView : public CListViewCtrl // derivied from CWindow
{ ... }
after I derivied from CWindowImpl instead, it gets work:
class MyListView : public CWindowImpl<MyListView, CListViewCtrl>
Thank you Daniel, without You my app maybe never would see the light
|
|
|
|
|
How do MFC Porject(MDI or SDI) link ?
|
|
|
|
|
I noticed the other day that in Visual Studio.NET I can move the tab order around simply by dragging tabs along the bar. This is cool - and now I've noticed it I feel like I have to have it in my own app (along with the moon on a stick!) Have you ever implemented this, and if not what would be your estimate as to how complex it would be to do?
Also, while I'm here, I'm wondering if you could provide any insight on this problem: Having been working in VS.NET for a while I got used to the (seemingly really clever) tab-order management:
Select window 4.
Select window 2.
Ctrl-tab takes you to window 4. Release Ctrl.
Ctrl-tab takes you to window 2. Release Ctrl.
Ctrl-tab takes you to window 4.... and so on.
They basically seem to store a history of your last visited tabs and work through them in that order - instead of the seemingly barking mad order that the basic MDI framework provides you with.
Have you seen any neat code to solve this problem - perhaps a nice generic way or even better a method fitted into a lovely tab control MDI child manager <cheeky-grin>?
thanks in advance,
--
Simon Steele
Programmers Notepad - http://www.pnotepad.org/
|
|
|
|
|
Simon Steele wrote:
I noticed the other day that in Visual Studio.NET I can move the tab order around simply by dragging tabs along the bar. This is cool - and now I've noticed it I feel like I have to have it in my own app (along with the moon on a stick!) Have you ever implemented this, and if not what would be your estimate as to how complex it would be to do?
This has always been on my TODO list. You've probably already seen it, but Sergey Klimov has a general docking window approach that now supports grouping docked windows with tabs and dragging tabs around (just for docked windows though, not for MDI tabs) - http://www.codeproject.com/wtl/wtldockingwindows.asp. He even uses my tab stuff (that I helped him integrate)
I have an example of using his docking windows and my tab stuff in the "DockingDemo" that comes with the samples to this article. If you run the EXE I provided, you should see the functionality of dragging tabs around of docked windows, and docking them elsewhere even. You'll have some problems compiling the code with Sergey's current public release though, because in my samples, I didn't provide the version of his code with my suggestions and bug-fixes that I sent him a while back that "DockingDemo" depends on (that he hasn't officially released yet).
One thing that is still on my TODO list however is dragging MDI tabs around. This functionality doesn't really come with (or belong in) the docking window stuff. Dragging MDI tabs around might not make it into my next update, but I'll try to get it in there if I can.
Note that I also have several other minor updates to the tab stuff that will be in an upcoming update. I can't really say when exactly I'll have my next update of the tab stuff ready, because of some other looming deadlines, but I hope to within a month or two at the latest.
Simon Steele wrote:
Having been working in VS.NET for a while I got used to the (seemingly really clever) tab-order management .... They basically seem to store a history of your last visited tabs and work through them in that order - instead of the seemingly barking mad order that the basic MDI framework provides you with.
Yes, I've always liked how Ctrl-Tab works with MDI children in Visual Studio. I think you might be suprised that there's really only one small difference between the default MDIClient behavior and the MDI behavior in Visual Studio (as far as I can tell).
It's all about Z-Order. The default MDIClient handles Ctrl-Tab by cycling through the MDI children in Z-Order. Ctrl-Shift-Tab cycles in reverse. The other default accelerators with MDI children are Ctrl-F6 and Ctrl-Shift-F6, that in the default implementation seem to work just like Ctrl-Tab and Ctrl-Shift-Tab. With the default implementation, the newly activated MDI child goes to the top of the Z-Order, and the last active MDI child goes to the bottom of the Z-Order. The effect is if you let up on the Ctrl key, the default behavior just picks up cycling where you left off.
With Visual Studio, Ctrl-Tab, Ctrl-Shift-Tab, Ctrl-F6, Ctrl-Shift-F6 also cycle forwards and backwards through MDI children according to Z-Order. However, there is one important difference from the default MDIClient implementation. When you cycle through windows (using Ctrl-Tab, etc.), the Z-order of the newly activated MDI child still gets moved to the top of the Z-Order, but the MDI child losing focus doesn't get sent to the bottom of the Z-Order (which really makes more sense if you ask me). The effect is that you hit Ctrl-Tab once, and it takes you to the last active MDI child, which let's you quickly toggle back and forth between two MDI children. In the default implementation, to toggle back and forth like this, you'd have to use Ctrl-Tab and then Ctrl-Shift-Tab.
My TabbedMDI classes seem like a good place to provide the option of having things work the Visual Studio way. I'll try to get this into my next update as well
-Daniel
|
|
|
|
|
Hi Dan,
Great control, especially the MDI version which I'm using in a current project.
Thanks a bunch
|
|
|
|
|
rdheadrick wrote:
Hi Dan,
Great control, especially the MDI version which I'm using in a current project.
Thanks a bunch
You're welcome. Thanks for the kind words I'm using the code in a real project as well, so I'm always interested in bug reports and missing functionality (that might be general enough to work into the base classes)
-Daniel
|
|
|
|
|
When using the CDotNetButtonTabCtrl like in the TabDemo project (but with a RichEdit control as a replacement for the PlainTextView) I cannot get shortcut keys (Ctrl-C, Ctrl-V etc.) to work in the RichEdit control.
If I skip the CDotNetButtonTabCtrl and use the RichEdit control like in the SimpleTabbedMDIDemo, everything is working fine.
I suspect it's something in the PreTranslateMessage of the MainFrame, but it's only a wild n00b guess.
|
|
|
|
|
Tester Joe wrote:
When using the CDotNetButtonTabCtrl like in the TabDemo project (but with a RichEdit control as a replacement for the PlainTextView) I cannot get shortcut keys (Ctrl-C, Ctrl-V etc.) to work in the RichEdit control.
If I skip the CDotNetButtonTabCtrl and use the RichEdit control like in the SimpleTabbedMDIDemo, everything is working fine.
I suspect it's something in the PreTranslateMessage of the MainFrame, but it's only a wild n00b guess.
Yes, PreTranslateMessage does have something to do with it.
The "proper" way to handle Ctrl-C, Ctrl-V, etc. is to have them be "accelerators". This essentially turns key strokes into WM_COMMAND messages (with an ID like ID_EDIT_COPY, etc.). You then have your window handle these WM_COMMAND message (COMMAND_ID_HANDLER in the message map) and call (or SendMessage) the appropriate thing.
Where PreTranslateMessage gets involved is in recognizing the keystrokes as accelerators before the key messages are processed as usual. Take for example an out-of-the-box MDI application using the WTL wizard. One of the things you get is a CChildFrame class that inherits from CMDIChildWindowImpl. The DECLARE_FRAME_WND_CLASS macro is used to say what resource ID is used for the MDI child. There can be an icon with this resource ID, a menu, an accelerator table, and so on. When you create the window, WTL will try to do a LoadAccelerators with that ID to load the accelerator table (which gets used later as you'll see). Then you have the main frame registering itself so that it gets PreTranslateMessage calls. In the default implementation, it calls the base class CMDIFrameWindowImpl<cmainframe>::PreTranslateMessage(pMsg), and then does a SendMessage WM_FORWARDMSG to the active MDI child (if there is one). WM_FORWARDMSG is essentially a message-only way to call PreTranslateMessage (when you don't have C++ knowledge of the window, but know its HWND). The default handler for the MDI child's WM_FORWARDMSG calls CMDIChildWindowImpl's PreTranslateMessage, which calls TranslateAccelerator to see if the message triggers an accelerator. So in your MDI child, this is how you get WM_COMMAND with ID_EDIT_COPY when you hit Ctrl-C.
The problem that I bet your having is getting Ctrl-C, etc. to work for a window that is docked. My guess is that it doesn't even work with a normal edit box. In order for it to work:
- You'll need an accelerator table for the docked window
- You'll need to make sure that the main frame PreTranslateMessage is modified to give your docked window a chance (depending on focus for example)
- Your docked window needs to handle that PreTranslateMessage/WM_FORWARDMSG to call TranslateAccelerator. You also need to make sure that someone else doesn't steal the message before you get it (i.e., the main frame).
I'll update the TabDemo and DockingDemo for a future release to show how to do this, but for the meantime, change your CMainFrame::PreTranslateAccelerator in TabDemo to be something like:
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
if(pMsg == NULL)
{
return FALSE;
}
if(pMsg->hwnd == m_hWnd || pMsg->hwnd == m_hWndMDIClient)
{
if(baseClass::PreTranslateMessage(pMsg))
return TRUE;
}
HWND hWndFocus = ::GetFocus();
HWND hWndMDIActive = this->MDIGetActive();
if((hWndMDIActive == hWndFocus) || (::IsChild(hWndMDIActive, hWndFocus)))
{
if(baseClass::PreTranslateMessage(pMsg))
return TRUE;
if(hWndMDIActive != NULL)
{
return (BOOL)::SendMessage(hWndMDIActive, WM_FORWARDMSG, 0, (LPARAM)pMsg);
}
}
else if(m_OutputFrame.IsWindow() && m_OutputFrame.IsChild(hWndFocus))
{
if(m_OutputFrame.PreTranslateMessage(pMsg))
{
return TRUE;
}
}
return FALSE;
}
Notice how I depend on the focus here as to who gets a crack at doing their own PreTranslateMessage calls.
You'll also need to update the docked window, like the edit window (or other window that wants to deal with accelerators). Here's how you might change "PlainTextView.h" to explicitly create an accelerator table manually and use it (there's other ways to do this too - like having the accelerator table in a resource and doing LoadAccelerators):
class CPlainTextView:
public CWindowImpl<CPlainTextView, CEdit, CPlainTextViewWinTraits>,
public CEditCommands<CPlainTextView>
{
protected:
typedef CPlainTextView thisClass;
typedef CWindowImpl<CPlainTextView, CEdit, CPlainTextViewWinTraits> baseClass;
protected:
HACCEL m_hAccel;
WTL::CFont m_font;
public:
CPlainTextView() :
m_hAccel(NULL)
{
}
public:
DECLARE_WND_SUPERCLASS(NULL, CEdit::GetWndClassName())
BOOL PreTranslateMessage(MSG* pMsg)
{
if(pMsg)
{
if((pMsg->hwnd == m_hWnd) || ::IsChild(m_hWnd, pMsg->hwnd))
{
if(m_hAccel != NULL && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
{
return TRUE;
}
}
}
return FALSE;
}
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
if(uMsg == WM_FORWARDMSG)
if(PreTranslateMessage((LPMSG)lParam)) return TRUE;
CHAIN_MSG_MAP_ALT(CEditCommands<CPlainTextView>, 1)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
bHandled = TRUE;
this->InitializeFont();
this->CreateAccelerators();
return lRet;
}
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
this->DestroyAccelerators();
bHandled = FALSE;
return 0;
}
protected:
void InitializeFont(void)
{
CClientDC dc(m_hWnd);
LOGFONT lf = {0};
::ZeroMemory(&lf, sizeof(LOGFONT));
const int PointSize = 8;
lf.lfHeight = -MulDiv(PointSize, GetDeviceCaps(dc, LOGPIXELSY), 72);
::lstrcpy(lf.lfFaceName, _T("Courier New"));
m_font.Attach( ::CreateFontIndirect(&lf) );
this->SetFont(m_font);
CFontHandle hOldFont = dc.SelectFont(m_font);
TEXTMETRIC tm = {0};
dc.GetTextMetrics(&tm);
dc.SelectFont(hOldFont);
int nDialogUnitsX = 4*(tm.tmAveCharWidth / LOWORD(GetDialogBaseUnits()));
int nTabStops = 4*nDialogUnitsX;
this->SetTabStops(nTabStops);
}
void CreateAccelerators(void)
{
const int cAccel = 12;
ACCEL AccelTable[cAccel] = {
{FVIRTKEY | FCONTROL | FNOINVERT, 'A', ID_EDIT_SELECT_ALL},
{FVIRTKEY | FCONTROL | FNOINVERT, 'X', ID_EDIT_CUT},
{FVIRTKEY | FCONTROL | FNOINVERT, 'C', ID_EDIT_COPY},
{FVIRTKEY | FCONTROL | FNOINVERT, 'V', ID_EDIT_PASTE},
{FVIRTKEY | FCONTROL | FNOINVERT, 'F', ID_EDIT_FIND},
{FVIRTKEY | FNOINVERT, VK_F3, ID_EDIT_REPEAT},
{FVIRTKEY | FCONTROL | FNOINVERT, 'H', ID_EDIT_REPLACE},
{FVIRTKEY | FCONTROL | FNOINVERT, 'Z', ID_EDIT_UNDO},
{FVIRTKEY | FCONTROL | FNOINVERT, 'Y', ID_EDIT_REDO},
{FVIRTKEY | FSHIFT | FNOINVERT, VK_DELETE, ID_EDIT_CUT},
{FVIRTKEY | FCONTROL | FNOINVERT, VK_INSERT, ID_EDIT_COPY},
{FVIRTKEY | FSHIFT | FNOINVERT, VK_INSERT, ID_EDIT_PASTE}
};
m_hAccel = ::CreateAcceleratorTable(AccelTable, cAccel);
}
void DestroyAccelerators(void)
{
if(m_hAccel)
{
::DestroyAcceleratorTable(m_hAccel);
m_hAccel = NULL;
}
}
};
HTH,
-Daniel
|
|
|
|
|
Daniel's reply is good and comprehensive and I've noted it for my own purposes, but you can also apply a simple workaround for this particular problem (one that I use):
Create handlers for WM_COPY, WM_CUT, WM_PASTE etc. in your main frame, and then use simple code in each like this:
::SendMessage(::GetFocus(), WM_?, wParam, lParam);
and this will forward on the relevant message to the active window.
hope that helps,
--
Simon Steele
Programmers Notepad - http://www.pnotepad.org/
|
|
|
|
|
On XP this project works perfectly. On W2K I’ve got a problem.
Under XP, the text in the tabs are perfectly readable. On W2K, the text of the tab that is active is perfectly readable but the text in tabs that are not active are nearly impossible to read.
Is there a workaround?
Daniel, this is absolutely terrific code. I wish I had the money to hire you.
Maybe someday I will.
Ralph Shnelvar
Managing Partner
Information LLC
|
|
|
|
|
Ralph Shnelvar wrote:
Daniel, this is absolutely terrific code. I wish I had the money to hire you.
Maybe someday I will.
Ralph Shnelvar wrote:
On XP this project works perfectly. On W2K I’ve got a problem.
Under XP, the text in the tabs are perfectly readable. On W2K, the text of the tab that is active is perfectly readable but the text in tabs that are not active are nearly impossible to read.
Is there a workaround?
Interesting. I assume you're talking about CDotNetTabCtrl. With CDotNetTabCtrl I tried to match how the pane tabs and MDI tabs in Visual Studio .Net look. When you run Visual Studio .Net on the XP and the W2K machine, do my tabs look different than theirs? If they look different, please send me some screen shots. If they look the same, but you don't like the look, here's a couple of options:
- FYI, The tab colors (active, inactive, background, etc.) in CDotNetTabCtrl (and the tabs in VS.Net) depend on the following system settings:
- SPI_GETICONTITLELOGFONT - SystemParametersInfo (The "Icon" font)
- COLOR_BTNFACE - GetSysColor (3D Objects)
- COLOR_BTNTEXT - GetSysColor (3D Objects)
- COLOR_BTNSHADOW - GetSysColor (3D Objects)
- COLOR_GRAYTEXT - GetSysColor (3D Objects)
- COLOR_BTNHIGHLIGHT - GetSysColor (Selected Items)
- COLOR_HOTLIGHT - GetSysColor (Selected Items)
- COLOR_APPWORKSPACE - GetSysColor (Application Background)
- The color bits per pixel (it does something different if there is less than 8bpp)
- Try adjusting "Appearance" settings (Control Panel -> Display -> Appearance). Under W2K, you'll see "Scheme" and also "Item". Under XP you'll see "Windows and buttons" (to choose either XP themes or "classic" style), "Color Scheme", "Font Size", and under advanced, you'll see the "Item" things you can individually change.
- CDotNetTabCtrl supports doing "Custom Draw" just like you do it with list views and tree views (CCustomTabCtrl supports custom draw in general, but the specific tab controls have to as well in order for it to change the appearance). If you have control of the parent window, you could respond to the NM_CUSTOMDRAW notification and choose different colors for things (see the article for a brief explanation).
HTH,
-Daniel
|
|
|
|
|
> Daniel wrote: Interesting. I assume you're talking about CDotNetTabCtrl. With CDotNetTabCtrl I tried to match how the pane tabs and MDI tabs in Visual Studio .Net look. When you run Visual Studio .Net on the XP and the W2K machine, do my tabs look different than theirs?
Daniel, yes, I'm talking about CDotNetTabCtrl. Sorry, should have been clearer.
Let me say that I am something of a novice when it comes to GUI programming (although I am a strong C++ programmer as well as something of a systems-level programmer). Thus some of what you wrote is not clear to me.
I have W2K and XP installed on the same machine on different partitions.
I have VC++6 installed on W2K and .Net on XP. I don't use .Net much because I lose Brief emulation as the text editor.
What can I do to satisfy your query about different looking tabs? Sorry, I’m not sure I understand what you want me to do.
I found that the source of my “trouble” is roughly at line 238 in DotNetTabCtrl where you issue
m_hbrBackground = CDCHandle::GetHalftoneBrush();
m_clrInactiveTab = ::GetSysColor(COLOR_GRAYTEXT);
Violating all good rules of OOP programming, I went in and replaced your code with
extern unsigned int _winmajor;
if(_winmajor == 6)
{
// if XP
m_hbrBackground = CDCHandle::GetHalftoneBrush();
m_clrInactiveTab = ::GetSysColor(COLOR_GRAYTEXT);
}
else
{
m_hbrBackground.CreateSolidBrush(m_clrBackground);
m_clrInactiveTab = ::GetSysColor(COLOR_BTNFACE);
}
which then cleared my problem.
Would you please point me at a WTL example of owner draw so that I do things “right.”?
Ralph Shnelvar
Managing Partner
Information LLC
|
|
|
|
|
Ralph Shnelvar wrote:
Sorry, I’m not sure I understand what you want me to do.
Sorry if I wasn't very clear
Ralph Shnelvar wrote:
I found that the source of my “trouble” is roughly at line 238 in DotNetTabCtrl where you issue
m_hbrBackground = CDCHandle::GetHalftoneBrush();
m_clrInactiveTab = ::GetSysColor(COLOR_GRAYTEXT);
That's a good clue. That little bit of code only gets called if your screen resolution is set so that the color depth isn't greater than 256 colors (8bpp). What do you have as your settings - i.e., 1600x1200, True Color (32 bit)? What does the line
int nBitsPerPixel = dcWindow.GetDeviceCaps(BITSPIXEL);
before the line you mentioned return for nBitsPerPixel?
Windows XP doesn't let you choose less than 16bpp (although it is possible if you know how), but Windows 2000 lets you choose down to 8bpp (256 colors).
...
OK, I just tried the code on Windows 2000 with the color depth set to "256 colors". You're right - it looks terrible I never personally run with less than 16bpp unless I'm testing something out, so I've missed this problem. I thought I had tested this out, but from the looks of it I probably missed it I ran Visual Studio .Net under 256 colors, and the tabs looks much better than what I have (the text for inactive tabs is white on a gray background).
I'll work on getting the drawing to look decent when your color depth is 256 colors or less for my next update. I had done quite a bit of testing with Visual Studio to try to figure out how to match their colors for various system settings. I'll have to do some of those tests again running with 256 colors.
Ralph Shnelvar wrote:
Would you please point me at a WTL example of owner draw so that I do things “right.”?
There's a bit to learn with owner draw and custom draw if you've never done it before. For now, go ahead and fix your copy of the code so that things look better
If you'd like to know more about Custom Draw, most of the samples are either in MFC or SDK style, so you have to convert to WTL. There are some that are WTL as well. Take a look at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/custdraw/custdraw.asp (SDK style)
http://www.codeproject.com/listctrl/lvcustomdraw.asp (MFC style)
http://www.codeproject.com/wtl/customdrawlist_wtl.asp (WTL style)
And thanks for the bug report!
-Daniel
|
|
|
|
|
Dear Daniel:
I apologize for taking so long to get back to you. It’s not been a quiet week in Lake Wobegon.
First, I want to say thank you for your kindness and patience. Second, part of the reason that it took me so long to get back to you is that I was waiting for an email in my inbox saying that you had responded. I never got that email.
Let me impose on you a bit more.
Could I ask you to please email me. Depending on finances and distance I may want to contract with you for some telephone support. No guarantees …
I also would like to learn how to post to this list with all the fancy markups that you use.
Is it nothing more than HTML text (that I might be able to produce with Word 2000)? Or is it something else since “everyone” seems to use the same kind of style.
I’m thinking about writing a little monograph about my experiences with ATL. Do you think people would be interested and where would the best place to pos it be?
Ralph Shnelvar
Managing partner
Information LLC
|
|
|
|
|
Ralph Shnelvar wrote:
First, I want to say thank you for your kindness and patience. Second, part of the reason that it took me so long to get back to you is that I was waiting for an email in my inbox saying that you had responded. I never got that email.
You're welcome.
I've noticed that I haven't gotten e-mail notifications on some things as well (must be a problem with CodeProject's notification stuff). There's a couple that did eventually come, but took a long time. There's others that never came at all.
Ralph Shnelvar wrote:
Could I ask you to please email me
Sure. When you see a codeproject message, if there's an "E-mail" link, you can click on it, and it will use codeproject to send an e-mail to the direct e-mail of the person, with your own e-mail as the reply-to address (if you're logged in).
Ralph Shnelvar wrote:
I also would like to learn how to post to this list with all the fancy markups that you use.
There's a bit of info about doing this on the pages that talk about submitting codeproject articles: http://www.codeproject.com/info/submit.asp. You can also get to that page by going to the home page, and under "Join CodeProject!" follow the link for "Adding and updating articles". Essentially, you can use HTML markup, and content in <pre> / <code> tags get syntax colorized.
Ralph Shnelvar wrote:
I’m thinking about writing a little monograph about my experiences with ATL. Do you think people would be interested and where would the best place to pos it be?
On CodeProject, you could start by posting a message to the ATL forum (click on "Message Boards" on the top right of the home page - the ATL/WTL/STL forum is http://www.codeproject.com/script/comments/forums.asp?forumid=4486). You could also post a message to the ATL mailing list (http://discuss.develop.com/atl.html) or the WTL mailing list (http://groups.yahoo.com/group/wtl/). You could also try writing up an article for codeproject.
-Daniel
|
|
|
|
|
When I cascade the child windows, I would like to hide the "main" tabbed toolbar (the one with the mdi child titles). Is there an easy way to do this?
Thanks,
Gordon.
|
|
|
|
|
Schmoo2k wrote:
When I cascade the child windows, I would like to hide the "main" tabbed toolbar (the one with the mdi child titles). Is there an easy way to do this?
You'll probably want to have your own class that inherits from CTabbedMDIClient (who subclasses the MDIClient window). You also will probably want a new class that inherits from CMDITabOwner, and add methods that do essentially what "OnAddFirstTab" and "OnRemoveLastTab" do, or you could just call those directly.
When your CTabbedMDIClient derived class receives WM_MDICASCADE, you'll then call either "OnRemoveLastTab", or your own CMDITabOwner derived class's method that does the same thing as CMDITabOwner::OnRemoveLastTab. If you also wanted to do this when an MDI child goes from maximized to restored, also handle the special registered message UWM_MDICHILDUNMAXIMIZED.
If you want to restore the tabs when a child window is maximized, then have your CTabbedMDIClient derived class handle the registered message "UWM_MDICHILDMAXIMIZED", and the call either "OnAddFirstTab", or your own CMDITabOwner derived class's method that does the same thing as CMDITabOwner::OnAddFirstTab.
In order to get UWM_MDICHILDMAXIMIZED and UWM_MDICHILDUNMAXIMIZED, be sure your MDI child frames inherit from CTabbedMDIChildWindowImpl.
-Daniel
|
|
|
|
|
Since Child Max/Restore is global for all child mdi's (either all maxed or all restored/iconized), I was thinking more of checking the state of the MDIActive on a ONIdle message and shrinking/hiding the mditabowner size to 0, any thoughts?
Thanks again,
Gordon.
|
|
|
|
|
Schmoo2k wrote:
Since Child Max/Restore is global for all child mdi's (either all maxed or all restored/iconized), I was thinking more of checking the state of the MDIActive on a ONIdle message and shrinking/hiding the mditabowner size to 0, any thoughts?
You could do it like that using OnIdle, but you can also safely depend on UWM_MDICHILDMAXIMIZED and UWM_MDIUNCHILDMAXIMIZED as long as the MDI child windows derive from CTabbedMDIChildWindowImpl (which they need to anyway if you want a tab to show up for the window). OnIdle gets called a lot, so it would actually be better to not do it there if you could help it.
There was already a need for knowing when an MDI child was maximized, and when it became not maximized - so you can take advantage of the code that's already there to handle this This was needed so that you could have the option of having the document icon and min/restore/max buttons show up in the command bar when a child is maximized, and taken away when no child was maximized (if you called UseMaxChildDocIconAndFrameCaptionButtons(true) on CTabbedMDICommandBarCtrl). I have the MDI child window send UWM_MDICHILDMAXIMIZED and UWM_MDIUNCHILDMAXIMIZED to the MDIClient as appropriate (see the WM_SIZE handler for CTabbedMDIChildWindowImpl).
There was also already a need for hiding and showing the MDI tabs - showing the tabs when the first tab was added, and hiding the tabs when the last tab was removed. So you can take advantage of the bit of code in there to do that too
Maybe I'll add a "HideMDITabsWhenMDIChildNotMaximized" property you can set in a future update
-Daniel
|
|
|
|
|
Ok, that makes sense...
fwiw this is what I plan on doing:
1. Derive the CTabbedMDIChildWindowImpl to implement:
a. "HideMDITabsWhenMDIChildNotMaximized"
b. Add a min + restore buttons to the toolbar (same as the UseMaxChildDocIconAndFrameCaptionButtons, but just in a more intuitive place - imo).
Thanks for the pointers
Gordon.
ps still new to all this <g>.
|
|
|
|
|
Ok, finally getting around to playing with this (I think this is more an API question)...
1. I derived CTabbedMDIClient and added handlers for UWM_MDICHILDMAXIMIZED and UWM_MDIUNCHILDMAXIMIZED.
2. On each I called either the AddFirst and RemoveLast.
3. All went well until the user switched windows via ctrl+f6 or by clicking on your tab control.
4. While switching windows, you get sent a UWM_MDICHILDMAXIMIZED for the new window with focus and a UWM_MDIUNCHILDMAXIMIZED for the old window losing focus, using logic from "2" I then hide the tabs window!!!
5. I hacked around the above with the following, is there a more polite method?
Gordon.
---code---
class CMyTabbedMDIClient : public CTabbedMDIClient<CDotNetTabCtrl<CTabViewTabItem> >
{
typedef CMyTabbedMDIClient thisClass;
typedef CTabbedMDIClient<CDotNetTabCtrl<CTabViewTabItem> > baseClass;
protected:
signed m_show;
public:
CMyTabbedMDIClient()
{
m_show = 0;
}
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(UWM_MDICHILDMAXIMIZED, OnMdiChildMaximized)
MESSAGE_HANDLER(UWM_MDICHILDUNMAXIMIZED, OnMdiChildUnMaximized)
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
LRESULT OnMdiChildMaximized(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = false;
if (++m_show == 1)
m_MdiTabOwner.OnAddFirstTab();
return 0;
}
LRESULT OnMdiChildUnMaximized(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = false;
if (--m_show == 0)
m_MdiTabOwner.OnRemoveLastTab();
ATLASSERT(m_show >= 0);
return 0;
}
};
|
|
|
|
|
Daniel,
First of all, great work with these classes.
I finally got around to upgrading to VC7 and had some trouble building my app because both ATL and WTL define _U_STRINGorID among other things. When I defined _WTL_NO_UNION_CLASSES in order to let ATL lead, I noticed that you explicitly reference WTL::_U_STRINGorID. Is there a reason for this? I changed it to a plain _U_STRINGorID and that works just fine.
-Anatoly
Anatoly Ivasyuk is co-founder of DTLink Software, a company specializing in Internet software and technologies. He is the author of DTLink's Windows products: AnswerTool, FAQTool, AppUpdate, and Personal Stock Monitor
|
|
|
|
|