Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS Tech Support » Transfering from CTreeCtrl to CExtTreeCtrl Collapse All
Subject Author Date
Robert Webb Aug 14, 2009 - 2:32 AM

Hi,


I’m trying to transfer some of our old CTreeCtrl’s to the Prof-UIS CExtTreeCtrl instead.  There are two problems though.


(1) CExtTreeCtrl seems to eat some mouse events and not pass them through.  Our derived tree class required the NM_CLICK, NM_DBLCLK and NM_RCLICK notifications, but when I change the base class to CExtTreeCtrl, these notifications are no longer received.  Can this be fixed?


(2) Just a minor incompatibility: SelectItem() now requires at least two arguments, whereas it previously only required one.  I suggest you make the second argument default to true (for selection rather than deselection).  This was easy enough to work-around by deriving my own tree control and over-riding SelectItem() to provide this default second arg, but I suggest you do it in the Prof-UIS code too.


Thanks,


Rob.

Robert Webb Aug 24, 2009 - 3:32 AM

I think I found another problem with CExtTreeCtrl.  It doesn’t always call OnLButtonUp() when the left button is released.  It does get called if you drag between the button down and up, or if you click to expand an item, but if you just click on an item and release without moving the mouse, then OnLButtonUp() is not called.


Thanks,


Rob.

Technical Support Aug 24, 2009 - 11:33 AM

Thank you for reporting this issue. Please update the source code for the following two methods:

bool CExtControlBar::stat_DragDetect_ImplStep(
      HWND hWnd,
      POINT pt,
      UINT nUpMessage // = WM_LBUTTONUP
      )
{
      if( hWnd == NULL || (! ::IsWindow( hWnd ) ) )
            return false;
CSize sizeDrag( ::GetSystemMetrics( SM_CXDRAG ), ::GetSystemMetrics( SM_CYDRAG ) );
CRect rc( pt.x - sizeDrag.cx, pt.y - sizeDrag.cy, pt.x + sizeDrag.cx, pt.y + sizeDrag.cy );
      ::SetCapture( hWnd );
      for( MSG _msg; ::IsWindow( hWnd ) ; )
    {
        for(      ;
                        ::IsWindow( hWnd )
                  &&    (     ::PeekMessage( &_msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE )
                        ||    ::PeekMessage( &_msg, 0, WM_CANCELMODE, WM_CANCELMODE, PM_NOREMOVE )
                        ||    ::PeekMessage( &_msg, 0, WM_ACTIVATEAPP, WM_ACTIVATEAPP, PM_NOREMOVE )
                        )
                        ;
                        )
        {
            if(         _msg.message == nUpMessage
                        ||    _msg.message == WM_CANCELMODE
                        ||    _msg.message == WM_ACTIVATEAPP
                        )
            {
                ::ReleaseCapture();
                return false;
            }
                  ::PeekMessage( &_msg, 0, _msg.message, _msg.message, PM_REMOVE );
            if( _msg.message == WM_MOUSEMOVE )
            {
                CPoint ptMessage( LOWORD( _msg.lParam ), HIWORD( _msg.lParam ) );
                if( ! rc.PtInRect( ptMessage ) )
                {
                    ReleaseCapture();
                    return true;
                }
            }
       }
        ::WaitMessage();
    }
    return false;
}

bool CExtControlBar::stat_DoDragDetect(
      HWND hWnd,
      const POINT & ptWndClient,
      UINT nUpMessage // = WM_LBUTTONUP
      )
{
      if( hWnd == NULL || (! ::IsWindow( hWnd ) ) )
            return false;
CPoint ptScreen = ptWndClient;
      ::ClientToScreen( hWnd, &ptScreen );
      for( ; stat_DragDetect_ImplStep( hWnd, ptScreen, nUpMessage ) ; )
      {
            POINT ptCursorPos = { -32767, -32767 };
            if( ! ::GetCursorPos( &ptCursorPos ) )
                  return false;
            if(  ptScreen == ptCursorPos )
                  continue; //only drag time elapsed but mouse is not moved
            return true;
      }
      return false;
}


Robert Webb Aug 24, 2009 - 8:07 PM

Something strange is still happening.  Now my OnLButtonUp callback gets called, but sometimes a drag operation starts AFTER the button up message.  Doesn’t happen if you click and hold before releasing, or if the mouse moves while the button is down, but if you just click and release quickly, the TVN_BEGINDRAG message arrives AFTER the button up message.  This causes problems of course because button-up is supposed to mark the end of the drag.


Thanks,


Rob.

Robert Webb Aug 24, 2009 - 8:14 PM

More info: the button-up event is sent downstream fromCExtTreeCtrl::OnLButtonDown.  That doesn’t seem right.  Then the begin-drag event is sent after button-up.  Here’s a stack trace showing button-up being called downstream from button-down.  As I said this only happens if you tap the button quickly.


 



