Jump to content

Recommended Posts

Posted

In order not to pollute other threads, here's one dedicated to Windows.UI controls and examples.

These are for use with Xaml Islands, NOT WinUI3 - although porting them across shouldn't be too demanding. Both methods have their pros and cons - but I'd say the XAML Island approach is bit more robust at this point in time.

Don't forget to update your manifests to make this work! (background/details in this thread)

Download

PS. More than happy for this to be a community thing if anyone else wishes to contribute ;)

  • 4 weeks later...
Posted

Hi All,

I've been working some more on this over the last day or two, and it seems there's issues with XAML controls that require keyboard input. (i.e. textboxes weren't behaving!)... I might not have this 100% right, but my understanding is:

A message pump basically looks something like this:

while (true)
{
  while (PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ))
  {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
  }
 //do other stuff
}
  • PeekMessage/GetMessage retrieves a message (GetMessage waits for a message, peek will return immediately)
  • TranslateMessage does some pre-processing on kb input.
  • Then DispatchMessage sends messages on to the appropriate window proc.

For xaml islands to properly handle the kb input, we're supposed to pass messages to the XAML host container using:
IDesktopWindowXamlSourceNative2_PreTranslateMessage($pDesktopWinXamlSrc).  And as the function name implies, this should be done before TranslateMessage. If PreTranslateMessage() fails, then the message is not meant for a XAML control and we should do a translate/dispatch as per normal.

Now the API calls for messaging are pretty straight forward - but the problem is that the message pump baked into Autoit. I think, internally the "do other stuff" part of the loop probably includes the execution of  a portion of our source script... Regardless of my take on AutoIt's internals, it seems impossible to trap every message before it has been dispatched.

I don't know how feasible it is to call PreTranslateMessage from custom window procs after messages have been dispatched. This would be a horrible approach, but I guess beggars can't be choosers...

Posted

Thank you for continuing this journey, Matty.

I still have a lot of interest in this. I feel like this can potentially be great for AutoIt. But at the same time, I also understand that this is a tremendous amount of work for one person to do. I feel bad that I personally don't have the skills or understanding yet to be of much help.

I've been reading a bunch of MS documentation and that has probably confused my understanding more. I was wondering if you can help clear it up a bit for me.

Are the controls created by either Xaml Islands or WinUI 3 essentially the same (at least visually on the surface)?

And is the main difference essentially the mechanism on which to get there (there, being what is seen on the surface)?

Posted
1 hour ago, WildByDesign said:

Are the controls created by either Xaml Islands or WinUI 3 essentially the same (at least visually on the surface)?

yeah, visually very similar - but not identical. 

1 hour ago, WildByDesign said:

And is the main difference essentially the mechanism on which to get there (there, being what is seen on the surface)?

Yes and no - XAML Islands are the older tech, and all the current MS development seems to be around WinUI3. But so far I'm having more success with XAML Islands. 

The hosting of the XAML source object is obviously quite different with the windowing class etc. in WinUI3.   But yes, on the control front WinUI3 is essentially an update on the same controls. The layout concepts with grids and stackpanels etc. all work the same way...   You will find some things moved around though - MS has obviously rationalised many of the classes' interfaces. for e.g. the functions on Windows.UI.Xaml.Controls.IButtonWithFlyout moves to Micorsoft.UI.Xaml.Controls.IButton in WinUI3 etc.

For WinUI3, The first thing I need to get to grips with (I think) is around setting up "resources".  Basically a bunch of nested dictionaries etc that contain stuff that a control can draw on.  At least that's probably where I need to start - I'm hoping it will fix some of the weirdness I'm seeing over there. Resources are a thing in the old paradigm too, but these seem to be pre-populated for the most part.  For example, ATM all your controls turn invisible in WinUI3 when in dark mode because none of the dark mode pallets are defined... (And there's some other issues that I need to work through as well)

2 hours ago, WildByDesign said:

Thank you for continuing this journey, Matty.

pleasure mate. thanks very much for the support :) Nothing seems to be straight forward with this stuff, so motivation comes and goes! 

Posted

Ok, so it seems the pretranslate function is used to handle input from the arrow/tab keys and some other things.. but there is more to this puzzle.

The window that we're calling $hIsland is a: Windows.UI.Composition.DesktopWindowContentBridge.  This window has a child, and the WM messages for BK/Mouse input seem to end up there. The child window is of the class: Windows.UI.Input.InputSite.WindowClass.

From some testing, I think we want to be sending WM_CHAR messages to this window.  - It kindof works anyway, I have to mouse over the island for the new characters to appear, but we're moving in the right direction!

WM_CHAR messages should be produced automatically by TranslateMessage.  As it parses WM_KEY* messages, we should by rights be getting new WM_CHAR messages in our queue. If I hijack the Input window's WndProc, I can see there's WM_KEYDOWN/UP messages coming in - but no WM_CHAR messages.  I guess a TranslateMessage was never performed here? Not sure why this would be the case, but its my best guess ATM..

As a workaraoud, you'd think we could subclass that Input window, fudge our own WM_CHAR translation, then forward messages to the original handler. But this plan falls down at the last hurdle.  Manually calling the original window proc throws a fatal Access Denied error :(.   So not sure where to go from here....

