Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Все больше устройств поставляется с экранами высокого разрешения. Эти экраны обычно имеют более 200 пикселей на дюйм (ppi). Для работы с приложением на этих компьютерах потребуется масштабировать содержимое в соответствии с потребностями просмотра содержимого на обычном расстоянии просмотра для устройства. Начиная с 2014 года основными устройствами, для которых предназначены дисплеи с высокой плотностью, являются мобильные вычислительные устройства (планшеты, ноутбуки и телефоны).
Windows 8.1 и более поздние версии содержат несколько функций, позволяющих этим компьютерам работать с дисплеями и средой, где компьютер подключен как к дисплеям высокой плотности, так и к дисплеям стандартной плотности.
Windows позволяет масштабировать содержимое на устройстве с помощью параметра "Сделать текст и другие элементы больше или меньше" (доступно с Windows XP).
Windows 8.1 и более новые версии будут автоматически масштабировать содержимое для большинства приложений, чтобы обеспечить согласованность при перемещении между дисплеями с различной плотностью пикселей. Если основной дисплей имеет высокую плотность (200% масштабирование), а дополнительный дисплей — стандартную плотность (100%), Windows автоматически уменьшает масштабирующее отображение содержимого окна приложения на дополнительном дисплее, показывая 1 пиксель для каждых 4 пикселей, рендеренных приложением.
Windows автоматически устанавливает оптимальное масштабирование для плотности пикселей и расстояния просмотра дисплея (начиная с Windows 7 и выше, при настройке OEM).
Windows может автоматически масштабировать содержимое до 250% на новых устройствах, превышающих 280 пикселей (по состоянию на Windows 8.1 S14).
Windows имеет способ масштабирования пользовательского интерфейса, чтобы воспользоваться преимуществами увеличения количества пикселей. Приложение выбирает эту систему, объявляя себя "с поддержкой системного DPI". Приложения, которые этого не делают, автоматически масштабируются системой. Это может привести к "размытой" пользовательской среде, в которой все приложение одинаково растянуто по пикселям. Рассмотрим пример.
Visual Studio оптимизируется для распознавания масштабирования DPI, и поэтому не является "виртуализированным".
Windows (и Visual Studio) используют несколько технологий пользовательского интерфейса, которые имеют различные способы работы с коэффициентами масштабирования, заданными системой. Рассмотрим пример.
WPF измеряет элементы управления устройствами независимо от устройства (единицы, а не пиксели). Пользовательский интерфейс WPF автоматически масштабируется для текущего DPI.
Все размеры текста независимо от платформы пользовательского интерфейса выражаются в точках и поэтому обрабатываются системой как независимо от DPI. Текст в Win32, WinForms и WPF уже правильно масштабируется при отображении на экране.
Диалоговые окна и окна Win32/WinForms позволяют включить макет, который изменяет размер вместе с текстом (например, с помощью панелей Grid, Flow и TableLayout). Это позволяет избежать жестко закодированных расположений пикселей, которые не масштабируются при увеличении размера шрифта.
Значки, предоставляемые системой или ресурсами на основе системных метрик (например, SM_CXICON и SM_CXSMICON), уже масштабируются.
Более старая версия пользовательского интерфейса на основе Win32 (GDI, GDI+) и WinForms
Хотя WPF уже поддерживает высокий уровень DPI, большая часть кода на основе Win32/GDI изначально не была написана с учетом осведомленности о DPI. Windows предоставила API-интерфейсы масштабирования DPI. Исправления проблем Win32 должны последовательно применяться в рамках всего продукта. Visual Studio предоставила вспомогательную библиотеку классов, чтобы избежать дублирования функциональности и обеспечить согласованность в продукте.
Изображения с высоким разрешением
Этот раздел предназначен в основном для разработчиков, расширяющих Visual Studio 2013. Для Visual Studio 2015 используйте службу образов, встроенную в Visual Studio. Вы также можете обнаружить, что вам нужно поддерживать или использовать многие версии Visual Studio, поэтому использование службы образов в 2015 году не является вариантом, так как он не существует в предыдущих версиях. Этот раздел также предназначен для вас.
Увеличение масштаба слишком маленьких изображений
Изображения, которые слишком малы, можно масштабировать и отображать в GDI и WPF с помощью некоторых распространенных методов. Вспомогательные классы управляемого DPI доступны для внутренних и внешних интеграторов Visual Studio для масштабирования иконок, bmp изображений, графических полос и списков изображений. Вспомогательные библиотеки Win32 на C/C++ доступны для масштабирования HICON, HBITMAP, HIMAGELIST и VsUI::GdiplusImage. Масштабирование растрового изображения обычно требует только однострочного изменения после включения ссылки на вспомогательной библиотеке. Рассмотрим пример.
Масштабирование списка изображений зависит от того, завершен ли список изображений во время загрузки или добавляется во время выполнения. Если к моменту загрузки завершено, вызовите LogicalToDeviceUnits(), используя список изображений так же, как и растровое изображение. Если коду необходимо загрузить отдельный растровый рисунок перед созданием списка изображений, обязательно масштабируйте размер изображения списка изображений:
imagelist.ImageSize = DpiHelper.LogicalToDeviceUnits(imagelist.ImageSize);
В родном коде размеры можно масштабировать при создании списка изображений следующим образом:
ImageList_Create(VsUI::DpiHelper::LogicalToDeviceUnitsX(16),VsUI::DpiHelper::LogicalToDeviceUnitsY(16), ILC_COLOR32|ILC_MASK, nCount, 1);
Функции в библиотеке позволяют указывать алгоритм изменения размера. При масштабировании изображений в списках изображений обязательно укажите цвет фона, используемый для прозрачности, или используйте масштабирование NearestNeighbor (что приведет к искажениям в 125% и 150%).
Изучите DpiHelper документацию на MSDN.
В следующей таблице показаны примеры масштабирования изображений с помощью соответствующих факторов масштабирования DPI. Изображения, обведённые оранжевым цветом, указывают на наши лучшие практики с масштабированием Visual Studio 2013 (100%–200% масштабирование DPI):
Проблемы с макетом
Частых проблем с макетом можно избежать, главным образом, путем масштабирования точек в пользовательском интерфейсе и поддержания их относительного положения друг к другу, а не с помощью абсолютных расположений, особенно в единицах измерения пикселей. Рассмотрим пример.
Позиции макета и текста должны быть скорректированы, чтобы учитывать увеличенные изображения.
Столбцы в сетках должны иметь ширину, скорректированную для масштабируемого текста.
Хардкодированные размеры и пробелы между элементами также должны быть увеличены. Размеры, зависящие только от размеров текста, обычно в порядке, потому что шрифты автоматически масштабируются.
Вспомогательные функции доступны в DpiHelper классе, чтобы разрешить масштабирование на оси X и Y:
LogicalToDeviceUnitsX/LogicalToDeviceUnitsY (функции позволяют масштабирование на оси X/Y)
int space = DpiHelper.LogicalToDeviceUnitsX (10);
int height = VsUI::DpiHelper::LogicalToDeviceUnitsY(5);
Существуют перегрузки LogicalToDeviceUnits, позволяющие масштабировать такие объекты, как Rect, Point и Size.
Использование библиотеки или класса DPIHelper для масштабирования изображений и макета
Вспомогательные библиотеки DPI Visual Studio доступны в собственных и управляемых формах и могут использоваться вне оболочки Visual Studio другими приложениями.
Чтобы использовать библиотеку, перейдите к примерам расширяемости VSSDK Visual Studio и клонируйте пример High-DPI_Images_Icons.
В исходных файлах включите VsUIDpiHelper.h и вызовите статические функции VsUI::DpiHelper класса:
#include "VsUIDpiHelper.h"
int cxScaled = VsUI::DpiHelper::LogicalToDeviceUnitsX(cx);
VsUI::DpiHelper::LogicalToDeviceUnits(&hBitmap);
Замечание
Не используйте вспомогательные функции в статических переменных уровня модуля или класса. Библиотека также использует статические переменные для синхронизации потоков, и могут возникнуть проблемы с порядком инициализации. Преобразуйте эти статические значения в нестатические переменные-члены или заключите их в функцию (поэтому они создаются при первом доступе).
Чтобы получить доступ к вспомогательным функциям DPI из управляемого кода, который будет выполняться в среде Visual Studio:
Используемый проект должен ссылаться на последнюю версию Shell MPF. Рассмотрим пример.
<Reference Include="Microsoft.VisualStudio.Shell.14.0.dll" />Убедитесь, что проект содержит ссылки на System.Windows.Forms, PresentationCore и PresentationUI.
В коде используйте пространство имен Microsoft.VisualStudio.PlatformUI и вызовите статические функции класса DpiHelper. Для поддерживаемых типов (точек, размеров, прямоугольников и т. д.) предоставляются функции расширения, возвращающие новые масштабируемые объекты. Рассмотрим пример.
using Microsoft.VisualStudio.PlatformUI; double x = DpiHelper.LogicalToDeviceUnitsX(posX); Point ptScaled = ptOriginal.LogicalToDeviceUnits(); DpiHelper.LogicalToDeviceUnits(ref bitmap);
Работа с нечеткостью изображения WPF в масштабируемом пользовательском интерфейсе
В WPF растровые изображения автоматически изменяются для соответствия текущему уровню масштабирования DPI с использованием высококачественного бикубического алгоритма (по умолчанию), который хорошо подходит для изображений или крупных снимков экрана, но не подходит для значков пунктов меню, поскольку вызывает видимую размытие.
Рекомендации.
Для изображения логотипа и баннеров можно использовать режим изменения размера по умолчанию BitmapScalingMode .
Для элементов меню и изображений значков следует использовать BitmapScalingMode, если это не вызывает других артефактов искажения, чтобы устранить нечеткость (при увеличении на 200% и 300%).
Для больших уровней масштабирования, не являющихся кратными 100% (например, 250% или 350%), масштабирование изображений значков с использованием бикубической интерполяции приводит к нечеткому, размытому пользовательскому интерфейсу. Лучший результат получается путем сначала масштабирования изображения с помощью метода ближайшего соседа до наибольшего кратного 100% (например, 200% или 300%), а затем с использованием бикубической интерполяции. Дополнительные сведения см. в разделе "Особый случай: предмасштабирование образов WPF для больших уровней DPI".
Класс DpiHelper в пространстве имен Microsoft.VisualStudio.PlatformUI предоставляет элемент BitmapScalingMode , который можно использовать для привязки. Она позволит оболочке Visual Studio управлять режимом масштабирования растрового изображения в продукте равномерно в зависимости от коэффициента масштабирования DPI.
Чтобы использовать его в XAML, добавьте:
xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.14.0"
<Setter Property="RenderOptions.BitmapScalingMode" Value="{x:Static vs:DpiHelper.BitmapScalingMode}" />
Оболочка Visual Studio уже задает это свойство в окнах верхнего уровня и диалоговых окнах. Пользовательский интерфейс на основе WPF, запущенный в Visual Studio, уже наследует его. Если параметр не распространяется на определенные части пользовательского интерфейса, его можно задать в корневом элементе пользовательского интерфейса XAML/WPF. Места, где это происходит, включают всплывающие элементы, элементы с главными элементами Win32 и окна конструктора, которые выполняются вне процесса, например, Blend.
Некоторый пользовательский интерфейс может масштабироваться независимо от уровня масштабирования набора DPI, таких как текстовый редактор Visual Studio и конструкторы на основе WPF (рабочий стол WPF и Магазин Windows). В этих случаях не следует использовать DpiHelper.BitmapScalingMode. Чтобы устранить эту проблему в редакторе, команда интегрированной среды разработки создала настраиваемое свойство RenderOptions.BitmapScalingMode. Задайте для этого свойства значение HighQuality или NearestNeighbor в зависимости от объединенного уровня масштабирования системы и пользовательского интерфейса.
Особый случай: предмасштабирование образов WPF для больших уровней DPI
Для очень больших уровней масштабирования, не кратных 100% (например, 250%, 350% и т. д.), масштабирование изображений значков методом бикубической интерполяции приводит к размытию и потере четкости в пользовательском интерфейсе. Впечатление от этих изображений в сочетании с четким текстом почти как оптическая иллюзия. Изображения кажутся ближе к глазу и не в фокусе по сравнению с текстом. Результат масштабирования в этом увеличенном размере можно улучшить, сначала масштабировав изображение с использованием метода ближайшего соседа до наибольшего кратного 100% (например, 200% или 300%), а затем с использованием бикубической интерполяции для оставшихся 50%.
Ниже приведен пример различий в результатах, где первый образ масштабируется с улучшенным алгоритмом двойного масштабирования 100%->200%->250%, а второй — только с бикубическим 100%->250%.
Чтобы пользовательский интерфейс мог использовать это двойное масштабирование, разметка XAML для отображения каждого элемента Image должна быть изменена. В следующих примерах показано, как использовать двойное масштабирование в WPF в Visual Studio с помощью библиотеки DpiHelper и Shell.12/14.
Шаг 1. Масштабируйте изображение до 200%, 300% и так далее, используя метод ближайшего соседа.
Изображение можно предварительно масштабировать с помощью преобразователя, применённого к привязке, или с помощью расширения разметки XAML. Рассмотрим пример.
<vsui:DpiPrescaleImageSourceConverter x:Key="DpiPrescaleImageSourceConverter" />
<Image Source="{Binding Path=SelectedImage, Converter={StaticResource DpiPrescaleImageSourceConverter}}" Width="16" Height="16" />
<Image Source="{vsui:DpiPrescaledImage Images/Help.png}" Width="16" Height="16" />
Если изображение также должно быть стилизовано (большинство, если не все, изображений должны), разметка может использовать другой преобразователь, который сначала выполняет стилизацию изображения, а затем предварительное масштабирование. Разметка может использовать либо DpiPrescaleThemedImageConverter, либо DpiPrescaleThemedImageSourceConverter, в зависимости от требуемых выходных данных преобразования.
<vsui:DpiPrescaleThemedImageSourceConverter x:Key="DpiPrescaleThemedImageSourceConverter" />
<Image Width="16" Height="16">
<Image.Source>
<MultiBinding Converter="{StaticResource DpiPrescaleThemedImageSourceConverter}">
<Binding Path="Icon" />
<Binding Path="(vsui:ImageThemingUtilities.ImageBackgroundColor)"
RelativeSource="{RelativeSource Self}" />
<Binding Source="{x:Static vsui:Boxes.BooleanTrue}" />
</MultiBinding>
</Image.Source>
</Image>
Шаг 2. Убедитесь, что окончательный размер соответствует текущему DPI.
Так как WPF масштабирует пользовательский интерфейс для текущего DPI с помощью свойства BitmapScalingMode, заданного в UIElement, элемент управления Image с предварительно масштабируемым изображением будет выглядеть в два или три раза больше, чем должно. Ниже приведены несколько способов противодействия этому эффекту:
Если вы знаете измерение исходного изображения на 100%, можно указать точный размер элемента управления Image. Эти размеры отражают размер пользовательского интерфейса перед применением масштабирования.
<Image Source="{Binding Path=SelectedImage, Converter={StaticResource DpiPrescaleImageSourceConverter}}" Width="16" Height="16" />Если размер исходного изображения не известен, LayoutTransform можно использовать для масштабирования конечного объекта Image. Рассмотрим пример.
<Image Source="{Binding Path=SelectedImage, Converter={StaticResource DpiPrescaleImageSourceConverter}}" > <Image.LayoutTransform> <ScaleTransform ScaleX="{x:Static vsui:DpiHelper.PreScaledImageLayoutTransformScale}" ScaleY="{x:Static vsui:DpiHelper.PreScaledImageLayoutTransformScale}" /> </Image.LayoutTransform> </Image>
Включение поддержки HDPI для WebOC
По умолчанию элементы управления WebOC (например, элемент управления WebBrowser в WPF или интерфейс IWebBrowser2) не поддерживают обнаружение и поддержку HDPI. Результатом будет встроенный элемент управления, содержание которого окажется слишком мелким на дисплее с высоким разрешением. В следующем описано, как включить поддержку высокого уровня DPI в определенном веб-экземпляре WebOC.
Реализуйте интерфейс IDocHostUIHandler (см. статью MSDN по IDocHostUIHandler:
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A")]
public interface IDocHostUIHandler
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ShowContextMenu(
[In, MarshalAs(UnmanagedType.U4)] int dwID,
[In] POINT pt,
[In, MarshalAs(UnmanagedType.Interface)] object pcmdtReserved,
[In, MarshalAs(UnmanagedType.IDispatch)] object pdispReserved);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetHostInfo([In, Out] DOCHOSTUIINFO info);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ShowUI(
[In, MarshalAs(UnmanagedType.I4)] int dwID,
[In, MarshalAs(UnmanagedType.Interface)] object activeObject,
[In, MarshalAs(UnmanagedType.Interface)] object commandTarget,
[In, MarshalAs(UnmanagedType.Interface)] object frame,
[In, MarshalAs(UnmanagedType.Interface)] object doc);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int HideUI();
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int UpdateUI();
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int EnableModeless([In, MarshalAs(UnmanagedType.Bool)] bool fEnable);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int OnDocWindowActivate([In, MarshalAs(UnmanagedType.Bool)] bool fActivate);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int OnFrameWindowActivate([In, MarshalAs(UnmanagedType.Bool)] bool fActivate);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ResizeBorder(
[In] COMRECT rect,
[In, MarshalAs(UnmanagedType.Interface)] object doc,
bool fFrameWindow);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int TranslateAccelerator(
[In] ref MSG msg,
[In] ref Guid group,
[In, MarshalAs(UnmanagedType.I4)] int nCmdID);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetOptionKeyPath(
[Out, MarshalAs(UnmanagedType.LPArray)] string[] pbstrKey,
[In, MarshalAs(UnmanagedType.U4)] int dw);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetDropTarget(
[In, MarshalAs(UnmanagedType.Interface)] IOleDropTarget pDropTarget,
[MarshalAs(UnmanagedType.Interface)] out IOleDropTarget ppDropTarget);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetExternal([MarshalAs(UnmanagedType.IDispatch)] out object ppDispatch);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int TranslateUrl(
[In, MarshalAs(UnmanagedType.U4)] int dwTranslate,
[In, MarshalAs(UnmanagedType.LPWStr)] string strURLIn,
[MarshalAs(UnmanagedType.LPWStr)] out string pstrURLOut);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int FilterDataObject(
IDataObject pDO,
out IDataObject ppDORet);
}
При необходимости реализуйте интерфейс ICustomDoc (см. статью MSDN по ICustomDoc:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B")]
public interface ICustomDoc
{
void SetUIHandler(IDocHostUIHandler pUIHandler);
}
Свяжите класс, реализующий IDocHostUIHandler с документом WebOC. Если вы реализовали приведенный выше интерфейс ICustomDoc, то как только свойство документа WebOC допустимо, присвойте его ICustomDoc и вызовите метод SetUIHandler, передав класс, реализующий IDocHostUIHandler.
// "this" references that class that owns the WebOC control and in this case also implements the IDocHostUIHandler interface
ICustomDoc customDoc = (ICustomDoc)webBrowser.Document;
customDoc.SetUIHandler(this);
Если вы не реализовали интерфейс ICustomDoc, то как только свойство документа WebOC становится доступным, необходимо преобразовать его в IOleObject и вызвать метод SetClientSite, передав в него класс, реализующий IDocHostUIHandler. Задайте флаг DOCHOSTUIFLAG_DPI_AWARE в DOCHOSTUIINFO, переданном вызову GetHostInfo метода:
public int GetHostInfo(DOCHOSTUIINFO info)
{
// This is what the default site provides.
info.dwFlags = (DOCHOSTUIFLAG)0x5a74012;
// Add the DPI flag to the defaults
info.dwFlags |=.DOCHOSTUIFLAG.DOCHOSTUIFLAG_DPI_AWARE;
return S_OK;
}
Это должно быть все, что необходимо для получения элемента управления WebOC, чтобы поддерживать HPDI.
Советы
Если свойство документа в элементе управления WebOC изменяется, может потребоваться повторно связать документ с классом IDocHostUIHandler.
Если приведенный выше параметр не работает, имеется известная проблема с WebOC, не обнаруживающим изменения флага DPI. Самый надежный способ исправления заключается в переключение оптического масштабирования WebOC, что означает два вызова с двумя разными значениями для процента масштабирования. Кроме того, если это решение необходимо, может потребоваться выполнить его при каждом вызове функции навигации.
// browser2 is a SHDocVw.IWebBrowser2 in this case // EX: Call the Exec twice with DPI%-1 and then DPI% as the zoomPercent values IOleCommandTarget cmdTarget = browser2.Document as IOleCommandTarget; if (cmdTarget != null) { object commandInput = zoomPercent; cmdTarget.Exec(IntPtr.Zero, OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, ref commandInput, ref commandOutput); }