Context Menus are sometimes not shown in High-DPI applications #2097
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Addresses #2088
As part of a previous fix (that shipped originally as part of .NET 4.8 in), a change was made to
Popup
that involved the destruction and recreation of the underlyingHWND
. This was done to ensure that theHWND
was always created with the correct monitor (~=DPI) affinity.In order to acheive this,
DestroyWindow()
andBuildWindow()
- private methods inPopup
- were used.We are finding that
DestroyWindow()
has side-effects that can lead to incorrect behavior. The incorrect beahvior works as follows:Popup.CreateWindow
is usually a consequencePopup.IsOpenChanged
(false -> true
)Popup.CreateWindow
,DestroyWindow()
is called (when high-dpi mode is detected).DestroyWindow()
destroys the underlyingHWND
, releases the capture, raises the OnClosed event and clears the placement-target.DestroyWindow()
, we getIsOpen == false
.DestroyWindow()
, we call intoBuildWindow()
andCreateNewPopupRoot()
etc., which go on to build thePopup
again (and also instnatiate a newHWND
).IsOpen
back totrue
(without also leading to an undesirable infinite-recursion that calls back intoCreateWindow
).If these calls to show the context-menu arise from
ContextMenu.OnIsOpenChanged
, and flow throughContextMenu.HookupParentPopup -> Popup.CreateRootPopup
, thenIsOpen
gets reset (there is a direct call intoSetBinding(IsOpenProperty)
, inPopup.CreateRootPopupInternal
) and the popup is shown correctly. Until then, the context-menu is "stuck" not being able to be shown.The solution is to stop using
DestroyWindow()
as-is, which does more than what we need for it to accomplish. Our original intent in callingDestroyWindow()
was simply destroy and recreate theHWND
. This fix refactorsDestroyWindow()
to suit this need and uses the newly introducedDestroyWindowImpl()
to destroy theHWND
, and then recreate just that. The rest of the state is retained intact, and thePopup/ContextMenu
continues to function well as before.