Posted (edited)
23 hours ago, MattyD said:

Yes and no - XAML Islands are the older tech, and all the current MS development seems to be around WinUI3. But so far I'm having more success with XAML Islands. 

There is definitely some pros and cons to both. As I understand it, XAML Islands can be beneficial for that fact that they can more easily be added to our existing GUI's.

23 hours ago, MattyD said:

For WinUI3, The first thing I need to get to grips with (I think) is around setting up "resources". 

One thing that I always found interesting about your WinUI3 examples is that they use exactly zero CPU while running. I've always become accustomed to seeing AutoIt GUI's using some small amount of CPU for their GUIGetMsg loop or While loops for OnEvent mode. So that was pretty neat to see.

I see benefits to the XAML Islands approach and the WinUI3 approach as well.

Hypothetically speaking... let's assume for a moment that you got some sort of superhuman powers, unlimited time, etc. Let's assume also that you had a full understanding of how both XAML Islands and WinUI3 work entirely.

Given those hypothetical circumstances:

  • Would you create a UDF for XAML Islands or WinUI3?
    • Or a UDF that somehow combines both?

I've actually been thinking about this for a few months now.

For example, if the community here was to come together on this and help. Obviously it's a significant amount of work for just one person. But what I've been pondering is which approach (XAML Islands or WinUI3) deserves the most attention. I think about which would be most beneficial for AutoIt in, for example, 2 years from now.

I also think about how much support and development Microsoft will put into both of those as time goes on. With XAML Islands being a bit older, I wonder if they will continue supporting and improving them. We all know how they like to jump from one thing to the next.

Anyway, sorry but that was mostly putting my thoughts in writing. To be perfectly clear, I personally am tossed (50/50) between the two.

Edited by WildByDesign
Posted (edited)
12 hours ago, WildByDesign said:

also think about how much support and development Microsoft will put into both of those as time goes on. With XAML Islands being a bit older, I wonder if they will continue supporting and improving them. We all know how they like to jump from one thing to the next.

Yep I agree with this - WinUI3 is by far the better option from this standpoint. 

But at the end of the day, we're just trying to get something to work - So the older approach was looking pretty appealing up until a few days ago!

As a side, I think we might need to change our terminology around this too - after a bit of reading, it looks like XAML Islands for WinUI3 controls will become a thing at some point as well...

Edited by MattyD
  • 2 weeks later...
Posted

Sure thing.

It looks to be quite different to the win32 control at first glance, but admittedly I didn't spend too long investigating things.

anyway, here's a first attempt: ListView nested inside a 1x1 Grid (using v1.6 of the WinRT Libraries.)

#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#Autoit3Wrapper_Res_Compatibility=win10
#AutoIt3Wrapper_UseX64=Y

#include <GUIConstants.au3>
#include "..\Include\WinRT.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.WindowsXamlManager.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.DesktopWindowXamlSource.au3"
#include "..\Include\Interfaces\IDesktopWindowXamlSourceNative.au3"

#include "..\Include\Classes\Windows.UI.Xaml.Controls.Grid.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.RowDefinitionCollection.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.ColumnDefinitionCollection.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.RowDefinition.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.ColumnDefinition.au3"
#include "..\Include\Enumerations\Windows.UI.Xaml.GridUnitType.au3"

#include "..\Include\Classes\Windows.UI.Xaml.Controls.ListView.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.ListViewItem.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.ItemCollection.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.SelectionChangedEventArgs.au3"
#include "..\Include\Classes\Windows.Foundation.PropertyValue.au3"
#include "..\Include\Interfaces\Windows.Foundation.IPropertyValue.au3"

MsgBox($MB_OK, "XAML Islands", "This example may need you to update AutoIt's application manifest." & @CRLF & @CRLF  & _
        "If no control shows, try running XAML_UpdateManifest.au3 and follow the instructions.")

_WinRT_Startup()
If @error Then Exit MsgBox(0, "WinRT Startup", "Error Initialising: " & _WinAPI_GetErrorMessage(@error))
_Main()
_WinRT_Shutdown()

