none
HwndHost keyboard focus

    Question

  • The chart area in the screenshot is a HwndHost control which hosts a native Win32 window (with it's own registered WNDCLASS) implemented in C++/CLI and drawn with Direct2D. The HwndHost is hosted in a WPF Border control.

    The problem I have is that I can't set the keyboard focus to the hosted Win32 window. I want the focus to move to the hosted Win32 window when I click on the chart area. I tried calling SetFocus on WM_LBUTTONDOWN, but that screws up the focus in the rest of the application.

    Currently, even if I click on the Win32 window, the focus remains on the tree-view on the left, and if I press the up/down cursor keys, the tree-view will get them, not the chart window.

    How do I make the hosted Win32 window receive keyboard input from when I click on the chart area, until I click on another control (like the tree-view, or the toolbar)?




    This is the code I have so far:

    template <typename T>
    inline T intPtrToPtr(IntPtr value)
    {
      return reinterpret_cast<T>(static_cast<void*>(value));
    }
    
    public ref class ChartWindowHost : public HwndHost, IKeyboardInputSink
    {
    private:
      ChartWindow* chartWindow; // this is a C++ class doing the actual work
    
    protected: 
      virtual HandleRef BuildWindowCore(HandleRef parent) override
      {
        chartWindow = new ChartWindow;
        const HINSTANCE hInstance = intPtrToPtr<HINSTANCE>(Marshal::GetHINSTANCE(Assembly::GetExecutingAssembly()->GetModules()[0]));
        const HWND parentWindow = intPtrToPtr<HWND>(parent.Handle);
        chartWindow->Create(hInstance, parentWindow);
        return HandleRef(this, IntPtr(chartWindow->GetHandle()));
      }
    
      virtual void DestroyWindowCore(HandleRef /*window*/) override
      {
        chartWindow->Destroy();
        delete chartWindow;
        chartWindow = NULL;
      }
    };
    
    I target .NET 4.0
    • Edited by Adal Chiriliuc Wednesday, May 19, 2010 9:40 PM Added image
    Wednesday, May 19, 2010 9:38 PM

All replies

  • Hi Adal,

    You need to create your own HwndHost class and override the TabIntoCore and TranslateAcceleratorCore methods. The sample below shows details:

      public class MyHwndHost : HwndHost
      {
        //Handle this event to focus the win32 control
        protected override bool TabIntoCore(TraversalRequest request)
        {
          if (request.FocusNavigationDirection == FocusNavigationDirection.Next)
            SetFocus(firstWin32Control);
          else
            SetFocus(lastWin32Control);
        }
    
        //Handle this event to focus back to the WPF conrol.
        protected override bool TranslateAcceleratorCore(ref MSG msg, ModifierKeys modifiers)
        {
          
          //handle some messages and focus the WPF elements.
        }
    
        private void SetFocus(IntPtr handle)
        {
          //call win32 SetFocus function to focus the win32 control.
        }
      }
    Let me know if this does not help.
    Aland Li

     


    Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
    Sunday, May 23, 2010 12:35 PM
  • Your code sample is for C#. I tried converting it to C++/CLI, but it doesn't work, TabIntoCore is never called, even when pressing Tab multiple times. I also get this warning when compiling: "warning C4490: 'override' : incorrect use of override specifier; 'Veloce::ChartWindowHost::TabIntoCore' does not match a base ref class method; 'new' or 'override' are only allowed when a matching base method from a ref class exists"

    For the moment, I've decided to display the chart in a separate fully native (overlapped) window. I'll try to integrate it back into the WPF window when I'll have more time to deal with this focus issue, and hopefully when I'll be more familiar with WPF/.NET.

    Thanks for trying to help.

     

    Monday, May 24, 2010 12:35 PM
  • Hi Adal,

    Below is the code in C++/CLI:

    ref class MyHwndHost : HwndHost
    {
    protected: 
    	virtual HandleRef BuildWindowCore(HandleRef hwndParent) override
    	{
    		HWND hwnd = CreateWindow(L"WebcamClass",			// Registered class
    						NULL,								// Title
    						WS_CHILD,							// Style
    						CW_USEDEFAULT, 0,					// Position
    						Webcam::GetWidth(),					// Width
    						Webcam::GetHeight(),				// Height
    						(HWND)hwndParent.Handle.ToInt32(),	// Parent
    						NULL,								// Menu
    						GetModuleHandle(NULL),				// hInstance
    						NULL);								// Optional parameter
    
    		if (hwnd == NULL)
    			throw gcnew ApplicationException("CreateWindow failed!");
    
    		Webcam::AttachToWindow(hwnd);
    
    		return HandleRef(this, IntPtr(hwnd));
    	}
    
    	virtual void DestroyWindowCore(HandleRef hwnd) override
    	{
    		// Just a formality:
    		::DestroyWindow((HWND)hwnd.Handle.ToInt32());
    	}
    
    
    
    	virtual bool TabInto(TraversalRequest^ request) override
    	{
    		if (request->FocusNavigationDirection == FocusNavigationDirection::Next)	
    		{
    			SetFocus(hwndForFirstWin32Control);
    		}
    		else
    			SetFocus(hwndForLastWin32Control);
    	}
    
    	virtual bool TranslateAccelerator(MSG% msg, ModifierKeys modifiers) override
    	{
    		if (msg.message == WM_KEYDOWN && msg.wParam == IntPtr(VK_TAB))
    		{
    			// Handle Shift+Tab
    			if (GetKeyState(VK_SHIFT))
    			{
    				if (GetFocus() == hwndOfFirstControl)
    				{
    					// We're at the beginning, so send focus to the previous WPF element
    					return this->KeyboardInputSite->OnNoMoreTabStops(
    						gcnew TraversalRequest(FocusNavigationDirection::Previous));
    				}
    				else
    					return (SetFocus(hwndOfPreviousControl) != NULL);
    			}
    			// Handle Shift without Tab
    			else
    			{
    				if (GetFocus() == hwndOfLastControl)
    				{
    					// We're at the end, so send focus to the next WPF element
    					return this->KeyboardInputSite->OnNoMoreTabStops(
    						gcnew TraversalRequest(FocusNavigationDirection::Next));
    				}
    				else
    					return (SetFocus(hwndOfNextControl) != NULL);
    			}
    		}
    	}
    };

    Wait for the results of your method.

     


    Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
    Tuesday, May 25, 2010 10:28 AM