|
I'll put the fix in when I update the code next.
-Daniel
|
|
|
|
|
I really like these components, but there are a few things I have come across:
1. When you have _[A/W]TL_NO_AUTOMATIC_NAMESPACE defined, the code fails to compile because CWindow, CListViewCtrl, etc. are not namespace qualified.
2. I have the ability to drag & drop files onto the MDI client area (even when there is a child window active with its own client. In order to support this, I implement WM_DROPFILES in my TabbedMDI frame class and add:
DragAcceptFiles( m_hWndClient, TRUE );
but this requires the following changes to work:
TabbedMDI.h(1532)
MESSAGE_HANDLER(WM_DROPFILES, OnDropFiles)
TabbedMDI.h(1568)
LRESULT OnDropFiles(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return GetParent().SendMessage(uMsg, wParam, lParam);
}
3. I find it useful to access the tab control from the frame class, so in my frame class I have:
BaseClass::TClient::TTabCtrl & GetTabControl()
{
return m_tabbedClient.GetTabOwner().GetTabCtrl();
}
to save typing!
4. There is an issue with ListViewNoFlicker.h on Win2000 - if the listview header has items that don't extend the length of the listview control, the remaining area isn't drawn properly. This can be fixed by changing the DoPaint implementation:
void DoPaint(CDCHandle dc, RECT& rcClip)
{
T* pT = static_cast<t*>(this);
if(m_headerCtrl.IsWindow())
{
// Draw the header first...
m_headerCtrl.SendMessage(WM_ERASEBKGND, (WPARAM)(HDC)dc, 0);
m_headerCtrl.SendMessage(WM_PAINT, (WPARAM)(HDC)dc, 0);
m_headerCtrl.ValidateRect(&rcClip);
// Prevent the header being drawn over...
CRect rcHeader;
m_headerCtrl.GetClientRect(&rcHeader);
dc.ExcludeClipRect(&rcHeader);
}
// Now draw the listview...
pT->DefWindowProc( WM_ERASEBKGND, (WPARAM)(HDC)dc, 0);
pT->DefWindowProc( WM_PAINT, (WPARAM)(HDC)dc, 0);
}
Reece Haston Dunn
Software Engineer, Sophos
Web: www.sophos.com
Sophos - protecting businesses against viruses and spam
|
|
|
|
|
Thanks!
rhdunn wrote:
1. When you have _[A/W]TL_NO_AUTOMATIC_NAMESPACE defined, the code fails to compile because CWindow, CListViewCtrl, etc. are not namespace qualified.
Sure, I can namespace qualify those things. Do you use that setting(s)?
rhdunn wrote:
2. I have the ability to drag & drop files onto the MDI client area ...
So, it worked before, but using CTabbedMDIClient it doesn't work? If you were subclassing the MDI client to handle WM_DROPFILES, you could still subclass the MDI client after CTabbedMDIClient subclasses it, and handle WM_DROPFILES there (using CContainedWindow or something).
rhdunn wrote:
3. I find it useful to access the tab control from the frame class, so in my frame class I have: ...
I'll add a "GetMDITabCtrl" to CTabbedMDIFrameWindowImpl that does this.
rhdunn wrote:
4. There is an issue with ListViewNoFlicker.h on Win2000 - if the listview header has items that don't extend the length of the listview control, the remaining area isn't drawn properly. ...
If you just add
m_headerCtrl.SendMessage(WM_ERASEBKGND, (WPARAM)(HDC)dc, 0); before the paint message to the header, does it also work? Like so:
inline void DoPaint(CDCHandle dc, RECT& rcClip)
{
T* pT = static_cast<T*>(this);
pT->DefWindowProc( WM_ERASEBKGND, (WPARAM)(HDC)dc, 0);
pT->DefWindowProc( WM_PAINT, (WPARAM)(HDC)dc, 0);
if(m_headerCtrl.IsWindow())
{
m_headerCtrl.SendMessage(WM_ERASEBKGND, (WPARAM)(HDC)dc, 0);
m_headerCtrl.SendMessage(WM_PAINT, (WPARAM)(HDC)dc, 0);
m_headerCtrl.ValidateRect(&rcClip);
}
}
Or do you have to switch things around like you've done and redraw the header first, exclude the header from drawing, then redraw the list for the problem to be fixed?
Thanks!
-Daniel
|
|
|
|
|
Daniel Bowen wrote:
rhdunn wrote:
1. When you have _[A/W]TL_NO_AUTOMATIC_NAMESPACE defined, the code fails to compile because CWindow, CListViewCtrl, etc. are not namespace qualified.
Sure, I can namespace qualify those things. Do you use that setting(s)?
I use them quite regularly: I am not a huge fan of "using namespace ..." . Note that you may have to use _CSTRING_NS::CString instead of ATL::CString or WTL::CString.
Daniel Bowen wrote:
rhdunn wrote:
2. I have the ability to drag & drop files onto the MDI client area ...
So, it worked before, but using CTabbedMDIClient it doesn't work? If you were subclassing the MDI client to handle WM_DROPFILES, you could still subclass the MDI client after CTabbedMDIClient subclasses it, and handle WM_DROPFILES there (using CContainedWindow or something).
I ran into this problem a while back, so I can't remember the exact details. As far as I can remember you are correct, but I will have another look.
Daniel Bowen wrote:
rhdunn wrote:
4. There is an issue with ListViewNoFlicker.h on Win2000 - if the listview header has items that don't extend the length of the listview control, the remaining area isn't drawn properly. ...
If you just add
m_headerCtrl.SendMessage(WM_ERASEBKGND, (WPARAM)(HDC)dc, 0);
before the paint message to the header, does it also work?
I tried that and other things. Your suggestion causes the entire listview to be erased with the header control The way I posted was the only one that worked correctly
Thanks for a great library .
Regards,
Reece
|
|
|
|
|
Hi,
In the TabDemo there is an IE control used for a new tab window. But I have noticed that when you switch tabs or create a new tab, the focus does not go to the IE control? As in, on a long a page its not possible to use the mouse wheel straight away, you have to click inside the new tab window. How would I give the IE control focus in these two cases?
Thanks.
|
|
|
|
|
Anonymous wrote:
In the TabDemo there is an IE control used for a new tab window. But I have noticed that when you switch tabs or create a new tab, the focus does not go to the IE control? As in, on a long a page its not possible to use the mouse wheel straight away, you have to click inside the new tab window. How would I give the IE control focus in these two cases?
I looked into this a little, and it turns out to be an annoyance that a lot of other people have when hosting the IE web browser control. The problem essentially is that there are multiple windows involved, and IE doesn't forward the focus like it should. Take the TabDemo example. The hierarchy looks like this:
HTML Frame (MDI child window frame)
Tab Control (sibling to tab views, not the parent)
Source View (edit control)
HTML View (hosting the web browser control)
Shell Embedding
Shell DocObject View
Internet Explorer_Server
So the when you switch between the MDI tabs, the code will set focus to the MDI child frame. The MDI child frame happens to be using tabs as well, but it appropriately sets focus to the active tab view, which in this case is the HTML View. In the base class code for the HTML view is the ActiveX control hosting code that will in turn set focus on its child window that's the ActiveX control. This is the Shell Embedding window. However, the Shell Embedding window is not appropriately setting focus to the "Internet Explorer_Server" window. When you click on the web page, then the "Internet Explorer_Server" window will get focus like you'd expect.
So to deal with this, you take over forwarding the focus in response to WM_SETFOCUS in the HTML View. Here's 2 different approaches that seem to work. Each has a couple variations. It seems to me that 1b is generic enough that it could even be used in the generic control hosting base class. For all of them, add a WM_SETFOCUS handler to CHtmlView.
Option 1, via interface calls:
a. Explicitly use IHTML* interfaces:
LRESULT OnSetFocus(UINT , WPARAM , LPARAM , BOOL& bHandled)
{
CComPtr<IWebBrowser2> webBrowser;
this->QueryControl(&webBrowser);
if(webBrowser)
{
CComPtr<IDispatch documentDisp;
webBrowser->get_Document(&documentDisp);
CComQIPtr<IHTMLDocument2> htmlDocument2(documentDisp);
if(htmlDocument2)
{
bool handled = false;
CComPtr<IHTMLElement> activeElement;
htmlDocument2->get_activeElement(&activeElement);
CComQIPtr<IHTMLElement2> activeElement2(activeElement);
if(activeElement2)
{
handled = true;
activeElement2->focus();
}
if(!handled)
{
CComPtr<IHTMLWindow2> parentWindow;
htmlDocument2->get_parentWindow(&parentWindow);
if(parentWindow)
{
parentWindow->focus();
}
}
}
}
bHandled = FALSE;
return 0;
}
b. Use more generic ActiveX control interfaces
CComPtr<IOleObject> oleObject;
this->QueryControl(&oleObject);
if(oleObject)
{
CComPtr<IOleClientSite> oleClientSite;
this->QueryHost(&oleClientSite);
if(oleClientSite)
{
CRect rcControl;
this->GetClientRect(&rcControl);
oleObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, oleClientSite, 0, m_hWnd, &rcControl);
}
}
Option 2, via Win32 calls:
a. Use the window names and hierarchy.
LRESULT OnSetFocus(UINT , WPARAM , LPARAM , BOOL& bHandled)
{
HWND hWndFocus = ::GetFocus();
HWND hWndShellEmbedding = ::FindWindowEx(m_hWnd, NULL, _T("Shell Embedding"), NULL);
if(hWndShellEmbedding)
{
HWND hWndShellDocObjectView = ::FindWindowEx(hWndShellEmbedding, NULL, _T("Shell DocObject View"), NULL);
if(hWndShellDocObjectView )
{
HWND hWndInternetExplorerServer = ::FindWindowEx(hWndShellDocObjectView, NULL, _T("Internet Explorer_Server"), NULL);
if(!::IsChild(hWndInternetExplorerServer, hWndFocus))
{
::SetFocus(hWndInternetExplorerServer);
}
}
}
bHandled = FALSE;
return 0;
}
b. Use the window hierarchy
HWND hWndFocus = ::GetFocus();
HWND hWndShellEmbedding = ::GetWindow(m_hWnd, GW_CHILD);
if(hWndShellEmbedding)
{
HWND hWndShellDocObjectView = ::GetWindow(hWndShellEmbedding, GW_CHILD);
if(hWndShellDocObjectView )
{
HWND hWndInternetExplorerServer = ::GetWindow(hWndShellDocObjectView, GW_CHILD);
if(!::IsChild(hWndInternetExplorerServer, hWndFocus))
{
::SetFocus(hWndInternetExplorerServer);
}
}
}
In the next updates I do, I'll update the sample code to deal with this situation.
HTH,
-Daniel
|
|
|
|
|
As a follow up to this, atlhost.h seems to have some kind of a bug, because it already has the code to do the OLEIVERB_UIACTIVATE in response to WM_SETFOCUS (essentially like the 1b option I posted). The thing that's failing it seems to be that the OLEIVERB_UIACTIVATE is not done if m_bUIActive is on. The very first WM_SETFOCUS call ends up doing the activate, but then the de-activate never happens until the window goes away. So subsequent WM_SETFOCUS handling does not do the OLEIVERB_UIACTIVATE again.
It seems that the check for m_bUIActive during OnSetFocus could be removed so that the activate always happens (which is essentially what I had for the code above). I'm not sure what that might break for other ActiveX controls though.
-Daniel
|
|
|
|
|
Hello
First thanks for the good job .
I tried to have a docking window in a MDI child window (as well as in the main frame) and started to change CChildFrame accordingly. After a lot of blood sweat and tears I got my code compiled but it brought up lots of assertions. The first was caused by the static pThis in the CDockingFocusHandler class. But it did not end there...
My question: Is it possible to have docking windows in MDI childs or not? If yes could somebody please give me a hint of how to proceed?
Thanks in advance
Thomas
|
|
|
|
|
Thomas Mx. wrote:
Is it possible to have docking windows in MDI childs or not? If yes could somebody please give me a hint of how to proceed?
In theory, yes it's possible to have docking windows in MDI children. But in practice, with Sergey's code, I don't know how much success you'll have. If you search the comments here (and on his article), I think there was at least one other person that tried to do this, but with the code as it stands it didn't work right. I've never personally tried though.
However, Sergey's code is not the only docking window code. If you're interested in starting from scratch, take a look at this tutorial for doing "docking toolbars" with straight Win32 code on http://www.catch22.net[^] - part 1[^], part 2[^]. Also, follow the links in part 2 for other people's take on his code. If you end up with something good, I'm sure there's lots of people that would be interested.
-Daniel
|
|
|
|
|
I've just managed to do this ... the upshot is that you need to make an intermediate window derived from CDockingSiteImpl. This becomes the client of the MDI window, and then you put the actual view window (or whatever) as the child of the intermediate window.
The code I have (in a very abbreviated form) is this:
DOCKING INTERMEDIARY:
class CDockingIntermediary
: public dockwins::CDockingSiteImpl<CDockingIntermediary>
{
typedef dockwins::CDockingSiteImpl<CDockingIntermediary> baseClass;
public:
DECLARE_WND_CLASS(_T("CDockingIntermediary"))
BEGIN_MSG_MAP(CCPDockView)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
public:
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
InitializeDockingFrame(CStyle::sIgnoreSysSettings | CStyle::sGhostDrag);
return 0;
}
void setClientWnd(HWND hWnd)
{
m_hWndClient = hWnd;
}
};
MDI FRAME WINDOW:
class CMyFrame
: public CTabbedMDIChildWindowImpl<CAppointmentsFrame, CMDIWindow>
/* other bases */
{
public:
CDockingIntermediary dockSite;
CMyView viewWnd;
CalendarDock miniCal;
BEGIN_MSG_MAP(CAppointmentsFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
/* chaining, etc */
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
m_hWndClient = dockSite.Create(m_hWnd,
rcDefault,
NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
UpdateLayout();
CRect rcDef(0,0,400,100);
m_view.Create(dockSite,
rcDef,
NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
dockSite.setClientWnd(m_view);
miniCal.Create(dockSite, rcDef, "Calendar", dwStyle);
SIZE sz = _miniCal._cal.GetMaxSize(1, 3);
m_dockSite.DockWindow(miniCal,
dockwins::CDockingSide(dockwins::CDockingSide::sLeft),
0, /*nBar*/
float(0.0), /*fPctPos*/
sz.cx, /*nWidth*/
sz.cy); /* nHeight*/
return 0;
}
};
I haven't included the view window, but if you've built a project using a wizard, you should already have one.
This is OK as far as it goes, but the CWindowStateMgr I'm using (from Sergey's library) is crashing when I try to Restore after closing and storing. So maybe it's not an ideal solution ... but it's working well enough for the moment.
Phil.
--
All things considered, you can't really consider all things ...
|
|
|
|
|
in dockingframe.h locate like that:
public:
CDockingFrameImplBase()
:m_vPackage(false),m_hPackage(true)
{
}
change: m_vPackage(false),m_hPackage(true)
to: m_vPackage(true),m_hPackage(false)
maybe can solved your question.
|
|
|
|
|
Hi
I wonder if you have any idea how your tabs could be converted to do the mouseover tricks (orange hilight) that XP tabs normally do
I had a (quick) look at UxTheme.dll but couldn't find an obvious way to do this with tabs
thanks
nikos
|
|
|
|
|
umeca74 wrote:
I wonder if you have any idea how your tabs could be converted to do the mouseover tricks (orange hilight) that XP tabs normally do
I do have the tabs setup to do "hot tracking" so that you can do special drawing for the tab under the mouse as the mouse moves over the tabs. However, since the tabs do things from scratch instead of customizing the common controls' tab control, you don't get the orange highlight thing for free.
To see the hot tracking in action, run the TabDemo or DockingDemo example, bring up a document window, then check out the tabs at the bottom for "HTML" and "Source". Move your mouse over them, and notice how the text changes color.
To do hot tracking, you first need to give the tab control the style "CTCS_HOTTRACK". You can then either use custom draw in the tab owner class, or inherit from one of the tab controls like CDotNetTabCtrlImpl and override the drawing. You can just handle the case when the item state has CDIS_HOT set, and let the default happen for other times. You can then draw the orange highlight, and maybe even using something like DrawThemeBackground with TABP_TABITEM and TIS_HOT (I'm not sure which "part" is the right one though if you do this). If you use custom draw, you could just draw on top of what's there for the item during the CDDS_ITEMPOSTPAINT draw stage.
HTH,
-Daniel
|
|
|
|
|
I've made a little modification of this framework in order to get the Visual 2005 tab look:
Screenshot here...[^]
Please mail for any critics:
rlesavre[at]ece.fr
|
|
|
|
|
Hi,
Looks good, are you going to make your changes available for others to use?
thanks,
Simon.
--
Simon Steele
Programmers Notepad - http://www.pnotepad.org/
|
|
|
|
|
Sure,
I'll put everything online when i'll be back at work, monday,
Thanks for the comment.
Romain LESAVRE :: http://rlesavre.free.fr/
|
|
|
|
|
Looks good!
I'll update the list of the different variations of tab controls available to list yours. I could either point to a URL you provide, or I could include the file where you implement this if you like. Let me know what you'd prefer.
Thanks!
-Daniel
|
|
|
|
|
|
Thanks.
I would much prefer that you create a new file for this and give the class a new name. Just like SimpleTabCtrls.h and SimpleDlgTabCtrls.h. Perhaps you could use something like DotNet2005TabCtrl.h, CDotNet2005TabCtrl, and so on. If you start out by copying DotNetTabCtrl.h, in addition to renaming CDotNetTabCtrlImpl and CDotNetTabCtrl to CDotNet2005TabCtrlImpl and CDotNet2005TabCtrl, be sure to remove CDotNetButtonTabCtrlImpl and CDotNetButtonTabCtrl from the file.
Also, you changed CustomTabCtrl to add some things to CTCSETTINGS, but the base class doesn't seem to use these new things. If iRMargin and iLMargin are only used by your CDotNet2005TabCtrl, for now at least I'd prefer that you track those in CDotNet2005TabCtrl itself (maybe in a new structure, or just as member variables). In the base class CCustomTabCtrl, only UpdateLayout_ScrollToFit uses CTCSETTINGS, and that is meant as more of a "fall back" implementation (CDotNetTabCtrlImpl completely re-implements UpdateLayout_ScrollToFit).
And then a couple other nit-picky things. In your copy of the file, you can take out all the history that applied to DotNetTabCtrl.h. To fit in with other places, I'd also remove the leading underscore off of _GetTabShapeTop and _GetTabShapeBottom. You might also want to call them through pT to make those methods overrideable (it does happen to be in the "Overrideables" section). And then with the comment in DrawItem_ImageAndText, you might also want to put in a comment about LM and RM. You also have a magic number "3" in there that might be added as another variable, or at least a const.
Another thing to note is that the look of the tabs in Visual Studio 2005 might change before the release. See http://blogs.msdn.com/aaronbrethorst/archive/2005/03/03/384590.aspx[^] and http://blogs.msdn.com/aaronbrethorst/archive/2005/03/16/397107.aspx[^].
Once you have a single .h file, you could put that up on your website, and I could make a link to it.
Thanks!
-Daniel
|
|
|
|
|
As well as the look, there is also the usability of the new controls to support:
1. a drop down list of all the tabs (as opposed to scroll arrows - CTCS_ITEMLIST?);
2. only tabs that are fully visible (CTCS_SHOWVISIBLEONLY?) are displayed - if using scroll arrows, this will have the Borland IDE behaviour of advancing a whole tab at a time;
3. items are added to the beginning of the tab control rather than the end (like they are in DotNet2002/3).
Reece Haston Dunn
Software Engineer, Sophos
Web: www.sophos.com
Sophos - protecting businesses against viruses and spam
|
|
|
|
|
Hi Daniel,
In a TabbedSplit Demo containing an RichEditCtrl control, I just copied your code like this:
m_tabCloseWnd.SetTabStyles(CTCS_TOOLTIPS | CTCS_DRAGREARRANGE);
m_tabCloseWnd.Create(m_hWnd, rcDefault);
m_tabCloseWnd.GetTabCtrl().ModifyStyle(CTCS_BOTTOM, (CTCS_SCROLL | CTCS_CLOSEBUTTON));
but in the ModifyStyle(), it occured ASSERT(::IsWindow(m_hWnd) Error sometimes(not all time)
I don't know where is wrong! Mybe you can help me!!
thanks a lot!
sunrong
|
|
|
|
|
Hi Daniel,
I made wrong with the Message Loop!
thank you for your work!
SunRong
|
|
|
|
|
Great work! But how can i add arrow/close icon into tab pane in the SDI application like your demo TabDemo?
Thank u!!!
rtrt
|
|
|
|
|
|
Thank you for your answer! Your work is really nice!!
burns7975
|
|
|
|
|