Func _Main()
    ;Startup XAML Host
    Local $pXamlMgr_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.WindowsXamlManager", $sIID_IWindowsXamlManagerStatics)
    Local $pWindowsXamlManager = IWindowsXamlManagerStatics_InitializeForCurrentThread($pXamlMgr_Fact)
    IUnknown_Release($pXamlMgr_Fact)

    ;Create Container
    Local $pDesktopWinXamlSrc_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.DesktopWindowXamlSource", $sIID_IDesktopWindowXamlSourceFactory)
    Local $pInner
    Local $pDesktopWinXamlSrc = IDesktopWindowXamlSourceFactory_CreateInstance($pDesktopWinXamlSrc_Fact, 0, $pInner)
    IUnknown_Release($pDesktopWinXamlSrc_Fact)

    ;Create Grid
    Local $pGrid_Fact =  _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.Grid", $sIID_IGridFactory)
    Local $pGrid = IGridFactory_CreateInstance($pGrid_Fact, 0, $pInner)

    ;Attach the grid control to the container.
    IDesktopWindowXamlSource_SetContent($pDesktopWinXamlSrc, $pGrid)

    ;Get Row and Column properties so we can assign child objects grid positions later.
    _WinRT_SwitchInterface($pGrid_Fact, $sIID_IGridStatics)
    Local $pRowProp = IGridStatics_GetRowProperty($pGrid_Fact)
    Local $pColProp = IGridStatics_GetColumnProperty($pGrid_Fact)
    IUnknown_Release($pGrid_Fact)

    ;Set grid layout to 1x1
    Local $tGridLen = DllStructCreate("align 4;double Value;ulong GridUnitType")
    $tGridLen.Value = 1
    $tGridLen.GridUnitType = _WinRT_GetEnum($mGridUnitType, "Star")

    _WinRT_SwitchInterface($pGrid, $sIID_IGrid)
    Local $pRowDefs = IGrid_GetRowDefinitions($pGrid)
    _WinRT_SwitchInterface($pRowDefs, $sIID_IVector_1_RowDefinition_)
    Local $pRowDef
    For $i = 0 To 0 ;1 Row
        $pRowDef = _WinRT_ActivateInstance("Windows.UI.Xaml.Controls.RowDefinition")
        _WinRT_SwitchInterface($pRowDef, $sIID_IRowDefinition)
        IRowDefinition_SetHeight($pRowDef, $tGridLen)
        IVector_Append($pRowDefs, $pRowDef)
        IUnknown_Release($pRowDef)
    Next
    IUnknown_Release($pRowDefs)

    Local $pColDefs = IGrid_GetColumnDefinitions($pGrid)
    _WinRT_SwitchInterface($pColDefs, $sIID_IVector_1_ColumnDefinition_)
    Local $pColDef
    For $i = 0 To 0 ;1 Column
        $pColDef = _WinRT_ActivateInstance("Windows.UI.Xaml.Controls.ColumnDefinition")
        _WinRT_SwitchInterface($pColDef, $sIID_IColumnDefinition)
        IColumnDefinition_SetWidth($pColDef, $tGridLen)
        IVector_Append($pColDefs, $pColDef)
        IUnknown_Release($pColDef)
    Next
    IUnknown_Release($pColDefs)

    ;Create a ListView
    Local $pListView_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.ListView", $sIID_IListViewFactory)
    Local $pListView = IListViewFactory_CreateInstance($pListView_Fact, 0, $pInner)
    IUnknown_Release($pListView_Fact)

    ;Make ListView multi-select with checkboxes
    _WinRT_SwitchInterface($pListView, $sIID_IListViewBase)
    IListViewBase_SetSelectionMode($pListView, $mListViewSelectionMode["Multiple"]) ;None, Single, Multiple, Extended (shift+ strl multi)
    _WinRT_SwitchInterface($pListView, $sIID_IListViewBase4)
    IListViewBase4_SetIsMultiSelectCheckBoxEnabled($pListView, True)

    ;Setup delegate
    Local $pSelectChgDgt = _WinRT_CreateDelegate("SelChanged")
    _WinRT_SwitchInterface($pListView, $sIID_ISelector)
    Local $iSelectChgTkn = ISelector_AddHdlrSelectionChanged($pListView, $pSelectChgDgt)

    ;Create Items
    Local $pLVItem, $pProp

    _WinRT_SwitchInterface($pListView, $sIID_IItemsControl)
    Local $pItemCol = IItemsControl_GetItems($pListView)
    _WinRT_SwitchInterface($pItemCol, $sIID_IVector_1_Object_)

    Local $pListViewItem_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.ListViewItem", $sIID_IListViewItemFactory)
    Local $pProp_Fact = _WinRT_GetActivationFactory("Windows.Foundation.PropertyValue", $sIID_IPropertyValueStatics)
    For $i = 0 To 9
        $pLVItem = IListViewFactory_CreateInstance($pListViewItem_Fact, 0, $pInner) ;Create Item

        ;Set Content to "Item #"
        $pProp = IPropertyValueStatics_CreateString($pProp_Fact, "Item " & $i)
        _WinRT_SwitchInterface($pLVItem, $sIID_IContentControl)
        IContentControl_SetContent($pLVItem, $pProp)
        IUnknown_Release($pProp)

        ;Add Item To ListView
        IVector_Append($pItemCol, $pLVItem)
        IUnknown_Release($pLVItem)
    Next
    IUnknown_Release($pListViewItem_Fact)

    ;Add the ListView to the grid
    _WinRT_SwitchInterface($pGrid, $sIID_IPanel)
    Local $pChildren = IPanel_GetChildren($pGrid)
    IVector_Append($pChildren, $pListView)
    IUnknown_Release($pChildren)
    IUnknown_Release($pGrid)

    ;Assign grid position - This is a bit surpurfulous (default pos is 0,0)
    _WinRT_SwitchInterface($pListView, $sIID_IDependencyObject)
    $pProp = IPropertyValueStatics_CreateInt32($pProp_Fact, 0)
    IDependencyObject_SetValue($pListView, $pRowProp, $pProp)
    IUnknown_Release($pProp)

    $pProp = IPropertyValueStatics_CreateInt32($pProp_Fact, 0)
    IDependencyObject_SetValue($pListView, $pColProp, $pProp)
    IUnknown_Release($pProp)

    IUnknown_Release($pProp_Fact)
    IUnknown_Release($pRowProp)
    IUnknown_Release($pColProp)

    Local $hGUI = GUICreate("Test Window", 200, 200)
    GUISetState()

    ;Attach the container to our GUI.
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSourceNative)
    IDesktopWindowXamlSourceNative_AttachToWindow($pDesktopWinXamlSrc, $hGUI)

    ;Position and show the island - (by default, the width and hight are 0)
    Local $hIsland = IDesktopWindowXamlSourceNative_GetWindowHandle($pDesktopWinXamlSrc)
    Local $tRect = _WinAPI_GetClientRect($hGUI)
    _WinAPI_SetWindowPos($hIsland, $HWND_TOP, 0, 0, $tRect.Right, $tRect.Bottom, $SWP_SHOWWINDOW)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ;Remove Delegate
    _WinRT_SwitchInterface($pListView, $sIID_ISelector)
    ISelector_RemoveHdlrSelectionChanged($pListView, $iSelectChgTkn)
    IUnknown_Release($pListView)
    $pSelectChgDgt = _WinRT_DestroyDelegate($pSelectChgDgt)

    ;Close out GUI etc
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IClosable)
    IClosable_Close($pDesktopWinXamlSrc)

    _WinRT_SwitchInterface($pWindowsXamlManager, $sIID_IClosable)
    IClosable_Close($pWindowsXamlManager)

    GUIDelete($hGUI)
