The SHMENUBAR resource is a pain. A real pain. Why is there no support for this on the VS 2005 / 2008 resource editor? This resource is actually quite powerful when it comes to defining the WM application menu and fine-tuning its appearance. But when you need to change something, you need to remember the string table IDs, the toolbar button options and all that.
Most WM applications I have seen so far use a popup menu attached to each of the menu bar buttons. If this is your case, you can forget about the SHMENUBAR resource and use the menu editor. Create a menu with two items and their popups on the resource editor. If you are using WTL, go to your CMainFrame::OnCreate method and instead of the simple
CreateSimpleCEMenuBar();
write:
CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);
The IDR_MAINFRAME is your main menu ID. And that's all you need.
If you are using straight API calls, when calling the SHCreateMenuBar API, make sure that the dwFlags member of the SHMENUBARINFO contains the SHCMBF_HMENU flag and that nToolbarId contains your menu ID.
Tuesday, May 27, 2008
Monday, May 26, 2008
The tamed tree view
On my last post I described a strange behavior of the tree view control when it is used as a WTL child view and when it is scrolled into view using the SlideView sample application that I have been developing to illustrate child view animations. The solution for this problem was, as I suspected, to host the tree view control in another window and making this window the frame's child view. All animations work correctly now when the tree view control is showed up.
In this latest version of the sample, I extended the use of the CSelectionBar control (some call it the header bar control) in order to select which animation to perform (using the left menu). Now the code also supports switching views without any animation (select 'None').
This control will be updated to allow userd to display arbitrary windows instead of a standard popup menu and also will allow the inclusion of other controls in the toolbar (like an edit control or a combobox).
In this latest version of the sample, I extended the use of the CSelectionBar control (some call it the header bar control) in order to select which animation to perform (using the left menu). Now the code also supports switching views without any animation (select 'None').
This control will be updated to allow userd to display arbitrary windows instead of a standard popup menu and also will allow the inclusion of other controls in the toolbar (like an edit control or a combobox).
Thursday, May 22, 2008
The misbehaved tree view
Following up on the WTL child view animation code I have been working on, I have noticed that when the tree view child window is selected into view (see the sample application), the animation never works as expected. In fact, the tree does not seem to correctly repaint while it is being moved, contrary to the form and list view. After selecting the tree view it also fails to correctly repaint when you open another application and return to the sample (use the Memory applet).
This problem became more apparent when I decided to adapt a very old piece of code to WTL 8.0: the selection tool bar (what you see on top of the PPC File Explorer). Being a toolbar, it must coexist with the child view within the same frame, so the scrolling code had to be adapted.
I will post the solution for this problem when I find it. Most likely I will have to host the tree in another window...
This problem became more apparent when I decided to adapt a very old piece of code to WTL 8.0: the selection tool bar (what you see on top of the PPC File Explorer). Being a toolbar, it must coexist with the child view within the same frame, so the scrolling code had to be adapted.
I will post the solution for this problem when I find it. Most likely I will have to host the tree in another window...
Monday, May 19, 2008
Animating Child View Transitions - II
Now that I undestood how to animate child views in a Windows Mobile WTL 8.0 applicaction (see Animating Child View Transitions), it's time to remove all the animation code from the sample's main frame and move it into specialized classes. Child view animations are performed by the CChildViewAnimate mix-in class (see the sample project). To use it, you merely add it to your main frame's inheritance list:
Next, change the main frame's PreTranslateMessage method to this:
The call to ChildPreTranslateMessage ensures that the active child view class has a chance to do some custom message translation work. To enable this, you must derive all you child view classes from CChildView, like this:
If you need to do custom message translation, you must override the PreTranslateMessage:
Note the IsWindow test: it is there to avoid having this method called when the child window is already destroyed.
class CMainFrame :
public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>,
public CChildViewAnimate<CMainFrame>,
public CMessageFilter,
public CIdleHandler
Next, change the main frame's PreTranslateMessage method to this:
if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
return TRUE;
return ChildPreTranslateMessage(pMsg);
The call to ChildPreTranslateMessage ensures that the active child view class has a chance to do some custom message translation work. To enable this, you must derive all you child view classes from CChildView, like this:
class CSlideFormView :
public CDialogImpl<CSlideFormView>,
public CChildView<CSlideFormView>
If you need to do custom message translation, you must override the PreTranslateMessage:
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
if(!::IsWindow(m_hWnd))
return FALSE;
return CWindow::IsDialogMessage(pMsg);
}
Note the IsWindow test: it is there to avoid having this method called when the child window is already destroyed.
Friday, May 09, 2008
ATL ASSERT on exit
If you tried the sample code I posted yesterday, you might have seen an elusive ATLASSERT dialog box (in Debug mode, of course). When the assertion fires, it happens when the application is closing and the dialog box is very briefly displayed and then closes. You never get to see what caused the assertion in the first place nor do you get a chance to break into the debugger.
To handle this situation, I changed the ATLASSERT macro definition the following way:
extern void AssertBreakpoint();
#define ATLASSERT(expr) { if(!(expr)) AssertBreakpoint(); \
_ASSERTE(expr); }
When the assertion is triggered, the code first calls the AssertBreakpoint function that is there just for you to place a debugger breakpoint. I implemented it with the following rocket-science code:
void AssertBreakpoint() { int a = 3; }
Now you can run the application and you will be sure that you know where the assertion fired. Why? When the debugger stops on your breakpoint you can immediately check the stack and look at the offending code. In my case the assertion was being fired in the CSlideFormView::PreTranslateMessage method. The form view HWND was already destroyed when the
return CWindow::IsDialogMessage(pMsg);
line was executed and this function validates m_hWnd in debug mode.
So if you find yourself in such a situation, use this technique because by defining your own version of ATLASSERT you essentially "infect" all the ATL and WTL code with your custom implementation. All your ATLASSERT are belong to me.
To handle this situation, I changed the ATLASSERT macro definition the following way:
extern void AssertBreakpoint();
#define ATLASSERT(expr) { if(!(expr)) AssertBreakpoint(); \
_ASSERTE(expr); }
When the assertion is triggered, the code first calls the AssertBreakpoint function that is there just for you to place a debugger breakpoint. I implemented it with the following rocket-science code:
void AssertBreakpoint() { int a = 3; }
Now you can run the application and you will be sure that you know where the assertion fired. Why? When the debugger stops on your breakpoint you can immediately check the stack and look at the offending code. In my case the assertion was being fired in the CSlideFormView::PreTranslateMessage method. The form view HWND was already destroyed when the
return CWindow::IsDialogMessage(pMsg);
line was executed and this function validates m_hWnd in debug mode.
So if you find yourself in such a situation, use this technique because by defining your own version of ATLASSERT you essentially "infect" all the ATL and WTL code with your custom implementation. All your ATLASSERT are belong to me.
Thursday, May 08, 2008
Animating Child View Transitions
On my last post I promised to start writing the OLE DB client code to replace the ATL Consumer Templates on mobile and embedded devices. Whenever I write a library, I always look for an interesting application to use the library. In this case there is an obvious candidate: a replacement for the SQL Compact "Query Analyzer".
Instead of writing a dull imitation of the existing tabbed interface, I looked for ways to make a WTL SDI application dynamically change the frame's child view and there was no shortage of ideas, especially on CodeProject: Switch Views in a WTL SDI Application. The code works very well, but there is no provision for animating the child view transition. So I decided to do a little research into this issue and produced an early prototype.
The sample application flips between two views (Form and List) by horizontally scrolling the changing views. The idea is that the form view is on the left and the list view is on the right so when you switch views they animate intuitively: the form hides and the list shows by scrolling to the left and the reverse happens by scrolling to the right. This concept can be extended to vertical scrolling so the user can associate the process of navigating through the application with a map.
There were some challenges I had to overcome to implement this. To make animations smoother, I thought about rendering both views (the old that is going out and the new that is coming in) in a big bitmap and then just paint the bitmap in the frame's client area. Interestingly, there is no documented way to render a hidden window to a memory DC so there was no way to render the new view in the bitmap. You can overcome this if you have full control of the new window's paint cycle, but if you use Windows controls like the list view you are out of luck. I even tried forcing a WM_PAINT message with the memory HDC on the WPARAM, but the list would not render the header... So now I only render to a bitmap the old view and scroll in the new view using MoveWindow.
Another issue I had to take care of is the scrolling speed. The speed of ScrollDC increases as the area to scroll diminishes, so you get a scrolling procedure that gets faster and faster. To make it work at the same apparent speed, I had to time the first scroll and then use the system timer to compensate for faster execution times when the scrolled bitmap gets smaller.
I still need to implement the vertical scrolling and to encapsulate all of the code in a mixin class for the WTL main frame. Stay tuned.
Instead of writing a dull imitation of the existing tabbed interface, I looked for ways to make a WTL SDI application dynamically change the frame's child view and there was no shortage of ideas, especially on CodeProject: Switch Views in a WTL SDI Application. The code works very well, but there is no provision for animating the child view transition. So I decided to do a little research into this issue and produced an early prototype.
The sample application flips between two views (Form and List) by horizontally scrolling the changing views. The idea is that the form view is on the left and the list view is on the right so when you switch views they animate intuitively: the form hides and the list shows by scrolling to the left and the reverse happens by scrolling to the right. This concept can be extended to vertical scrolling so the user can associate the process of navigating through the application with a map.
There were some challenges I had to overcome to implement this. To make animations smoother, I thought about rendering both views (the old that is going out and the new that is coming in) in a big bitmap and then just paint the bitmap in the frame's client area. Interestingly, there is no documented way to render a hidden window to a memory DC so there was no way to render the new view in the bitmap. You can overcome this if you have full control of the new window's paint cycle, but if you use Windows controls like the list view you are out of luck. I even tried forcing a WM_PAINT message with the memory HDC on the WPARAM, but the list would not render the header... So now I only render to a bitmap the old view and scroll in the new view using MoveWindow.
Another issue I had to take care of is the scrolling speed. The speed of ScrollDC increases as the area to scroll diminishes, so you get a scrolling procedure that gets faster and faster. To make it work at the same apparent speed, I had to time the first scroll and then use the system timer to compensate for faster execution times when the scrolled bitmap gets smaller.
I still need to implement the vertical scrolling and to encapsulate all of the code in a mixin class for the WTL main frame. Stay tuned.
Thursday, May 01, 2008
SQL Compact Command Parameter Sizes
This is not a native issue, but one of the things I learned from the SQL Compact sessions from the last MVP Summit (and I think is not under my NDA, so I can talk about it) is the fact that you can speed up your SqlCeCommand (yes, I'm talking managed code here) by explicitly setting the parameter sizes. This was a bit of a surprise for me because with OLE DB you have no such niceties - when you create a command parameter, you must correctly specify the command parameter size, otherwise you just may see your accessor writing in very strange places...
Coming up: I'm starting to write the native alternative to the ATL OLE DB Consumer Templates for devices. This is going to be a lot of fun...
Coming up: I'm starting to write the native alternative to the ATL OLE DB Consumer Templates for devices. This is going to be a lot of fun...
Subscribe to:
Posts (Atom)