microsoft / microsoft-ui-xaml

WinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.
MIT License
6.38k stars 683 forks source link

Xaml Islands: Opening the context menu on a TextBox on a secondary window after opening one on main window focuses the main app window #5341

Open lyahdav opened 3 years ago

lyahdav commented 3 years ago

Steps to reproduce the bug

  1. Make a Xaml Islands app with two Xaml Roots on two win32 Windows. In each insert a Xaml TextBox.
  2. Run app.
  3. Right click on TextBox in 2nd window. Context menu shows correctly.
  4. Right click on TextBox in 1st window.

Expected behavior TextBox context menu shows in 1st window. Instead the context menu doesn't show and 2nd window gets focused.

Screenshots https://user-images.githubusercontent.com/359157/124189731-08965c80-da76-11eb-9950-b481779be7fc.mov

Version Info Windows 10 21H1 / 19043.928 Islands

Device form factor Saw the problem?
Desktop Yes
Xbox
Surface Hub
IoT

Additional context

Here's a backtrace of the code causing the wrong window to set focus. At the top of the stack (not shown) Xaml is sending a WM_SETFOCUS event to the wrong win32 window. I think what's happening is that TextBox.ContextFlyout is a singleton and it's not correctly updating the associated XamlIslandsRoot when it's opened. After opening a TextBox context menu the first time it gets set to that window's XamlIslandsRoot and then it never gets updated. I tried hooking into TextBox.ContextFlyout.Opening event and setting flyout.XamlRoot(flyout.Target().XamlRoot()) but that didn't help.

    user32.dll!00007ff965ebe858()   Unknown
    user32.dll!00007ff965ebe3dc()   Unknown
    user32.dll!00007ff965ed0bc3()   Unknown
    ntdll.dll!00007ff9672d0b54()    Unknown
    win32u.dll!00007ff964d61a64()   Unknown
    InputHost.dll!00007ff955cb8c49()    Unknown
    InputHost.dll!00007ff955cb8d1b()    Unknown
    Windows.UI.Xaml.dll!CXamlIslandRoot::SetFocus() Line 1068   C++
    Windows.UI.Xaml.dll!CFocusManager::UpdateFocus(const Focus::FocusMovement & movement) Line 1960 C++
    Windows.UI.Xaml.dll!CFocusManager::SetFocusedElement(const Focus::FocusMovement & movement) Line 130    C++
    Windows.UI.Xaml.dll!CUIElement::Focus(DirectUI::FocusState focusState, bool animateIfBringIntoView, bool * focusChanged, DirectUI::FocusNavigationDirection focusNavigationDirection) Line 6288 C++
    Windows.UI.Xaml.dll!CPopup::SetFocus(DirectUI::FocusState focusState) Line 303  C++
    Windows.UI.Xaml.dll!CPopup::Open() Line 448 C++
    Windows.UI.Xaml.dll!CPopup::SetValue(const SetValueParams & args) Line 164  C++
    [Inline Frame] Windows.UI.Xaml.dll!DependencyObject_SetValue(CDependencyObject *) Line 2351 C++
    Windows.UI.Xaml.dll!DirectUI::DependencyObject::SetValueCore(const SetValueParams & args, bool fSetEffectiveValueOnly) Line 1052    C++
    [Inline Frame] Windows.UI.Xaml.dll!DirectUI::DependencyObject::SetValueCore(const CDependencyProperty *) Line 273   C++
    Windows.UI.Xaml.dll!DirectUI::DependencyObject::SetValueByKnownIndex<unsigned char>(KnownPropertyIndex nPropertyIndex, unsigned char value) Line 392    C++
    Windows.UI.Xaml.dll!DirectUI::FlyoutBase::Open() Line 1413  C++
    Windows.UI.Xaml.dll!DirectUI::FlyoutBase::ShowAtCore(Windows::UI::Xaml::IFrameworkElement * pPlacementTarget) Line 1253 C++
    Windows.UI.Xaml.dll!DirectUI::FlyoutBase::ShowAtImpl(Windows::UI::Xaml::IDependencyObject * pPlacementTarget, Windows::UI::Xaml::Controls::Primitives::IFlyoutShowOptions * pShowOptions) Line 1198 C++
    Windows.UI.Xaml.dll!DirectUI::FlyoutBaseGenerated::ShowAt(Windows::UI::Xaml::IDependencyObject * pPlacementTarget, Windows::UI::Xaml::Controls::Primitives::IFlyoutShowOptions * pShowOptions) Line 639 C++
    Windows.UI.Xaml.dll!DirectUI::FlyoutBase::ShowAtStatic(CFlyoutBase * pFlyoutBase, CFrameworkElement * pTarget, Windows::Foundation::Point point, Windows::Foundation::Rect exclusionRect, Windows::UI::Xaml::Controls::Primitives::FlyoutShowMode flyoutShowMode) Line 4164 C++
    Windows.UI.Xaml.dll!DirectUI::TextControlFlyout::ShowAt(Windows::Foundation::Point point, Windows::Foundation::Rect exclusionRect, Windows::UI::Xaml::Controls::Primitives::FlyoutShowMode showMode) Line 357   C++
    Windows.UI.Xaml.dll!DirectUI::TextControlFlyoutHelper::ShowAt(CFlyoutBase * flyout, CFrameworkElement * owner, Windows::Foundation::Point point, Windows::Foundation::Rect exclusionRect, Windows::UI::Xaml::Controls::Primitives::FlyoutShowMode showMode) Line 179    C++
    Windows.UI.Xaml.dll!CFxCallbacks::TextControlFlyout_ShowAt(CFlyoutBase * flyoutBase, CFrameworkElement * target, Windows::Foundation::Point point, Windows::Foundation::Rect exclusionRect, Windows::UI::Xaml::Controls::Primitives::FlyoutShowMode flyoutShowMode) Line 1551   C++
    Windows.UI.Xaml.dll!CUIElement::OnContextRequestedCore(CDependencyObject * sender, CDependencyObject * contextFlyoutObject, CEventArgs * eventArgs) Line 14476  C++
    Windows.UI.Xaml.dll!DirectUI::Control::ShowContextFlyout(Windows::UI::Xaml::Input::IContextRequestedEventArgs * pArgs, DirectUI::Control * pContextFlyoutControl) Line 1403 C++
    Windows.UI.Xaml.dll!DirectUI::TextBox::OnContextRequestedImpl(Windows::UI::Xaml::Input::IContextRequestedEventArgs * pArgs) Line 400    C++
    Windows.UI.Xaml.dll!DirectUI::Control::FireEvent(KnownEventIndex nDelegate, DirectUI::DependencyObject * pSender, IInspectable * pArgs) Line 227    C++
    [Inline Frame] Windows.UI.Xaml.dll!DirectUI::DXamlCore::FireEvent(CDependencyObject *) Line 1979    C++
    [Inline Frame] Windows.UI.Xaml.dll!AgCoreCallbacks::FireEvent(CDependencyObject *) Line 92  C++
    [Inline Frame] Windows.UI.Xaml.dll!CFxCallbacks::JoltHelper_FireEvent(CDependencyObject *) Line 1015    C++
    Windows.UI.Xaml.dll!CCoreServices::CLR_FireEvent(CDependencyObject * pListener, EventHandle hEvent, CDependencyObject * pSender, CEventArgs * pArgs, unsigned int flags) Line 3234  C++
    Windows.UI.Xaml.dll!CommonBrowserHost::CLR_FireEvent(CDependencyObject * pListener, EventHandle hEvent, CDependencyObject * pSender, CEventArgs * pArgs, unsigned int flags) Line 771   C++
    Windows.UI.Xaml.dll!CControlBase::ScriptCallback(void * pControl, CDependencyObject * pListener, EventHandle hEvent, CDependencyObject * pSender, CEventArgs * pArgs, int flags, IScriptObject * pScriptObject, HRESULT(*)(CDependencyObject *, CEventArgs *) pInternalHandler) Line 267    C++
    Windows.UI.Xaml.dll!CXcpDispatcher::OnScriptCallback(CEventInfo * pEventInfo) Line 1347 C++
    Windows.UI.Xaml.dll!CXcpDispatcher::OnReentrancyProtectedWindowMessage(HWND__ * msg, unsigned int lParam, unsigned __int64) Line 1021   C++
    [Inline Frame] Windows.UI.Xaml.dll!CXcpDispatcher::ProcessMessage(HWND__ *) Line 890    C++
    Windows.UI.Xaml.dll!CXcpDispatcher::WindowProc(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 839   C++
    user32.dll!00007ff965ebe858()   Unknown
    user32.dll!00007ff965ebe3dc()   Unknown
    user32.dll!00007ff965ed0bc3()   Unknown
    ntdll.dll!00007ff9672d0b54()    Unknown
    win32u.dll!00007ff964d61124()   Unknown
    user32.dll!00007ff965ebdf02()   Unknown
    user32.dll!00007ff965ebd68a()   Unknown
    [Inline Frame] Windows.UI.Xaml.dll!CXcpDispatcher::SendMessageW(unsigned int) Line 809  C++
    Windows.UI.Xaml.dll!CXcpBrowserHost::SyncScriptCallbackRequest(void * pVoidBH, CDependencyObject * pListener, EventHandle hEvent, CDependencyObject * pSender, CEventArgs * pArgs, int flags, IScriptObject * pScriptObject, HRESULT(*)(CDependencyObject *, CEventArgs *) pHandler) Line 1048  C++
    [Inline Frame] Windows.UI.Xaml.dll!CEventManager::RaiseControlEvents(EventHandle) Line 1133 C++
    Windows.UI.Xaml.dll!CEventManager::Raise(EventHandle hEvent, int bRefire, CDependencyObject * pSender, CEventArgs * pArgs, bool fRaiseSync, bool fInputEvent, bool bAllowErrorFallback, CDependencyObject * pSenderOverride) Line 887   C++
    Windows.UI.Xaml.dll!CEventManager::RaiseRoutedEventBubbling(EventHandle hEvent, CDependencyObject * pSource, CRoutedEventArgs * pArgs, bool bIgnoreVisibility, bool fRaiseSync, bool fInputEvent, CDependencyObject * coerceToHandledAtElement) Line 1334   C++
    [Inline Frame] Windows.UI.Xaml.dll!CEventManager::RaiseRoutedEvent(EventHandle) Line 1241   C++
    Windows.UI.Xaml.dll!ContentRootInput::ContextMenuProcessor::RaiseContextRequestedEvent(CDependencyObject * pSource, Windows::Foundation::Point point, bool isTouchInput) Line 67    C++
    Windows.UI.Xaml.dll!CInputManager::RaiseContextRequestedEvent(CDependencyObject * pSource, Windows::Foundation::Point point, bool) Line 186 C++
    Windows.UI.Xaml.dll!CInputServices::ProcessGestureInput(CDependencyObject * pElement, TouchInteractionMsg * pMsg) Line 2510 C++
    Windows.UI.Xaml.dll!CInputServices::ProcessTouchInteractionCallback(const xref_ptr<CUIElement> & element, TouchInteractionMsg * message) Line 2312  C++
    Windows.UI.Xaml.dll!CCoreServices::ProcessTouchInteractionCallback(const xref_ptr<CUIElement> & element, TouchInteractionMsg * message) Line 1019   C++
    Windows.UI.Xaml.dll!GestureRecognizerAdapter::OnRightTapped(ElementGestureTracker * elementGestureTracker, Windows::UI::Input::IGestureRecognizer * args, Windows::UI::Input::IRightTappedEventArgs *) Line 178 C++
    [Inline Frame] Windows.UI.Xaml.dll!GestureRecognizerAdapter::Init::__l11::<lambda_3afc669bded338f07346825eaa66d3ab>::operator()(Windows::UI::Input::IGestureRecognizer *) Line 38   C++
    Windows.UI.Xaml.dll!Microsoft::WRL::Details::DelegateArgTraits<long (__cdecl Windows::Foundation::ITypedEventHandler_impl<Windows::Foundation::Internal::AggregateType<Windows::UI::Input::GestureRecognizer *,Windows::UI::Input::IGestureRecognizer *>,Windows::Foundation::Internal::AggregateType<Windows::UI::Input::RightTappedEventArgs *,Windows::UI::Input::IRightTappedEventArgs *>>::*)(Windows::UI::Input::IGestureRecognizer *,Windows::UI::Input::IRightTappedEventArgs *)>::DelegateInvokeHelper<Microsoft::WRL::Implements<Microsoft::WRL::RuntimeClassFlags<2>,Windows::Foundation::ITypedEventHandler<Windows::UI::Input::GestureRecognizer *,Windows::UI::Input::RightTappedEventArgs *>,Microsoft::WRL::FtmBase>,<lambda_3afc669bded338f07346825eaa66d3ab> &,-1,Windows::UI::Input::IGestureRecognizer *,Windows::UI::Input::IRightTappedEventArgs *>::Invoke(Windows::UI::Input::IGestureRecognizer * <args_0>, Windows::UI::Input::IRightTappedEventArgs * <args_1>) Line 245 C++
    Windows.UI.dll!00007ff955f59c31()   Unknown
    Windows.UI.dll!00007ff955f5967c()   Unknown
    Windows.UI.dll!00007ff955f66d72()   Unknown
    Windows.UI.dll!00007ff955f6691b()   Unknown
    Windows.UI.dll!00007ff955f66ede()   Unknown
    ninput.dll!00007ff95dbbb196()   Unknown
    ninput.dll!00007ff95dbc0798()   Unknown
    ninput.dll!00007ff95dbdf27f()   Unknown
    ninput.dll!00007ff95dbdf246()   Unknown
    ninput.dll!00007ff95dbde9fa()   Unknown
    ninput.dll!00007ff95dbdeacf()   Unknown
    ninput.dll!00007ff95dbdd3ea()   Unknown
    ninput.dll!00007ff95dbc11b0()   Unknown
    ninput.dll!00007ff95dbc0ac7()   Unknown
    ninput.dll!00007ff95dbbeff4()   Unknown
    Windows.UI.dll!00007ff955f677f5()   Unknown
    Windows.UI.Xaml.dll!ElementGestureTracker::ProcessPointerInformation(const InputMessage & msg, Windows::UI::Input::IPointerPointStatics * const pointerPointStatics) Line 715   C++
    Windows.UI.Xaml.dll!ElementGestureTracker::ProcessPointerMessage(const InputMessage & msg, Windows::UI::Input::IPointerPointStatics * const pointerPointStatics) Line 91    C++
    Windows.UI.Xaml.dll!CInputServices::ProcessPointerMessagesWithInteractionEngine(unsigned int pMsg, InputMessage * pInteractionElement, CUIElement * pManipulationElement, CUIElement * bIgnoreManipulationElement, unsigned int pInteractionContext, ElementGestureTracker * bForceDisableGesture, unsigned int) Line 1994  C++
    Windows.UI.Xaml.dll!CInputServices::ProcessInteractionPointerMessages(unsigned int pointerId, InputMessage * pMsg) Line 1760    C++
    Windows.UI.Xaml.dll!ContentRootInput::PointerInputProcessor::ProcessPointerInput(InputMessage * pMsg, int * handled) Line 716   C++
    Windows.UI.Xaml.dll!CInputServices::ProcessInput(InputMessage * pMsg, CContentRoot * contentRoot, int * handled) Line 889   C++
    Windows.UI.Xaml.dll!CCoreServices::ProcessInput(InputMessage * pMessage, CContentRoot * contentRoot, int * fHandled) Line 993   C++
    Windows.UI.Xaml.dll!CXcpBrowserHost::HandleInputMessage(unsigned int uMsg, MsgPacket * pMsgPack, CContentRoot * contentRoot, bool & fHandled) Line 1411 C++
    Windows.UI.Xaml.dll!CJupiterControl::HandlePointerMessage(unsigned int uMsg, unsigned __int64 wParam, __int64 lParam, CContentRoot * contentRoot, Windows::UI::Input::IPointerPoint * pointerPoint, Windows::UI::Core::IPointerEventArgs * pointerArgs) Line 750    C++
    Windows.UI.Xaml.dll!CJupiterWindow::OnIslandPointerMessage(unsigned int uMsg, CContentRoot * contentRoot, Windows::UI::Core::IPointerEventArgs * args) Line 2315    C++
    Windows.UI.Xaml.dll!CXamlIslandRoot::InjectPointerMessage(unsigned int msg, Windows::UI::Core::IPointerEventArgs * args) Line 617   C++
    [Inline Frame] Windows.UI.Xaml.dll!CXamlIslandRoot::OnIslandPointerReleased(IInspectable *) Line 563    C++
    [Inline Frame] Windows.UI.Xaml.dll!CXamlIslandRoot::SubscribeToPointerInputObserverEvents::__l43::<lambda_85fbf4d407c7e311044a83519608d385>::operator()(Windows::UI::Internal::Input::IPointerInputObserver *) Line 1406    C++
    Windows.UI.Xaml.dll!Microsoft::WRL::Details::DelegateArgTraits<long (__cdecl Windows::Foundation::ITypedEventHandler_impl<Windows::Foundation::Internal::AggregateType<Windows::UI::Internal::Input::PointerInputObserver *,Windows::UI::Internal::Input::IPointerInputObserver *>,Windows::Foundation::Internal::AggregateType<Windows::UI::Core::PointerEventArgs *,Windows::UI::Core::IPointerEventArgs *>>::*)(Windows::UI::Internal::Input::IPointerInputObserver *,Windows::UI::Core::IPointerEventArgs *)>::DelegateInvokeHelper<Microsoft::WRL::Implements<Microsoft::WRL::RuntimeClassFlags<2>,Windows::Foundation::ITypedEventHandler<Windows::UI::Internal::Input::PointerInputObserver *,Windows::UI::Core::PointerEventArgs *>,Microsoft::WRL::FtmBase>,<lambda_85fbf4d407c7e311044a83519608d385> &,-1,Windows::UI::Internal::Input::IPointerInputObserver *,Windows::UI::Core::IPointerEventArgs *>::Invoke(Windows::UI::Internal::Input::IPointerInputObserver * <args_0>, Windows::UI::Core::IPointerEventArgs * <args_1>) Line 245 C++
StephenLPeters commented 3 years ago

@Austin-Lamb and @marb2000 FYI on an Islands issue.

rozele commented 3 years ago

A rather simple workaround for this is to create an instance of TextCommandBarFlyout for each TextBox and TextBlock view you create in your XAML Islands app:

TextBlock textBlock;
TextCommandBarFlyout textBlockFlyout;
textBlock.ContextFlyout(textBlockFlyout);
textBlock.SelectionFlyout(textBlockFlyout);

TextBox textBox;
TextCommandBarFlyout textBoxFlyout;
textBox.ContextFlyout(textBoxFlyout);
textBox.SelectionFlyout(textBoxFlyout);

You could probably even go as far as to create a TextCommandBarFlyout per XamlRoot to save memory.

lyahdav commented 3 years ago

One thing that needs to be added to the workaround above to make the Flyout position correctly: textBlockFlyout.Placement(xaml::Controls::Primitives::FlyoutPlacementMode::BottomEdgeAlignedLeft);

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 5 days.

lyahdav commented 1 year ago

Likely still relevant

JesseCol commented 1 year ago

WinAppSDK Xaml Islands doesn't have this same precise problem, but there are still some issues with this scenario so I'm leaving this open. Thanks all for posting information about workarounds.