EndFunc

Func SelChanged($pThis, $pSender, $pArgs)
    #forceref $pThis, $pSender, $pArgs
    Local $pAdded = ISelectionChangedEventArgs_GetAddedItems($pArgs)
    Local $pRemoved = ISelectionChangedEventArgs_GetRemovedItems($pArgs)

    _WinRT_SwitchInterface($pAdded, $sIID_IVector_1_Object_)
    Local $pItem, $pProp
    For $i = 0 To IVector_GetSize($pAdded) - 1
        $pItem = IVector_GetAt($pAdded, $i)
        _WinRT_SwitchInterface($pItem, $sIID_IContentControl)
        $pProp = IContentControl_GetContent($pItem)
        ConsoleWrite(" Added: " &  IPropertyValue_GetString($pProp) & @CRLF)
        IUnknown_Release($pProp)
        IUnknown_Release($pItem)
    Next
    IUnknown_Release($pAdded)

    _WinRT_SwitchInterface($pRemoved, $sIID_IVector_1_Object_)
    For $i = 0 To IVector_GetSize($pRemoved) - 1
        $pItem = IVector_GetAt($pRemoved, $i)
        _WinRT_SwitchInterface($pItem, $sIID_IContentControl)
        $pProp = IContentControl_GetContent($pItem)
        ConsoleWrite(" Removed: " &  IPropertyValue_GetString($pProp) & @CRLF)
        IUnknown_Release($pProp)
        IUnknown_Release($pItem)
    Next
    IUnknown_Release($pRemoved)

EndFunc
Posted
14 minutes ago, MattyD said:

It looks to be quite different to the win32 control at first glance, but admittedly I didn't spend too long investigating things.

anyway, here's a first attempt: ListView nested inside a 1x1 Grid (using v1.6 of the WinRT Libraries.)

Thanks Matty. I tested this along with the other examples included in your WinRT 1.6 release and it works very well.

The one thing that I find pretty cool about these is how they automatically use dark mode or light mode depending on what the user has set. And if you have one of the examples running and you switch between dark/light modes, the controls switch modes automatically without having to restart the examples. That is wild! :)

  • 2 months later...
Posted
On 12/14/2025 at 6:47 PM, MattyD said:

it seems there's issues with XAML controls that require keyboard input.

Had a bit of a breakthrough with this one. We learnt here that we can hook incoming messages - and with that, we now KB input.   I'll try to get tabbing between the island and the parent window working, but no promises at this stage... We might just have to take the win for now.

I'll look to integrate the hook into the WinRT library at some point soon.

#include <GUIConstants.au3>
#include <winapi.au3>

#include "..\Include\WinRT.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.WindowsXamlManager.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.DesktopWindowXamlSource.au3"
#include "..\Include\Interfaces\IDesktopWindowXamlSourceNative2.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.StackPanel.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.TextBox.au3"
#include "..\Include\Interfaces\Windows.Foundation.Collections.IVector.au3"

Global Const $tagMSG = "struct;hwnd hwnd;uint message;wparam wparam;lparam lparam;dword time;long X;long Y;dword lprivate;endstruct"
Global Const $HC_ACTION = 0
Global $__g_hMsgHook, $__g_hMsgHookProc
Global $__g_hDllUser32 = DllOpen("user32.dll")

Global $hGUI, $hIsland, $pWindowsXamlManager

Init()
BuildGUI()
AddHook()
Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE
RemoveHook()
Cleanup()