>    cadwind.exe!ViewTreeCtrl::OnLButtonUp(unsigned int nFlags=0, CPoint point={...})  Line 258    C++
	 mfc80d.dll!CWnd::OnWndMsg(unsigned int message=514, unsigned int wParam=0, long lParam=12058691, long * pResult=0x0013a47c)  Line 2169    C++
	 mfc80d.dll!CWnd::WindowProc(unsigned int message=514, unsigned int wParam=0, long lParam=12058691)  Line 1741 + 0x20 bytes    C++
	 ProfUIS285nd.dll!CExtTreeCtrl::WindowProc(unsigned int message=514, unsigned int wParam=0, long lParam=12058691)  Line 4500 + 0x14 bytes    C++
	 cadwind.exe!CExtNCSB_Impl<CExtTreeCtrl>::WindowProc(unsigned int message=514, unsigned int wParam=0, long lParam=12058691)  Line 1145 + 0x17 bytes    C++
	 mfc80d.dll!AfxCallWndProc(CWnd * pWnd=0x0fd84a18, HWND__ * hWnd=0x000e1752, unsigned int nMsg=514, unsigned int wParam=0, long lParam=12058691)  Line 240 + 0x1c bytes    C++
	 mfc80d.dll!AfxWndProc(HWND__ * hWnd=0x000e1752, unsigned int nMsg=514, unsigned int wParam=0, long lParam=12058691)  Line 389    C++
	 mfc80d.dll!AfxWndProcBase(HWND__ * hWnd=0x000e1752, unsigned int nMsg=514, unsigned int wParam=0, long lParam=12058691)  Line 411 + 0x15 bytes    C++
	 user32.dll!7e418734()     
	 [Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]    
	 user32.dll!7e418816()     
	 user32.dll!7e4189cd()     
	 user32.dll!7e41ca67()     
	 user32.dll!7e4196c7()     
	 cadwind.exe!gr_intr(int * type=0x0013a880, int clearbuffer=0)  Line 5107 + 0xc bytes    C++
	 cadwind.exe!CheckUserAbort()  Line 491 + 0xb bytes    C++
	 cadwind.exe!CGLRenderer::DrawSurfaces(CGLRenderer::enumDrawType drawtype=drawSurfaceAll)  Line 959 + 0x5 bytes    C++
	 cadwind.exe!CGLRenderer::DrawSubwin(bool bClear=false)  Line 273    C++
	 cadwind.exe!CGLRenderer::RenderScene(bool bClear=false)  Line 2027    C++
	 cadwind.exe!CCadView::ViewDrawSubWindow(int nSubno=0, int nAction=1, bool bClear=true)  Line 366 + 0xa bytes    C++
	 cadwind.exe!CCadView::ViewDraw(int iSubno=-1, int nAction=1)  Line 122 + 0x14 bytes    C++
	 cadwind.exe!dr_update(int clear=-1, int nAction=1)  Line 465 + 0x13 bytes    C++
	 cadwind.exe!dr_update(int clear=-1)  Line 478 + 0xd bytes    C++
	 cadwind.exe!vw_ChangeView(int subno=1, int nViewNum=10)  Line 1164 + 0xb bytes    C++
	 cadwind.exe!vw_select(int * redrawn=0x0013af80, int autoselect=1, const char * viewname=0x00000000)  Line 1139 + 0xd bytes    C++
	 cadwind.exe!CDockableViewDlg::SelectView(_TREEITEM * item=0x00232350)  Line 444 + 0x10 bytes    C++
	 cadwind.exe!CDockableViewDlg::OnSelectView(tagNMHDR * pNMHDR=0x0013baf4, long * pResult=0x0013b2a4)  Line 642    C++
	 mfc80d.dll!_AfxDispatchCmdMsg(CCmdTarget * pTarget=0x0fd84898, unsigned int nID=1308, int nCode=65134, void (void)* pfn=0x00ba1e9e, void * pExtra=0x0013b17c, unsigned int nSig=60, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  Line 112    C++
	 mfc80d.dll!CCmdTarget::OnCmdMsg(unsigned int nID=1308, int nCode=65134, void * pExtra=0x0013b17c, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  Line 381 + 0x27 bytes    C++
	 mfc80d.dll!CDialog::OnCmdMsg(unsigned int nID=1308, int nCode=5176942, void * pExtra=0x0013b17c, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  Line 85 + 0x18 bytes    C++
	 cadwind.exe!CDockableViewDlg::OnCmdMsg(unsigned int nID=1308, int nCode=5176942, void * pExtra=0x0013b17c, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  Line 674    C++
	 mfc80d.dll!CWnd::OnNotify(unsigned int __formal=1308, long lParam=1293044, long * pResult=0x0013b2a4)  Line 2326    C++
	 mfc80d.dll!CWnd::OnWndMsg(unsigned int message=78, unsigned int wParam=1308, long lParam=1293044, long * pResult=0x0013b2dc)  Line 1767 + 0x2a bytes    C++
	 mfc80d.dll!CWnd::WindowProc(unsigned int message=78, unsigned int wParam=1308, long lParam=1293044)  Line 1741 + 0x20 bytes    C++
	 ProfUIS285nd.dll!CExtWS<CExtADLG<CExtResDlg>,301>::WindowProc(unsigned int message=78, unsigned int wParam=1308, long lParam=1293044)  Line 1197    C++
	 ProfUIS285nd.dll!CExtWA<CExtWS<CExtADLG<CExtResDlg>,301> >::WindowProc(unsigned int message=78, unsigned int wParam=1308, long lParam=1293044)  Line 1844 + 0x14 bytes    C++
	 ProfUIS285nd.dll!CExtResizableDialog::WindowProc(unsigned int message=78, unsigned int wParam=1308, long lParam=1293044)  Line 378 + 0x14 bytes    C++
	 mfc80d.dll!AfxCallWndProc(CWnd * pWnd=0x0fd84898, HWND__ * hWnd=0x002216d8, unsigned int nMsg=78, unsigned int wParam=1308, long lParam=1293044)  Line 240 + 0x1c bytes    C++
	 mfc80d.dll!AfxWndProc(HWND__ * hWnd=0x002216d8, unsigned int nMsg=78, unsigned int wParam=1308, long lParam=1293044)  Line 389    C++
	 mfc80d.dll!AfxWndProcBase(HWND__ * hWnd=0x002216d8, unsigned int nMsg=78, unsigned int wParam=1308, long lParam=1293044)  Line 411 + 0x15 bytes    C++
	 user32.dll!7e418734()     
	 user32.dll!7e418816()     
	 user32.dll!7e41b4c0()     
	 user32.dll!7e41b50c()     
	 ntdll.dll!_KiUserCallbackDispatcher@12()  + 0x13 bytes    
	 user32.dll!7e4194be()     
	 user32.dll!7e41d4e4()     
	 user32.dll!7e41b903()     
	 comctl32.dll!773eab19()     
	 comctl32.dll!77407392()     
	 comctl32.dll!77407720()     
	 user32.dll!7e41bf78()     
	 comctl32.dll!7740885c()     
	 comctl32.dll!774096bd()     
	 comctl32.dll!7740af3f()     
	 mfc80d.dll!CWnd::OnWndMsg(unsigned int message=4363, unsigned int wParam=9, long lParam=2302800, long * pResult=0x0fd84a18)  Line 1866 + 0x19 bytes    C++
	 mfc80d.dll!CWnd::WindowProc(unsigned int message=4363, unsigned int wParam=9, long lParam=2302800)  Line 1742 + 0x1c bytes    C++
	 ProfUIS285nd.dll!CExtTreeCtrl::WindowProc(unsigned int message=4363, unsigned int wParam=9, long lParam=2302800)  Line 4500 + 0x14 bytes    C++
	 cadwind.exe!CExtNCSB_Impl<CExtTreeCtrl>::WindowProc(unsigned int message=4363, unsigned int wParam=9, long lParam=2302800)  Line 1145 + 0x17 bytes    C++
	 mfc80d.dll!AfxCallWndProc(CWnd * pWnd=0x0fd84a18, HWND__ * hWnd=0x000e1752, unsigned int nMsg=4363, unsigned int wParam=9, long lParam=2302800)  Line 240 + 0x1c bytes    C++
	 mfc80d.dll!AfxWndProc(HWND__ * hWnd=0x000e1752, unsigned int nMsg=4363, unsigned int wParam=9, long lParam=2302800)  Line 389    C++
	 mfc80d.dll!AfxWndProcBase(HWND__ * hWnd=0x000e1752, unsigned int nMsg=4363, unsigned int wParam=9, long lParam=2302800)  Line 411 + 0x15 bytes    C++
	 user32.dll!7e418734()     
	 user32.dll!7e418816()     
	 user32.dll!7e41b4c0()     
	 user32.dll!7e41b50c()     
	 ntdll.dll!_KiUserCallbackDispatcher@12()  + 0x13 bytes    
	 user32.dll!7e4194be()     
	 user32.dll!7e41d890()     
	 user32.dll!7e42f3cc()     
	 mfc80d.dll!CTreeCtrl::SelectItem(_TREEITEM * hItem=0x00232350)  Line 288 + 0x44 bytes    C++
	 ProfUIS285nd.dll!CExtTreeCtrl::FocusItem(_TREEITEM * hti=0x00232350, bool bSelect=true, bool bUnselectOtherItems=true, bool bMouseClickEvent=true)  Line 5129    C++
	 ProfUIS285nd.dll!CExtTreeCtrl::OnTreeMouseClick(_TREEITEM * hti=0x00232350, unsigned long dwHitTestFlags=4, int nMouseButton=1, int nClick=0, unsigned int nMouseEventFlags=1, CPoint point={...})  Line 5545    C++
	 ProfUIS285nd.dll!CExtTreeCtrl::_OnTreeMouseClickImpl(int nMouseButton=1, int nClick=0, unsigned int nMouseEventFlags=1, CPoint point={...})  Line 5236 + 0x2e bytes    C++
>    ProfUIS285nd.dll!CExtTreeCtrl::OnLButtonDown(unsigned int nFlags=1, CPoint point={...})  Line 5146 + 0x18 bytes    C++
     cadwind.exe!ViewTreeCtrl::OnLButtonDown(unsigned int nFlags=1, CPoint point={...})  Line 246 + 0x17 bytes    C++
	 mfc80d.dll!CWnd::OnWndMsg(unsigned int message=513, unsigned int wParam=1, long lParam=12058691, long * pResult=0x0013c4c4)  Line 2169    C++
	 mfc80d.dll!CWnd::WindowProc(unsigned int message=513, unsigned int wParam=1, long lParam=12058691)  Line 1741 + 0x20 bytes    C++
	 ProfUIS285nd.dll!CExtTreeCtrl::WindowProc(unsigned int message=513, unsigned int wParam=1, long lParam=12058691)  Line 4500 + 0x14 bytes    C++
	 cadwind.exe!CExtNCSB_Impl<CExtTreeCtrl>::WindowProc(unsigned int message=513, unsigned int wParam=1, long lParam=12058691)  Line 1145 + 0x17 bytes    C++
	 mfc80d.dll!AfxCallWndProc(CWnd * pWnd=0x0fd84a18, HWND__ * hWnd=0x000e1752, unsigned int nMsg=513, unsigned int wParam=1, long lParam=12058691)  Line 240 + 0x1c bytes    C++
	 mfc80d.dll!AfxWndProc(HWND__ * hWnd=0x000e1752, unsigned int nMsg=513, unsigned int wParam=1, long lParam=12058691)  Line 389    C++
	 mfc80d.dll!AfxWndProcBase(HWND__ * hWnd=0x000e1752, unsigned int nMsg=513, unsigned int wParam=1, long lParam=12058691)  Line 411 + 0x15 bytes    C++
	 user32.dll!7e418734()     
	 user32.dll!7e418816()     
	 user32.dll!7e4189cd()     
	 user32.dll!7e418a10()     
	 user32.dll!7e42d99d()     
	 user32.dll!7e43c69b()     
	 mfc80d.dll!CWnd::IsDialogMessageA(tagMSG * lpMsg=0x0013cee0)  Line 198    C++
	 mfc80d.dll!CWnd::PreTranslateInput(tagMSG * lpMsg=0x0013cee0)  Line 4268    C++
	 mfc80d.dll!CDialog::PreTranslateMessage(tagMSG * pMsg=0x0013cee0)  Line 80    C++
	 ProfUIS285nd.dll!CExtWS<CExtADLG<CExtResDlg>,301>::PreTranslateMessage(tagMSG * pMsg=0x0013cee0)  Line 622    C++
	 ProfUIS285nd.dll!CExtResizableDialog::PreTranslateMessage(tagMSG * pMsg=0x0013cee0)  Line 593    C++
	 mfc80d.dll!CWnd::WalkPreTranslateTree(HWND__ * hWndStop=0x00161586, tagMSG * pMsg=0x0013cee0)  Line 2882 + 0x14 bytes    C++
	 mfc80d.dll!AfxInternalPreTranslateMessage(tagMSG * pMsg=0x0013cee0)  Line 233 + 0x12 bytes    C++
	 mfc80d.dll!CWinThread::PreTranslateMessage(tagMSG * pMsg=0x0013cee0)  Line 773 + 0x9 bytes    C++
	 cadwind.exe!gr_event(int * type=0x0013d250, double * x=0x0013d21c, double * y=0x0013d20c, int * outside=0x0013d244, int * buttons=0x0013d238, std::basic_string<char,std::char_traits<char>,std::allocator<char> > & sReturnString="", long * returnparam=0x00000000, int UseResult=0)  Line 4087 + 0x22 bytes    C++
	 cadwind.exe!mn_get(const char * prompt=0x0013d418, const menu_def_t * menu=0x0013df60, int * mn_base=0x0013d2c0, int * item_no=0x0013d498, char * * string=0x0013d470, int * valid=0x0013d48c, int contextmenu=0, bool bWantWords=false, int default_number=-1)  Line 698 + 0x24 bytes    C++
	 cadwind.exe!mn_get(const char * prompt=0x0013d418, const menu_def_t * menu=0x0013df60, int * mn_base=0x0013d2c0, int * item_no=0x0013d498, char * * string=0x0013d470, int * valid=0x0013d48c, int contextmenu=0, bool bWantWords=false)  Line 568 + 0x27 bytes    C++
	 cadwind.exe!mn_select(const char * prompt=0x0013d418, const menu_def_t * menu=0x0013df60, int * item_no=0x0013d498, char * * string=0x0013d470, int * valid=0x0013d48c, int contextmenu=0, bool bWantWords=false)  Line 406 + 0x25 bytes    C++
	 cadwind.exe!CWinCadApp::RunTextMenuLoop(CCadApp::nRunMode Mode=ModeMODEL)  Line 1322 + 0x2c bytes    C++
	 cadwind.exe!CWinCadApp::RunModelLoop()  Line 1260    C++
	 cadwind.exe!CCadApp::RunMainLoop()  Line 325 + 0x12 bytes    C++
	 cadwind.exe!mdmain(int iargc=2, char * * argc=0x04cbe7c8)  Line 518 + 0x17 bytes    C++
	 cadwind.exe!CWinCadApp::Run()  Line 520 + 0x18 bytes    C++
	 mfc80d.dll!AfxWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00162339, int nCmdShow=1)  Line 47 + 0xd bytes    C++
	 cadwind.exe!WinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x00162339, int nCmdShow=1)  Line 33    C++
	 cadwind.exe!__tmainCRTStartup()  Line 589 + 0x35 bytes    C
	 cadwind.exe!WinMainCRTStartup()  Line 414    C
	 kernel32.dll!7c816fd7()    

Technical Support Aug 26, 2009 - 5:11 AM

The call stack really shows that the WM_LBUTTONUP message is delivered when the WM_LBUTTONUP message is processes. But this is the normal situation. It occurred because some API invocations between ViewTreeCtrl::OnLButtonUp() and CExtTreeCtrl::OnLButtonDown() APIs in the call stack listing caused invocation of the message delivery.

Robert Webb Aug 26, 2009 - 7:39 PM

OK, but the actual problems I’m experiencing are clearly errors.  Could you address those?


Firstly, I am receiving begin-drag events AFTER button-up events.  Standard practice is to end the drag when button-up arrives, which would mean that the drag never ends.


Secondly, resizing the window often gets "stuck", where you release the mouse button but it keeps resizing anyway, but only to make it smaller.  This frustrates me and users of our software.  I have seen the effect in your ribbon bar sample, especially in the debug build (I think just because it slows the program down), so it is not our error.  I have reported this several times over many months and never even got a reply to say whether or not you can reproduce it.


Thanks,


Rob.

Technical Support Aug 29, 2009 - 4:40 AM

Here is the latest source code for the CExtTreeCtrl class:

http://www.prof-uis.com/download/forums/tmp/ExtCommonControls-27-August-2009-for-RobertWebb.zip

Here is the latest source code for the drag-n-drop detection methods:

 bool CExtControlBar::stat_DragDetect_ImplStep(
      HWND hWnd,
      POINT pt,
      UINT nUpMessage // = WM_LBUTTONUP
      )
{
      if( hWnd == NULL || (! ::IsWindow( hWnd ) ) )
            return false;
CSize sizeDrag( ::GetSystemMetrics( SM_CXDRAG ), ::GetSystemMetrics( SM_CYDRAG ) );
CRect rc( pt.x - sizeDrag.cx, pt.y - sizeDrag.cy, pt.x + sizeDrag.cx, pt.y + sizeDrag.cy );
      ::SetCapture( hWnd );
      for( MSG _msg; ::IsWindow( hWnd ) ; )
    {
        for(      ;
                        ::IsWindow( hWnd )
                  &&    (     ::PeekMessage( &_msg, hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE )
                        ||    ::PeekMessage( &_msg, 0, WM_CANCELMODE, WM_CANCELMODE, PM_NOREMOVE )
                        ||    ::PeekMessage( &_msg, 0, WM_ACTIVATEAPP, WM_ACTIVATEAPP, PM_NOREMOVE )
                        )
                        ;
                        )
        {
            if(         _msg.message == nUpMessage
                        ||    _msg.message == WM_CANCELMODE
                        ||    _msg.message == WM_ACTIVATEAPP
                        )
            {
                ::ReleaseCapture();
                return false;
            }
                  ::PeekMessage( &_msg, hWnd, _msg.message, _msg.message, PM_REMOVE );
            if( _msg.message == WM_MOUSEMOVE )
            {
                CPoint ptMessage( LOWORD( _msg.lParam ), HIWORD( _msg.lParam ) );
                if( ! rc.PtInRect( ptMessage ) )
                {
                    ReleaseCapture();
                    return true;
                }
            }
       }
        ::WaitMessage();
    }
    return false;
}

bool CExtControlBar::stat_DoDragDetect(
      HWND hWnd,
      const POINT & ptWndClient,
      UINT nUpMessage // = WM_LBUTTONUP
      )
{
      if( hWnd == NULL || (! ::IsWindow( hWnd ) ) )
            return false;
CPoint ptScreen = ptWndClient;
      ::ClientToScreen( hWnd, &ptScreen );
      for( ; stat_DragDetect_ImplStep( hWnd, ptScreen, nUpMessage ) ; )
      {
            POINT ptCursorPos = { -32767, -32767 };
            if( ! ::GetCursorPos( &ptCursorPos ) )
                  return false;
            if(  ptScreen == ptCursorPos )
                  continue; //only drag time elapsed but mouse is not moved
            return true;
      }
      return false;
}

We made the several left and right mouse button based drag-n-dropping tests with the ProfUIS_Controls sample application. We set breakpoints at the beginning of the CExtTreeCtrl::OnLButtonUp and CExtTreeCtrl::OnRButtonUp methods in Prof-UIS source code. We used the Tree View dialog page in the ProfUIS_Controls sample application because the CPageTreeCtrl::OnBegindragTree1 and CPageTreeCtrl::OnBeginRdragTree1 methods are displaying message boxes when the TVN_BEGINDRAG and TVN_BEGINRDRAG notifications are sent. The message boxes are displayed always earlier than the debugger breaks at the set breakpoints.
It looks like we fixed the window resizing behavior. Please open the source .../Prof-UIS/Src/ExtNcFrame.cpp file and find the WM_ENTERSIZEMOVE text in it. There are two cases. Both code snippets are looking very similar. You will find the following lines in both cases:
::SendMessage( hWnd, WM_ENTERSIZEMOVE, 0L, 0L );

Then you will find the following nested code statements a bit closer from the line above in the both cases:
                   bool bStop = false;
                        for( ; ! bStop ; )
                        {
                              ::WaitMessage();
                              MSG msg;
                              // Process all the messages in the message queue
                              while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
                              {
                                    switch( msg.message )
                                    {

The switch( msg.message ) statement contains the following in both cases:
                               case WM_MOUSEMOVE:
                                          ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
                                          if( msg.hwnd == hWnd )
                                          {
                                                while( ::PeekMessage( &msg, hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE ) );

The while( ::PeekMessage( &msg, hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE ) ); line of code should be simply commented/removed in both cases.

Robert Webb Aug 31, 2009 - 3:03 AM

Thanks for the code updates.  Unfortunately it doesn’t seem to fix anything.  I still get the window frame resizing after the mouse button is up, and clicking quickly on tree items can still cause the up-event to be sent before the begin-drag event.


I have worked around the tree problem by setting a flag on mouse-down and clearing it again on mouse-up, then checking that the flag is set in my begin-drag handler.  If it’s not set, then the drag is disallowed.  I also override OnCaptureChanged() in case my capture was lost while the button was down, meaning I might miss the mouse-up event.


You could do a similar thing I imagine to fix both the tree and frame-resizing problems.  Aside from the persistant resizing, we also have problems with the frame moving and resizing briefly after releasing the mouse button (to end a move or resize).  Our users have to move the mouse slowly to avoid this.  I often want to resize the window and quickly move the mouse elsewhere after letting go.  This can cause the window to follow my mouse back across the screen before stopping.  Very annoying.  In this case, I think it probably just handles one extra mouse-move event AFTER the mouse-up event.  Maybe it is similar to the other problem, with a mouse-move and mouse-up event being handled below mouse-down in the stack, meaning that mouse-down hasn’t finished being processed before the next mouse-move.  Or maybe you are polling the mouse position in mouse-up instead of using the position sent with the event?  That always causes problems.


Rob.

Technical Support Sep 1, 2009 - 11:36 AM

We solved this issue in Prof-UIS 2.87. Please drop us an e-mail to the support mail box at this web site so we will provide you with the FTP download for the latest Prof-UIS version.

Robert Webb Sep 14, 2009 - 7:49 PM

I’m using 2.8.7 now and don’t see any difference.  I haven’t tested the tree stuff since I now have a work-around that works anyway, so maybe that was fixed.  But I still have the problem of the window resizing getting stuck, and now also sometimes moving the whole window gets stuck.  By stuck I mean that the operation continues to occur after the mouse button has been released.  It seems to have lost mouse-capture though, so the resizing only starts up again when you move the mouse back inside the window.  It’s quite difficult to get out of this situation when it happens.  Even clicking doesn’t always fix it.


Getting stuck moving the window is a new one, but that may be due to stuff I’m doing which slows down redraws in the view.  The ProfUIS problems seem worst when the view takes some time to redraw.  You could probably simulate it by putting a one-second sleep in your MDI ribbon bar sample in the redraw/resize.  Getting stuck moving the window happens as follows: I click on the title bar to bring the window to the front (when another window was previously obscuring part of our window).  Then, although I just did a quick click and release with no intention of moving the window, the window moves and follows the mouse anyway.


Along with the highly CPU-intensive and slow ribbon bar, these are probably the two most frustrating issues.  If it’s similar to the issue with tree control events arriving out of order, you could probably fix these by setting a flag on button-down and clearing it on button-up, then testing before any kind of drag operation (ie resizing or moving) and not doing it unless the flag is set.


Rob.

Robert Webb Aug 25, 2009 - 8:53 PM

This problem of getting messages out of order reminds me of some of the more fundamental problems with ProfUIS.  For example, I’ve often seen the problem where ProfUIS doesn’t realise when you stop resizing the main window.  That is, you click on the resizable border, drag it to change the size, then release, but sometimes it keeps resizing anyway.  Well, sort of.  When it gets stuck in this state, you can move the mouse around outside the window without it resizing, but as soon as you move the mouse inside the window, the window starts resizing smaller again.  Very frustrating and hard to escape from.  I’ve seen it in our software on multiple machines, and also in ProfUIS’s own ribbon bar samples.  I think it’s made worse if the app does other computationally expensive stuff, so it isn’t usually noticable in the ProfUIS samples, although I have seen it several times when running the debug version of the ribbon bar sample.  Maybe the debug version is enough slower to make the effect more prevalent.  As for our software, it happens all the time.  This strikes me as similar because if you get your mouse events out of order, then you can miss the button-up event that should have stopped the window resizing.  Could this all be related?


Thanks,


Rob.

Robert Webb Aug 21, 2009 - 1:03 AM

I noticed another problem with CExtTreeCtrl.  The TVN_ITEMEXPANDED notification is not sent when the user expands or collapses an item in the tree.  I’m using the Vista icons if that makes a difference.   I presume TVN_ITEMEXPANDING is also missing, although I didn’t test that.


Note: the TVN_ITEMEXPANDED notification IS sent when ExpandItem() is called in the code, but not when the user manually expands/collapses an item.


Thanks,


Rob.

Technical Support Aug 22, 2009 - 11:37 AM

Thank you for reporting this issue. Please update the source code for the following method:

BOOL CExtTreeCtrl::Expand( HTREEITEM hti, UINT nCode )
{
      ASSERT_VALID( this );
HWND hWndOwn = m_hWnd;
      if( hWndOwn == NULL || ( ! ::IsWindow( hWndOwn ) ) )
            return FALSE;
      if( hti == TVI_ROOT )
      {
            hti = GetRootItem();
            if( hti == NULL )
                  return FALSE;
      }
HWND hWndParent = ::GetParent( m_hWnd );
UINT nOwnID = GetDlgCtrlID();
CExtSafeString strItemText;
NM_TREEVIEW _data;
      ::memset( &_data, 0, sizeof(NM_TREEVIEW) );
      _data.hdr.hwndFrom = m_hWnd;
      _data.hdr.idFrom = nOwnID;
      _data.hdr.code = TVN_ITEMEXPANDING;
      _data.action = nCode;
      _data.itemOld.mask = TVIF_CHILDREN|TVIF_HANDLE|TVIF_PARAM|TVIF_STATE;
      _data.itemOld.hItem = hti;
      GetItem( &_data.itemOld );
      strItemText = GetItemText( hti );
      _data.itemOld.cchTextMax = INT(strItemText.GetLength());
      _data.itemOld.pszText = strItemText.IsEmpty() ? _T("") : LPTSTR(LPCTSTR(strItemText));
      _data.itemOld.mask |= TVIF_TEXT;
      ::memcpy( &_data.itemNew, &_data.itemOld, sizeof(TVITEM) );
      if( ! ::GetCursorPos( &_data.ptDrag ) )
            _data.ptDrag.x = _data.ptDrag.y = 0;
      if( ::SendMessage( hWndParent, WM_NOTIFY, WPARAM(nOwnID), LPARAM(&_data) ) != 0 )
            return TRUE;

      if( ! CTreeCtrl::Expand( hti, nCode ) )
            return FALSE;

      ::memset( &_data, 0, sizeof(NM_TREEVIEW) );
      _data.hdr.hwndFrom = m_hWnd;
      _data.hdr.idFrom = nOwnID;
      _data.hdr.code = TVN_ITEMEXPANDED;
      _data.action = nCode;
      _data.itemOld.mask = TVIF_CHILDREN|TVIF_HANDLE|TVIF_PARAM|TVIF_STATE;
      _data.itemOld.hItem = hti;
      GetItem( &_data.itemOld );
      strItemText = GetItemText( hti );
      _data.itemOld.cchTextMax = INT(strItemText.GetLength());
      _data.itemOld.pszText = strItemText.IsEmpty() ? _T("") : LPTSTR(LPCTSTR(strItemText));
      _data.itemOld.mask |= TVIF_TEXT;
      ::memcpy( &_data.itemNew, &_data.itemOld, sizeof(TVITEM) );
      if( ! ::GetCursorPos( &_data.ptDrag ) )
            _data.ptDrag.x = _data.ptDrag.y = 0;
      ::SendMessage( hWndParent, WM_NOTIFY, WPARAM(nOwnID), LPARAM(&_data) );

      return TRUE;
}



Technical Support Aug 17, 2009 - 11:46 AM

The WM_CREATE message cannot be handled by any dialog controls. The dialog template loading code creates all the dialog’s child windows. Then MFC subclasses them. Please use the PreSubclassWindow() virtual method.

The CExtTreeCtrl control does not support themed scroll bars. The CExtListCtrl does not supports them either. But the CExtNCSB template class can skin scroll bars of common controls. This means you should use the following class:

//
// In .H file:
//

class CAdvTreeCtrl : public CExtNCSB < CExtTreeCtrl >
{
public:
      DECLARE_DYNCREATE( CAdvTreeCtrl );
      CAdvTreeCtrl();
      virtual ~CAdvTreeCtrl();

      //{{AFX_VIRTUAL(CAdvTreeCtrl)
      protected:
      virtual void PreSubclassWindow();
      //}}AFX_VIRTUAL

protected:
      //{{AFX_MSG(CAdvTreeCtrl)
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};


//
// In .CPP file:
//

IMPLEMENT_DYNCREATE( CAdvTreeCtrl, CExtTreeCtrl );

CAdvTreeCtrl::CAdvTreeCtrl()
      : CExtNCSB < CExtTreeCtrl > ( true )
{
}

CAdvTreeCtrl::~CAdvTreeCtrl()
{
}

BEGIN_MESSAGE_MAP( CAdvTreeCtrl, CExtTreeCtrl )
      //{{AFX_MSG_MAP(CAdvTreeCtrl)
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CAdvTreeCtrl::PreSubclassWindow()
{
      CExtNCSB < CExtTreeCtrl > :: PreSubclassWindow();
      //
      // TO-DO: put your tree control initialization code here
      //
} 


Robert Webb Aug 18, 2009 - 1:30 AM

Thanks.


One remaining problem with CExtTreeCtrl is that SelectItem() now seems to leave the previous item selected as well, even though the tree has multiple selection disabled.  Surely when multi-select is disabled, SelectItem() should deselect any other items before doing the selection?  Otherwise it’s incompatible with CTreeCtrl.


I notice the new method FocusItem(), which allows all other items to be deselected.  I’ll try that, although I’m not sure what additional side-effects giving focus to an item might have beyond just selecting it.


Thanks,


Rob.

Technical Support Aug 18, 2009 - 12:46 PM

Thank you for reporting this issue. Please update the source code for the CExtTreeCtrl::SelectItem() method:

void CExtTreeCtrl::SelectItem(
      HTREEITEM hti,
      bool bSelect, // = true
      bool bSubtract // = false
      )
{
      ASSERT_VALID( this );
      if( hti == NULL )
            return;
TREEITEMINFO_t & _TII = TreeItemInfoGet( hti );
      if( bSelect && bSubtract && _TII.m_bSelected )
            _TII.m_bSelected = false;
      else
            _TII.m_bSelected = bSelect;
      if( _TII.m_bSelected && (! MultipleSelectionGet() ) )
      {
            CList < HTREEITEM, HTREEITEM > _listSelectedItems;
            GetSelectedItemsList( _listSelectedItems );
            POSITION pos = _listSelectedItems.GetHeadPosition();
            for( ; pos != NULL; )
            {
                  HTREEITEM htiSel = _listSelectedItems.GetNext( pos );
                  if( htiSel == hti )
                        continue;
                  TREEITEMINFO_t & _TII_Sel = TreeItemInfoGet( htiSel );
                  _TII_Sel.m_bSelected = false;
            }
      }
}


Robert Webb Aug 19, 2009 - 2:30 AM

Thanks.  There still seem to be some issues though:


(1) After calling SelectItem() the tree is not redrawn, so the old selection appears to remain selected until it redraws for another reason.


(2) The focus rectangle is still visible around the previously selected item.  What is this for?  Is it at all relevant in a single-selection tree?  It’s visually confusing to have one item highlighted and another with a rectangle around it in a tree that only allows a single item to be selected.


Thanks,


Rob.

Technical Support Aug 19, 2009 - 2:32 PM

Here is the updated CExtTreeCtrl::SelectItem() method:

 void CExtTreeCtrl::SelectItem(
      HTREEITEM hti,
      bool bSelect, // = true
      bool bSubtract // = false
      )
{
      ASSERT_VALID( this );
      if( hti == NULL )
            return;
HWND hWndOwn = GetSafeHwnd();
TREEITEMINFO_t & _TII = TreeItemInfoGet( hti );
bool bWasSelected =  _TII.m_bSelected;
      if( bSelect && bSubtract && _TII.m_bSelected )
            _TII.m_bSelected = false;
      else
            _TII.m_bSelected = bSelect;
CList < HTREEITEM, HTREEITEM > _listInvalidateItems;
      if( _TII.m_bSelected && (! MultipleSelectionGet() ) )
      {
            CList < HTREEITEM, HTREEITEM > _listSelectedItems;
            GetSelectedItemsList( _listSelectedItems );
            POSITION pos = _listSelectedItems.GetHeadPosition();
            for( ; pos != NULL; )
            {
                  HTREEITEM htiSel = _listSelectedItems.GetNext( pos );
                  if( htiSel == hti )
                        continue;
                  TREEITEMINFO_t & _TII_Sel = TreeItemInfoGet( htiSel );
                  _TII_Sel.m_bSelected = false;
                  if( hWndOwn != NULL )
                        _listInvalidateItems.AddTail( htiSel );
            }
      }
      if( hWndOwn == NULL || (! IsWindowVisible() ) )
            return;
      if( ( bWasSelected && (!_TII.m_bSelected) ) || ( (!bWasSelected) && _TII.m_bSelected ) )
            _listInvalidateItems.AddTail( hti );
POSITION pos = _listInvalidateItems.GetHeadPosition();
      if( pos == NULL )
            return;
CRect rcClient, rcItemEntire;
      GetClientRect( &rcClient );
      for( ; pos != NULL; )
      {
            HTREEITEM htiRedraw = _listInvalidateItems.GetNext( pos );
            TreeItemRectGet( htiRedraw, rcItemEntire, e_tirt_entire );
            if( rcItemEntire.IsRectEmpty() )
                  continue;
            if(         ( rcClient.top <= rcItemEntire.top && rcItemEntire.top <= rcClient.bottom )
                  ||    ( rcClient.top <= rcItemEntire.bottom && rcItemEntire.bottom <= rcClient.bottom )
                  )
                  InvalidateRect( &rcItemEntire );
      }
}

Now it repaints changed selection. But we cannot make it exactly similar to the CTreeCtrl::SelectItem() method because the focus and selection are two different features in the CExtTreeCtrl control.
We did several other improvements in the tree control:
http://www.prof-uis.com/download/forums/tmp/UpdatedExtTreeCtrl-For-RobertWebb.zip

Technical Support Aug 14, 2009 - 1:00 PM

We implemented the NM_KEYDOWN, NM_CLICK, NM_DBLCLK, NM_RCLICK and NM_RDBLCLK notifications in the CExtTreeCtrl control. We made the true flag the default value of the bool bSelect parameter in the CExtTreeCtrl::SelectItem() method. Here is the source code update:

http://www.prof-uis.com/download/forums/tmp/NewExtTreeCtrlNotifications.zip

Robert Webb Aug 16, 2009 - 7:26 PM

Thanks.  There’s a compile error though:


..\Src\ExtControlsCommon.cpp(5077) : error C2660: ’CExtControlBar::stat_DoDragDetect’ : function does not take 3 arguments


Do I need the new version of ExtControlBar.cpp/h too?


Thanks,


Rob.

Technical Support Aug 17, 2009 - 11:45 AM

The CExtControlBar::stat_DoDragDetect() static method is a better version of the DragDetect() Win32 API. Now this static method supports drag-n-drop starting detection by any mouse button - not only by left mouse button:

       static bool stat_DragDetect_ImplStep(
            HWND hWnd,
            POINT pt,
            UINT nUpMessage = WM_LBUTTONUP
            );
      static bool stat_DoDragDetect(
            HWND hWnd,
            const POINT & ptWndClient,
            UINT nUpMessage = WM_LBUTTONUP
            );


bool CExtControlBar::stat_DragDetect_ImplStep(
      HWND hWnd,
      POINT pt,
      UINT nUpMessage // = WM_LBUTTONUP
      )
{
      if( hWnd == NULL || (! ::IsWindow( hWnd ) ) )
            return false;
CSize sizeDrag( ::GetSystemMetrics( SM_CXDRAG ), ::GetSystemMetrics( SM_CYDRAG ) );
CRect rc( pt.x - sizeDrag.cx, pt.y - sizeDrag.cy, pt.x + sizeDrag.cx, pt.y + sizeDrag.cy );
      ::SetCapture( hWnd );
      for( MSG _msg; ::IsWindow( hWnd ) ; )
    {
        for( ; ::IsWindow( hWnd ) && ::PeekMessage( &_msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ) ; )
        {
            if(         _msg.message == nUpMessage
                        ||    _msg.message == WM_CANCELMODE
                        ||    _msg.message == WM_ACTIVATEAPP
                        )
            {
                ::ReleaseCapture();
                return false;
            }
            if( _msg.message == WM_MOUSEMOVE )
            {
                CPoint ptMessage( LOWORD( _msg.lParam ), HIWORD( _msg.lParam ) );
                if( ! rc.PtInRect( ptMessage ) )
                {
                    ReleaseCapture();
                    return true;
                }
            }
        }
        ::WaitMessage();
    }
    return false;
}

bool CExtControlBar::stat_DoDragDetect(
      HWND hWnd,
      const POINT & ptWndClient,
      UINT nUpMessage // = WM_LBUTTONUP
      )
{
      if( hWnd == NULL || (! ::IsWindow( hWnd ) ) )
            return false;
CPoint ptScreen = ptWndClient;
      ::ClientToScreen( hWnd, &ptScreen );
      for( ; stat_DragDetect_ImplStep( hWnd, ptScreen, nUpMessage ) ; )
      {
            POINT ptCursorPos = { -32767, -32767 };
            if( ! ::GetCursorPos( &ptCursorPos ) )
                  return false;
            if(  ptScreen == ptCursorPos )
                  continue; //only drag time elapsed but mouse is not moved
            return true;
      }
      return false;
}


Robert Webb Aug 17, 2009 - 2:17 AM

I tried just removing the third argument in the call to stat_DoDragDetect() and it seems to be working.  Is that a safe work-around?  Will it cause any problems with drag/drop when I get to that?


There are two remaining problems with using CExtTreeCtrl now:


(1) For some reason, I can’t get OnCreate() to be called in my class derived from CExtTreeCtrl.  I added ON_WM_CREATE() to the message map, and my function header is copy/pasted from the MSDN docs:


int CAdvTreeCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)


Any idea why it wouldn’t be getting called in this case?  What I want to do is call LoadWinVistaTreeBox() and TreeBoxStyleSet(CExtTreeCtrl::tree_box_style_bitmap) so that these are the default for all my tree controls.  Maybe there is a better way to do this?


(2) The tree’s scroll bars are still old-style un-themed.  The ProfUIS Controls demo shows a tree with a nice new themed scroll bar.  Do I have to do something to make it use these?


Thanks,


Rob.