Skip to content

Commit 28bdf8b

Browse files
authored
Merge pull request #3046 from MicrosoftDocs/master636905150352080365
For protected CLA branch, push strategy should use PR and merge to target branch method to work around git push error
2 parents 1719f4a + 6c6e210 commit 28bdf8b

10 files changed

+275
-16
lines changed

docs/cross-platform/change-log-visual-studio-tools-for-unity.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Visual Studio Tools for Unity change log.
122122

123123
- **Integration:**
124124

125-
- Added support for Visual Studio 2019.
125+
- Added support for Visual Studio 2019 (you need at least Unity 2018.3 for being able to use Visual Studio 2019 as an external script editor).
126126

127127
- Adopted the Visual Studio image service and catalog, with full support for HDPI scaling, pixel perfect images and theming.
128128

docs/extensibility/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@
423423
href: ux-guidelines/animations-for-visual-studio.md
424424
- name: Evaluation Tools
425425
href: ux-guidelines/evaluation-tools-for-visual-studio.md
426+
- name: Per-monitor-awareness support
427+
href: ux-guidelines/per-monitor-awareness-extenders.md
426428
- name: Addressing DPI Issues
427429
href: addressing-dpi-issues2.md
428430
- name: Image Service and Catalog
Loading
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
---
2+
title: "Per-Monitor Awareness support for Visual Studio extenders"
3+
titleSuffix: ""
4+
description: "Learn about the new extender support for per-monitor-awareness available in Visual Studio 2019."
5+
ms.date: 04/09/2019
6+
helpviewer_keywords:
7+
- "Visual Studio, PMA, per-monitor-awareness, extenders, Windows Forms"
8+
- "Per-Monitor Awareness support for extenders"
9+
ms.assetid:
10+
author: rub8n
11+
ms.author: rurios
12+
manager: anthc
13+
ms.prod: visual-studio-windows
14+
ms.technology: vs-ide-general
15+
ms.topic: reference
16+
ms.workload:
17+
- "multiple"
18+
---
19+
20+
# Per-Monitor Awareness support for Visual Studio extenders
21+
22+
Versions prior to Visual Studio 2019 had their DPI awareness context set to system aware, rather than a per-monitor DPI aware (PMA). Running in System awareness resulted in a degraded visual experience (e.g. blurry fonts or icons) whenever Visual Studio had to render across monitors with different scale factors or remote into machines with different display configurations (e.g. different Windows scaling).
23+
24+
The DPI awareness context of Visual Studio 2019 is set as per-monitor aware, allowing Visual Studio to render according to the configuration of the display where it's hosted rather than a generic system defined configuration. Ultimately translating into a crisp UI for surface areas that implement PMA support.
25+
26+
Refer to the [High DPI Desktop Application Development on Windows](https://docs.microsoft.com/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows) documentation for more information about the terms and overall scenario covered in this document.
27+
28+
## Quickstart
29+
- Ensure Visual Studio is running in PMA mode (See **Enabling PMA**)
30+
31+
- Validate your extension works correctly across a set of common scenarios (See **Testing your extensions for PMA issues**)
32+
33+
- If you find issues, you’ll need to add the new PMA nugget package, diagnose, and fix issues using the strategies & recommendations discussed in this document
34+
35+
## Enabling PMA
36+
To enable PMA in Visual Studio, the following requirements need to be met:
37+
1) Windows 10 April 2018 Update (v1803, RS4) or later
38+
2) .NET Framework 4.8 RTM or greater (currently ships as standalone preview or bundle with recent Windows Insider builds)
39+
3) Visual Studio 2019 with the ["Optimize rendering for screens with different pixel densities"](https://docs.microsoft.com/visualstudio/ide/reference/general-environment-options-dialog-box?view=vs-2019) option enabled
40+
41+
Once these requirements are met, Visual Studio will automatically enable PMA across the process.
42+
43+
## Testing your extensions for PMA issues
44+
45+
Visual Studio officially supports the WPF, Windows Forms, Win32, and HTML/JS UI frameworks. When Visual Studio is put into PMA mode, the different UI stacks behave differently. Therefore, regardless of UI framework, it's recommended that a test pass is performed to ensure all UI is compliant with PMA.
46+
47+
Regardless of the UI framework your extension supports, it's recommended you validate the following common scenarios:
48+
49+
1. Changing the scale factor of a single monitor environment while the application is running*
50+
- This scenario helps test that UI is responding to the dynamic Windows DPI change
51+
52+
2. Docking/undocking a laptop where an attached monitor is set to the primary and the attached monitor has a different scale factor than the primary while the application is running.
53+
- This scenario helps test that UI is responding to the display DPI change as well as handling displays dynamically being added or removed
54+
55+
3. Having multiple monitors with different scale factors and moving the application between them.
56+
- This scenario helps test that UI is responding to the display DPI change
57+
58+
4. Remoting into a machine when the local and remote machines have different scale factors for the primary monitor.
59+
- This scenario helps test that UI is responding to the dynamic Windows DPI change
60+
61+
A good preliminary test for whether UI might have problems is whether or not the code utilizes either the *Microsoft.VisualStudio.Utilities.Dpi.DpiHelper* or *Microsoft.VisualStudio.PlatformUI.DpiHelper* classes. These old DpiHelper classes only support System DPI awareness and won’t always function correctly when the process is PMA.
62+
63+
Typical usage of these DpiHelpers will look like:
64+
65+
```cs
66+
Point screenTopRight = logicalBounds.TopRight.LogicalToDeviceUnits();
67+
68+
POINT screenIntTopRight = new POINT
69+
{
70+
x = (int)screenTopRIght.X,
71+
y = (int)screenTopRIght.Y
72+
}
73+
74+
IntPtr monitor = NativeMethods.MonitorFromPoint(screenIntTopRight, NativeMethods.Monitor_DEFAULTTONEARST);
75+
```
76+
77+
In the previous example, a rectangle representing the logical bounds of a window is converted to device units so that it can be passed to the native method MonitorFromPoint that expects device coordinates in order to return back an accurate monitor pointer.
78+
79+
### Classes of issues
80+
81+
When PMA is enabled for Visual Studio, the UI could replicate issues in several common ways. Most, if not all, of these issues can happen in any of Visual Studios supported UI frameworks. Additionally, these issues can happen even when a piece of UI is being hosted in mixed-mode DPI scaling scenarios (refer to the Windows [documentation](https://docs.microsoft.com/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows) to learn more).
82+
83+
#### Win32 window creation
84+
When creating windows with CreateWindow() or CreateWindowEx(), a common pattern is to create the window at coordinates 0,0 (the top/left corner of the primary display), then move it to its final position. However, doing so can cause the window to trigger a DPI changed message or event, which can retrigger other UI messages or events, and eventually lead to undesired behavior or rendering.
85+
86+
#### WPF element placement
87+
When moving WPF elements using the old Microsoft.VisualStudio.Utilities.Dpi.DpiHelper, top-left coordinates might not be calculated correctly whenever elements are in the non-primary DPI case.
88+
89+
#### Serialization of UI element sizes or positions
90+
Whenever UI size or position is restored at a different DPI context than what it was stored at, it will be positioned and sized incorrectly. This happens because the logical bounds of a window are converted to device units so that it can be passed to the Win32 method MonitorFromPoint that expects device coordinates in order to return back an accurate monitor pointer.
91+
92+
#### Incorrect scaling
93+
UI elements created on the primary DPI will scale correctly, however when moved to a display with a different DPI, they don't rescale and thus, their content ends up being too large or too small.
94+
95+
#### Incorrect bounding
96+
Similarly, to the scaling problem, UI elements will calculate their bounds correctly on their primary DPI context, however when moved to a non-primary DPI, they won't calculate the new bounds correctly. As such, the content window ends up being too small or too large compared to the hosting UI, which results in empty space or clipping.
97+
98+
#### Drag & drop
99+
Whenever inside mixed-mode DPI scenarios (e.g. different UI elements rendering in both primary and non-primary DPI context), drag and drop coordinates could be miscalculated, resulting in the final drop position end up incorrect.
100+
101+
#### Out-of-process UI
102+
Some UI is created out-of-process and if the creating external process is in a different DPI awareness mode than Visual Studio, this can introduce any of the previous rendering issues.
103+
104+
#### Windows Forms controls, images, or windows not displaying
105+
One of the main causes for this issue is developers trying to reparent a control or window with one DpiAwarenessContext to a different DpiAwarenessContext window.
106+
107+
The following pictures show the current Windows operating system restrictions in parenting windows, unless thread hosting behavior is explicitly changed:
108+
109+
![A screenshot of the correct parenting behavior](../../extensibility/ux-guidelines/media/PMA-parenting-behavior.PNG)
110+
111+
As a result, if you set parent-child relationship between unsupported modes, it will fail, and the control or window may not be rendered as expected.
112+
113+
### Diagnosing issues
114+
There are many factors to consider when identifying PMA-related issues:
115+
116+
1. Does the UI or API expected logical or device values.
117+
- WPF UI and APIs typically use logical values (but not always)
118+
- Win32 UI and APIs typically use device values
119+
120+
2. Where are the values coming from?
121+
- If receiving values from other UI or API, is it passing device or logical values.
122+
- If receiving values from multiple sources, do they all use/expect the same types of values or do conversions need to be mixed and matched?
123+
124+
3. Are UI constants in use and what form are they in?
125+
126+
4. Is the thread in the correct DPI context for the values it's receiving?
127+
- The changes to enable CLMM should generally put code paths in the right context, however, work done outside the main message loop or event flow might execute in the wrong DPI context.
128+
129+
5. Do values cross DPI context boundaries?
130+
- Drag & drop is a common situation where coordinates can cross DPI contexts. Window tries to do the right thing, but in some cases, the host UI may need to do conversion work to ensure matching context boundaries.
131+
132+
### PMA Nugget package
133+
The new DpiAwarness libraries can be found on the Microsoft.VisualStudio.DpiAwareness NuGet package.
134+
135+
### Recommended tools
136+
The following tools can help debug PMA-related issues across some of the different UI stacks supported by Visual Studio.
137+
138+
#### Snoop
139+
Snoop is a XAML debugging tool that has some extra functionality that the built-in Visual Studio XAML tools doesn’t have. Additionally, Snoop doesn’t need to actively debug Visual Studio to be able to view and tweak its WPF UI. The two main ways Snoop can be useful for diagnosing PMA issues is for validating logical placement coordinates or size bounds, and for validating the UI has the right DPI.
140+
141+
#### Visual Studio XAML tools
142+
Like Snoop, the XAML tools in Visual Studio can help diagnose PMA issues. Once a likely culprit is found, you can set breakpoints, and use the Live Visual Tree window as well as the debug windows, to inspect UI bounds and current DPI.
143+
144+
## Strategies for fixing PMA issues
145+
146+
### Replacing DpiHelper calls
147+
In most cases, fixing UI issues in PMA boils down to replacing calls in managed code to the old:
148+
*Microsoft.VisualStudio.Utilities.Dpi.DpiHelper* and *Microsoft.VisualStudio.PlatformUI.DpiHelper* classes, with calls to the new *Microsoft.VisualStudio.Utilities.DpiAwareness* helper class.
149+
150+
For native code, it will entail replacing calls to the old *VsUI::CDpiHelper* class with calls to the new *VsUI::CDpiAwareness* class. The new DpiAwareness and CDpiAwareness classes offer the same conversion helpers as the DpiHelper classes but require an additional input parameter: the UI element to use as a reference for the conversion operation.
151+
152+
The managed DpiAwareness class offers helpers for WPF Visuals, Windows Forms Controls, and Win32 HWNDs and HMONITORs (both in the form of IntPtrs), while the native CDpiAwareness class offers HWND and HMONITOR helpers.
153+
154+
### Windows Forms dialogs, windows, or controls displayed in the wrong DpiAwarenessContext
155+
Even after a successful parenting of windows with different DpiAwarenessContext (because of windows default behavior), users may still see scaling issues as different DpiAwarenessContext windows scale differently. As a result, users may see alignment/blurry text or Image issues on the UI.
156+
157+
The solution is to set the correct DpiAwarenessContext scope for all the windows and controls in the application.
158+
159+
### TLMM dialogs
160+
When creating top-level windows such as modal dialogs, it’s important to make sure the thread is in the correct state prior to the HWND being created. The thread can be put into System awareness by using the CDpiScope helper in native or the DpiAwareness.EnterDpiScope helper in managed. (TLMM should generally be used on non-WPF dialogs/windows.)
161+
162+
### Child-level mixed mode (CLMM)
163+
164+
By default, child windows receive the same DPI-awareness mode as their parents. However, you can use SetThreadDpiHostingBehavior to override it and have child windows run in a different scaling mode than their parent or host.
165+
166+
167+
#### CLMM issues
168+
Most of the UI calculation work that happens as part of the main messaging loop or event chain should already be running in the right DPI context. However, if coordinate or sizing calculations are done outside these main workflows (such as during an idle time task, or off the UI thread, then the current DPI context might be incorrect leading to UI misplacement or mis-sizing issues. Putting the thread into the correct state for the UI work generally fixes the problem.
169+
170+
#### Opting out of CLMM
171+
If a non-WPF tool window is being migrated to fully support PMA, it will need to opt out of CLMM. To do so, a new interface needs to be implemented: IVsDpiAware.
172+
173+
C#:
174+
```cs
175+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
176+
public interface IVsDpiAeware
177+
{
178+
[ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSDPIMode")]
179+
uint Mode {get;}
180+
}
181+
```
182+
183+
C++:
184+
```cplusplus
185+
IVsDpiAware : public IUnknown
186+
{
187+
public:
188+
HRRESULT STDMETHODCALLTYPE get_Mode(__RCP__out VSDPIMODE *dwMode);
189+
};
190+
```
191+
192+
193+
For managed languages, the best place to implement this interface is in the same class that derives from *Microsoft.VisualStudio.Shell.ToolWindowPane*. For C++, the best place to implement this interface is in the same class that implements *IVsWindowPane* from vsshell.h.
194+
195+
The value returned by the Mode property on the interface is a __VSDPIMODE (and cast to a uint in managed):
196+
197+
```cs
198+
enum __VSDPIMODE
199+
{
200+
VSDM_Unaware = 0x01,
201+
VSDM_System = 0x02,
202+
VSDM_PerMonitor = 0x03,
203+
}
204+
```
205+
206+
- Unaware means the tool window needs to handle 96 DPI, Windows will handle scaling it for all other DPIs. Resulting on content being slightly blurry.
207+
- System means the tool window needs to handle the DPI for the primary display DPI. Any display with a matching DPI will look crisp, but if the DPI is different or changes during the session, Windows will handle the scaling and it will be slightly blurry.
208+
- PerMonitor means the tool window needs to handle all DPIs on all displays and whenever the DPI changes.
209+
210+
**NOTE**: Visual Studio only supports PerMonitorV2 awareness, so the PerMonitor enum value translates to the Windows value of DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2.
211+
212+
## Known issues
213+
214+
### Windows Forms
215+
216+
To optimize for the new mixed-mode scenarios, Windows Forms changed how it creates controls and windows whenever their parent was not explicitly set. Earlier, controls without an explicit parent used an internal "Parking Window" as a temporary parent to the control or window being created.
217+
218+
The "Parking Window" gets its DpiAwarenessContext from the process the application is running under. The control inherits the same DpiAwarenessContext as the Parking Window and would then be reparented to the original/expected parent by the application developer. This doesn't work when the intended parent to the control is not the same DpiAwarenessContext as the control being created.
219+
220+
As of .NET 4.8, if the parent is not explicitly set on the control or window, Windows Forms will query for a Parking Window that matches the DpiAwarenessContext of the thread in which the control or window creation is requested and use that as a temporary parent. In other words, upon creation the control is now created with the intended DpiAwarenessContext. The control or window will then be reparented to the expected parent by the application developer.

docs/ide/reference/convert-foreach-linq.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,37 @@ This refactoring applies to:
4040
![LINQ query result](media/convert-foreach-to-LINQ-result.png)
4141

4242
![LINQ call form result](media/convert-foreach-to-LINQ-callform-result.png)
43+
44+
### Sample Code
45+
46+
```csharp
47+
using System.Collections.Generic;
48+
49+
public class Class1
50+
{
51+
public void MyMethod()
52+
{
53+
var greetings = new List<string>()
54+
{ "hi", "yo", "hello", "howdy" };
55+
56+
IEnumerable<string> enumerable()
57+
{
58+
foreach (var greet in greetings)
59+
{
60+
if (greet.Length < 3)
61+
{
62+
yield return greet;
63+
}
64+
}
65+
66+
yield break;
67+
}
68+
}
69+
}
70+
```
4371

4472
## See also
4573

4674
- [Refactoring](../refactoring-in-visual-studio.md)
4775
- [Preview Changes](../../ide/preview-changes.md)
48-
- [Tips for .NET Developers](../../ide/visual-studio-2017-for-dotnet-developers.md)
76+
- [Tips for .NET Developers](../../ide/visual-studio-2017-for-dotnet-developers.md)

0 commit comments

Comments
 (0)