Func Init()
    _WinRT_Startup()

    ;Startup XAML Host
    Local $pXamlMgr_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.WindowsXamlManager", $sIID_IWindowsXamlManagerStatics)
    $pWindowsXamlManager = IWindowsXamlManagerStatics_InitializeForCurrentThread($pXamlMgr_Fact)
    IUnknown_Release($pXamlMgr_Fact)
EndFunc

Func BuildGUI()
    ;Create Container
    Local $pDesktopWinXamlSrc_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.DesktopWindowXamlSource", $sIID_IDesktopWindowXamlSourceFactory)
    Local $pInner
    Global $pDesktopWinXamlSrc = IDesktopWindowXamlSourceFactory_CreateInstance($pDesktopWinXamlSrc_Fact, 0, $pInner)
    IUnknown_Release($pDesktopWinXamlSrc_Fact)

    ;Create StackPanel
    Local $pStackPanel_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.StackPanel", $sIID_IStackPanelFactory)
    Local $pStackPanel = ITextBoxFactory_CreateInstance($pStackPanel_Fact, 0, $pInner)
    IDesktopWindowXamlSource_SetContent($pDesktopWinXamlSrc, $pStackPanel) ;Attach control to the container.
    IUnknown_Release($pStackPanel_Fact)

    ;CreateBoxes
    Local $pTextBox_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.TextBox", $sIID_ITextBoxFactory)
    Local $pTextBox1 = ITextBoxFactory_CreateInstance($pTextBox_Fact, 0, $pInner)
    Local $pTextBox2 = ITextBoxFactory_CreateInstance($pTextBox_Fact, 0, $pInner)
    IUnknown_Release($pTextBox_Fact)

    ;Add To stackpanel
    _WinRT_SwitchInterface($pStackPanel, $sIID_IPanel)
    Local $pChildren = IPanel_GetChildren($pStackPanel)
    IVector_Append($pChildren, $pTextBox1)
    IVector_Append($pChildren, $pTextBox2)
    IUnknown_Release($pChildren)
    IUnknown_Release($pStackPanel)
    IUnknown_Release($pTextBox2)

    ;SetFocus to textbox 1
    _WinRT_SwitchInterface($pTextBox1, $sIID_IControl)
    IControl_Focus($pTextBox1, 2)
    IUnknown_Release($pTextBox1)

    ;Create GUI and attach the container.
    Global $hGUI = GUICreate("", 300, 180)
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSourceNative)
    IDesktopWindowXamlSourceNative_AttachToWindow($pDesktopWinXamlSrc, $hGUI)

    ;Position and show the island
    Global $hIsland = IDesktopWindowXamlSourceNative_GetWindowHandle($pDesktopWinXamlSrc)
    Local $tRect = _WinAPI_GetClientRect($hGUI)
    _WinAPI_SetWindowPos($hIsland, $HWND_TOP, 10, 50, $tRect.Right - 20 , $tRect.Bottom - 60, $SWP_SHOWWINDOW)

    ;Lets us take focus away from the island for testing..
    GUICtrlCreateInput("", 10, 10, $tRect.Right - 20, 25)
    GUISetState()
EndFunc

Func AddHook()
    $__g_hMsgHookProc = DllCallbackRegister("GetMsgProc", "lresult", "int;wparam;lparam")
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSourceNative2)
    $__g_hMsgHook = _WinAPI_SetWindowsHookEx($WH_GETMESSAGE, DllCallbackGetPtr($__g_hMsgHookProc), _WinAPI_GetModuleHandle(0), _WinAPI_GetCurrentThreadId())
EndFunc

Func RemoveHook()
    _WinAPI_UnhookWindowsHookEx($__g_hMsgHook)
    DllCallbackFree($__g_hMsgHookProc)
EndFunc

Func GetMsgProc($iCode, $wParam, $lParam)
    Local $tMsg, $aCall

    If $iCode = $HC_ACTION Then
        $tMsg = DllStructCreate($tagMSG, $lParam)
        If $tMsg.hwnd = $hIsland Or _WinAPI_IsChild($tMsg.hwnd , $hIsland) Then
           If Not IDesktopWindowXamlSourceNative2_PreTranslateMessage($pDesktopWinXamlSrc, $tMsg) then
                DllCall($__g_hDllUser32, "bool", "TranslateMessage", "struct*", $tMsg)
                DllCall($__g_hDllUser32, "lresult", "DispatchMessageW", "struct*", $tMsg)
            EndIf
            $tMsg.message = $WM_NULL
        EndIf
    EndIf

    Return _WinAPI_CallNextHookEx(0, $iCode, $wParam, $lParam)
EndFunc

Func Cleanup()
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IClosable)
    IClosable_Close($pDesktopWinXamlSrc)
    GUIDelete($hGUI)

    _WinRT_SwitchInterface($pWindowsXamlManager, $sIID_IClosable)
    IClosable_Close($pWindowsXamlManager)
    _WinRT_Shutdown()
EndFunc

 

Posted (edited)

Hi all,

Here's some tabbing and reverse tabbing between worlds.

Theres a delegate that fires when you leave the XAML Island. (TakeFocusReq)

There's also be one that triggers when the Island gets focus (GotFocus) - but that doesn't work for me, not entirely sure why.. Maybe someone might have more luck on a win10 box? It should be teed up anyway, so if it works on another rig you'll get a "GotFocus Fired!!" message in the console.

