[release/3.0] Context Menus are sometimes not shown in High-DPI applications #2099
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
.NET 5 PR: #2097
.NET Core 3.1 PR: #2098
Description (Summary)
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.This previous fix depended upon a private helper-method (
Popup.DestroyWindow
) that had side-effects that were not accounted for in the original fix. One of the side effects is that theContextMenu
is not sown consistently.The solution modifies (refactors) the private method to extract the useful portion and uses it to improve the previous fix.
Customer Impact
This is a forward-port from from .NET 4.8. This was reported by a customer, and has also been discovered by Visual Studio internally.
Regression
Regression introduced by .NET 4.8. .NET Core 3.0 shipped with this bug.
Risk
Low - this has been well tested internally and validated by multiple customers. The fix is well understood and small/scoped.
Details
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.