As a workaround I installed a WH_CALLWNDPROC hook so we can see when the island gets focus, and which control passed it to us.

So in this demo we now have 2 hooks:

  1. one that fires between the dispatcher and the wndproc. We get some more meaningful messages at this level - after the raw mouse/kb messages have been interpreted.
  2. Then our original WH_GETMESSAGE hook is more that raw input processor that dishes stuff off to the island.

 Also, we need to pop a tabstop on our island.  So there's 2 ways to do this.

  1. Add the WS_TABSTOP to the $hIsland (given to us by XamlDesktopSource)
    • If $hIsland recieves KB focus, it immediately passes it off to its child.  (which actually handles the KB/Mouse input)
  2. Give $hIsland  WS_EX_CONTROLPARENT and add WS_TABSTOP to $hIsland's child

Anyway, enough of that...

Tab to go forward, shift-tab to go back:

;~ #AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

#include <GUIConstants.au3>
#include <winapi.au3>

#include "..\Include\WinRT.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.WindowsXamlManager.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.DesktopWindowXamlSource.au3"
#include "..\Include\Interfaces\IDesktopWindowXamlSourceNative2.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.StackPanel.au3"
#include "..\Include\Interfaces\Windows.Foundation.Collections.IVector.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Controls.TextBox.au3"
#include "..\Include\Interfaces\Windows.UI.Xaml.Hosting.IDesktopWindowXamlSourceGotFocusEventArgs.au3"
#include "..\Include\Interfaces\Windows.UI.Xaml.Hosting.IDesktopWindowXamlSourceTakeFocusRequestedEventArgs.au3"
#include "..\Include\Classes\Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationRequest.au3"

Global Const $tagMSG = "struct;hwnd hwnd;uint message;wparam wparam;lparam lparam;dword time;long X;long Y;dword lprivate;endstruct"
Global Const $tagCWPSTRUCT = "struct;lparam lparam;wparam wparam;uint message;hwnd hwnd;endstruct;"
Global Const $HC_ACTION = 0

Global $__g_hMsgHook, $__g_hMsgHookProc
Global $__g_hCallWndProc, $__g_hWndProcHook
Global $__g_hDllUser32

Global $hGUI, $hIsland, $hIslandInput, $pWindowsXamlManager, $pDesktopWinXamlSrc
Global $pGotFocus, $iTknGotFocus, $pTakeFocusReq, $iTknTakeFocusReq

Init()
BuildGUI()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Cleanup()

Func Init()
    _WinRT_Startup()
    If @error Then Exit MsgBox(0, "Oops", "Something went wrong, Are you using AutoIt x64?")

    $__g_hDllUser32 = DllOpen("user32.dll")

    ;Startup XAML Host
    Local $pXamlMgr_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.WindowsXamlManager", $sIID_IWindowsXamlManagerStatics)
    $pWindowsXamlManager = IWindowsXamlManagerStatics_InitializeForCurrentThread($pXamlMgr_Fact)
    IUnknown_Release($pXamlMgr_Fact)

    ;Create Container
    Local $pDesktopWinXamlSrc_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.DesktopWindowXamlSource", $sIID_IDesktopWindowXamlSourceFactory)
    Local $pInner
    $pDesktopWinXamlSrc = IDesktopWindowXamlSourceFactory_CreateInstance($pDesktopWinXamlSrc_Fact, 0, $pInner)
    IUnknown_Release($pDesktopWinXamlSrc_Fact)

    ;Setup Focus Delegates
;~  Windows.Foundation.TypedEventHandler`2<Windows.UI.Xaml.Hosting.DesktopWindowXamlSource, Windows.UI.Xaml.Hosting.DesktopWindowXamlSourceTakeFocusRequestedEventArgs>
;~  Windows.Foundation.TypedEventHandler`2<Windows.UI.Xaml.Hosting.DesktopWindowXamlSource, Windows.UI.Xaml.Hosting.DesktopWindowXamlSourceGotFocusEventArgs>
    $pGotFocus = _WinRT_CreateDelegate("GotFocus") ;Not working!
    $pTakeFocusReq = _WinRT_CreateDelegate("TakeFocusReq") ;Working!

    ;Register Delegates
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSource)
    $iTknGotFocus = IDesktopWindowXamlSource_AddHdlrGotFocus($pDesktopWinXamlSrc, $pGotFocus)
    $iTknTakeFocusReq = IDesktopWindowXamlSource_AddHdlrTakeFocusRequested($pDesktopWinXamlSrc, $pTakeFocusReq)
EndFunc   ;==>Init

Func BuildGUI()
    Local $pInner

    ;Create StackPanel
    Local $pStackPanel_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.StackPanel", $sIID_IStackPanelFactory)
    Local $pStackPanel = ITextBoxFactory_CreateInstance($pStackPanel_Fact, 0, $pInner)
    IDesktopWindowXamlSource_SetContent($pDesktopWinXamlSrc, $pStackPanel)
    IUnknown_Release($pStackPanel_Fact)

    ;CreateBoxes
    Local $pTextBox_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Controls.TextBox", $sIID_ITextBoxFactory)
    Local $pTextBox1 = ITextBoxFactory_CreateInstance($pTextBox_Fact, 0, $pInner)
    Local $pTextBox2 = ITextBoxFactory_CreateInstance($pTextBox_Fact, 0, $pInner)
    IUnknown_Release($pTextBox_Fact)

    ;Add To stackpanel
    _WinRT_SwitchInterface($pStackPanel, $sIID_IPanel)
    Local $pChildren = IPanel_GetChildren($pStackPanel)
    IVector_Append($pChildren, $pTextBox1)
    IVector_Append($pChildren, $pTextBox2)
    IUnknown_Release($pChildren)
    IUnknown_Release($pStackPanel)
    IUnknown_Release($pTextBox2)

    ;SetFocus to textbox 1
    _WinRT_SwitchInterface($pTextBox1, $sIID_IControl)
    IControl_Focus($pTextBox1, 2)
    IUnknown_Release($pTextBox1)

    ;Create GUI and attach the container.
    $hGUI = GUICreate("", 300, 180)
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSourceNative)
    IDesktopWindowXamlSourceNative_AttachToWindow($pDesktopWinXamlSrc, $hGUI)

    ;Position and show the island
    $hIsland = IDesktopWindowXamlSourceNative_GetWindowHandle($pDesktopWinXamlSrc)
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSource)
    Local $tRect = _WinAPI_GetClientRect($hGUI)
    _WinAPI_SetWindowPos($hIsland, $HWND_TOP, 10, 50, $tRect.Right - 20, $tRect.Bottom - 60, $SWP_SHOWWINDOW)

    ;Get underlying input window of the island. (Windows.UI.Input.InputSite.WindowClass)
    Local $aWnds = _WinAPI_EnumChildWindows($hIsland, False)
    $hIslandInput = $aWnds[1][0]

    ;Make the island tabb-able.
    ;(you can just add WS_TABSTOP to $hIsland instead - it just palms off focus to the child anyway!)
    Local $iExStyle = _WinAPI_GetWindowLong($hIsland, $GWL_EXSTYLE)
    _WinAPI_SetWindowLong($hIsland, $GWL_EXSTYLE, BitOR($iExStyle, $WS_EX_CONTROLPARENT))
    Local $iStyle = _WinAPI_GetWindowLong($aWnds[1][0], $GWL_STYLE)
    _WinAPI_SetWindowLong($aWnds[1][0], $GWL_STYLE, BitOR($iStyle, $WS_TABSTOP))

    ;Create other controls.
    GUICtrlCreateInput("", 10, 10, $tRect.Right / 2 - 20, 25)
    GUICtrlCreateButton("btn", 150, 10, 80, 25)

    ;Add Hooks
    $__g_hMsgHookProc = DllCallbackRegister("GetMsgProc", "lresult", "int;wparam;lparam")
    $__g_hCallWndProc = DllCallbackRegister("CallWndProc", "lresult", "int;wparam;lparam")
    $__g_hMsgHook = _WinAPI_SetWindowsHookEx($WH_GETMESSAGE, DllCallbackGetPtr($__g_hMsgHookProc), _WinAPI_GetModuleHandle(0), _WinAPI_GetCurrentThreadId())
    $__g_hWndProcHook = _WinAPI_SetWindowsHookEx($WH_CALLWNDPROC, DllCallbackGetPtr($__g_hCallWndProc), _WinAPI_GetModuleHandle(0), _WinAPI_GetCurrentThreadId())

    GUISetState()
EndFunc   ;==>BuildGUI

Func GetMsgProc($iCode, $wParam, $lParam)
    Local $tMsg

    If $iCode = $HC_ACTION Then
        $tMsg = DllStructCreate($tagMSG, $lParam)

        ;Handle XAML Island messages..
        If $tMsg.hwnd = $hIsland Or _WinAPI_IsChild($tMsg.hwnd, $hIsland) Then
            ;Probably could give the hook a dedicated ref to IDesktopWindowXamlSourceNative2 given we're essentialy in a loop.
            ;but meh - we're not here exessively under the "hIsland and child" filter.
            _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSourceNative2)
            If Not IDesktopWindowXamlSourceNative2_PreTranslateMessage($pDesktopWinXamlSrc, $tMsg) Then
                DllCall($__g_hDllUser32, "bool", "TranslateMessage", "struct*", $tMsg)
                DllCall($__g_hDllUser32, "lresult", "DispatchMessageW", "struct*", $tMsg)
            EndIf
            $tMsg.message = $WM_NULL
        EndIf
    EndIf

    Return _WinAPI_CallNextHookEx(0, $iCode, $wParam, $lParam)
EndFunc   ;==>GetMsgProc

Func Cleanup()
    ;Remove Hooks
    _WinAPI_UnhookWindowsHookEx($__g_hMsgHook)
    _WinAPI_UnhookWindowsHookEx($__g_hWndProcHook)
    DllCallbackFree($__g_hMsgHookProc)
    DllCallbackFree($__g_hCallWndProc)

    ;Remove Delegates
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSource)
    IDesktopWindowXamlSource_RemoveHdlrGotFocus($pDesktopWinXamlSrc, $iTknGotFocus)
    IDesktopWindowXamlSource_RemoveHdlrGotFocus($pDesktopWinXamlSrc, $iTknTakeFocusReq)
    _WinRT_DestroyDelegate($pGotFocus)
    _WinRT_DestroyDelegate($pTakeFocusReq)

    ;Close Island
    _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IClosable)
    IClosable_Close($pDesktopWinXamlSrc)

    ;Delete GUI
    GUIDelete($hGUI)

    ;Close XamlMgr
    _WinRT_SwitchInterface($pWindowsXamlManager, $sIID_IClosable)
    IClosable_Close($pWindowsXamlManager)

    _WinRT_Shutdown()
    DllClose($__g_hDllUser32)
EndFunc   ;==>Cleanup

Func GotFocus($pThis, $pSender, $pArgs)
    ConsoleWrite("GotFocus Fired!!" & @CRLF)

    ;Not working (can't get this to trigger...)
    _WinRT_SwitchInterface($pSender, $sIID_IDesktopWindowXamlSource)
    Local $pNavReq_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationRequest", $sIID_IXamlSourceFocusNavigationRequestFactory)
    Local $pNavReq = IXamlSourceFocusNavigationRequestFactory_CreateInstance($pNavReq_Fact, $mXamlSourceFocusNavigationReason["First"])
    IDesktopWindowXamlSource_NavigateFocus($pSender, $pNavReq)
EndFunc   ;==>GotFocus

Func TakeFocusReq($pThis, $pSender, $pArgs)
    ;Leaving the Island.

    ;we might have left with shift-tab, so we need to determine which ctrl to tab to.
    ;I'm not dealing with arrow keys atm.
    Local $pNavReq = IDesktopWindowXamlSourceTakeFocusRequestedEventArgs_GetRequest($pArgs)
    Local $iReason = IXamlSourceFocusNavigationRequest_GetReason($pNavReq)
    Switch _WinRT_GetEnum($mXamlSourceFocusNavigationReason, $iReason)
        Case "First"
            _WinAPI_SetFocus(_WinAPI_GetNextDlgTabItem($hGUI, $hIslandInput))
        Case "Last"
            _WinAPI_SetFocus(_WinAPI_GetNextDlgTabItem($hGUI, $hIslandInput, True))
    EndSwitch
    ConsoleWrite("Set Focus to " & _WinRT_GetEnum($mXamlSourceFocusNavigationReason, $iReason) & " Win32 Ctrl" & @CRLF)
    IUnknown_Release($pNavReq)
EndFunc   ;==>TakeFocusReq

Func CallWndProc($iCode, $wParam, $lParam)
    ;This hook fires after the msg is dispatched, but before it hits a wndproc.
    ;We also should see sendmessage msgs here. These ones never hit the queue, they go straight to the wndproc after this.

    Local $tCWPSTRUCT = DllStructCreate($tagCWPSTRUCT, $lParam)
    Local Static $hFocusFrom

    If $iCode = $HC_ACTION Then
        If $tCWPSTRUCT.hwnd = $hIslandInput Then
            If $tCWPSTRUCT.message = $WM_SETFOCUS Then

                ;Determine which end of the XAML taborder we need to navigate to.
                Local $sTarget = "First"
                If $hFocusFrom = _WinAPI_GetNextDlgTabItem($hGUI, $hIslandInput) Then $sTarget = "Last"
                $hFocusFrom = 0
                ConsoleWrite("Set Focus to " & $sTarget & " XAML Ctrl" & @CRLF)

                ;Navigate!
                _WinRT_SwitchInterface($pDesktopWinXamlSrc, $sIID_IDesktopWindowXamlSource)
                Local $pNavReq_Fact = _WinRT_GetActivationFactory("Windows.UI.Xaml.Hosting.XamlSourceFocusNavigationRequest", $sIID_IXamlSourceFocusNavigationRequestFactory)
                Local $pNavReq = IXamlSourceFocusNavigationRequestFactory_CreateInstance($pNavReq_Fact, $mXamlSourceFocusNavigationReason[$sTarget])
                IDesktopWindowXamlSource_NavigateFocus($pDesktopWinXamlSrc, $pNavReq)
                IUnknown_Release($pNavReq)
            EndIf

        ElseIf $tCWPSTRUCT.message = $WM_KILLFOCUS Then
            ;Store the control which is handing the XAML Island focus.
            If $tCWPSTRUCT.wParam = $hIslandInput Then $hFocusFrom = $tCWPSTRUCT.hwnd
        EndIf
    EndIf

    Return _WinAPI_CallNextHookEx(0, $iCode, $wParam, $lParam)
EndFunc   ;==>CallWndProc

Func _WinAPI_GetNextDlgTabItem($hDialog, $hCtrl, $bPrevious = 0)
    Local $aCall = DllCall($__g_hDllUser32, "hwnd", "GetNextDlgTabItem", "hwnd", $hDialog, "hwnd", $hCtrl, "bool", $bPrevious)
    If @error Then Return SetError(@error, @extended, -1)
    Return $aCall[0]
EndFunc   ;==>_WinAPI_GetNextDlgTabItem

 

Edited by MattyD

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...