Řešení problémů s DPI
Rostoucí počet zařízení se dodává s obrazovkami s vysokým rozlišením. Tyto obrazovky obvykle mají více než 200 pixelů na paleč (ppi). Práce s aplikací na těchto počítačích bude vyžadovat vertikální navýšení kapacity obsahu tak, aby vyhovovalo potřebám zobrazení obsahu v normální vzdálenosti pro prohlížení zařízení. Od roku 2014 je primárním cílem displejů s vysokou hustotou mobilní výpočetní zařízení (tablety, notebooky se clamshellem a telefony).
Windows 8.1 a vyšší obsahuje několik funkcí, které umožňují těmto počítačům pracovat s displeji a prostředími, ve kterých je počítač připojený k displejům s vysokou hustotou i zobrazením standardní hustoty současně.
Systém Windows umožňuje škálovat obsah na zařízení pomocí nastavení Zvětšit nebo zmenšit text a další položky (k dispozici od systému Windows XP).
Systém Windows 8.1 a vyšší automaticky škáluje obsah pro většinu aplikací, aby při přesouvání mezi zobrazeními různých hustot pixelů byl konzistentní. Pokud je primární displej s vysokou hustotou (měřítko 200 % ) a sekundární displej je standardní hustota (100 %), Systém Windows automaticky škáluje obsah okna aplikace na sekundárním displeji (1 pixel zobrazený pro každý 4 pixely vykreslené aplikací).
Windows ve výchozím nastavení nastaví správné měřítko hustoty pixelů a vzdálenost zobrazení pro displej (Windows 7 a vyšší, OEM-configurable).
Windows může automaticky škálovat obsah až 250 % na nových zařízeních, která překračují 280 ppi (od Windows 8.1 S14).
Systém Windows nabízí způsob, jak vertikálně navýšit kapacitu uživatelského rozhraní a využít tak vyšší počet pixelů. Aplikace se k tomuto systému přihlásí tím, že deklaruje "systém DPI". Aplikace, které to neudělají, se systémem vertikálně navyšují kapacitu. Výsledkem může být "přibližné" uživatelské prostředí, ve kterém je celá aplikace rovnoměrně roztažená na pixelech. Příklad:
Visual Studio se rozhodne, že je podporující škálování DPI, a proto není "virtualizován".
Windows (a Visual Studio) využívají několik technologií uživatelského rozhraní, které mají různé způsoby práce s faktory škálování nastavenými systémem. Příklad:
WPF měří ovládací prvky způsobem nezávislým na zařízení (jednotky, ne pixely). Uživatelské rozhraní WPF automaticky vertikálně navyšuje kapacitu aktuálního DPI.
Všechny velikosti textu bez ohledu na architekturu uživatelského rozhraní jsou vyjádřeny v bodech, a proto je systém považuje za nezávislý na DPI. Text v systému Win32, WinForms a WPF se již při nakresleném zařízení pro zobrazení správně vertikálně navyšují.
Dialogy Win32/WinForms a okna mají význam pro povolení rozložení, které mění velikost textu (například pomocí mřížky, toku a panelů rozložení tabulky). Ty umožňují vyhnout se pevně zakódovaným pixelovým umístěním, která nejsou při zvýšení velikosti písma škálována.
Ikony poskytované systémem nebo prostředky na základě systémových metrik (například SM_CXICON a SM_CXSMICON) se už vertikálně navyšují.
Starší uživatelské rozhraní Win32 (GDI, GDI+) a WinForms
I když wpF je již s vysokým rozlišením DPI, velká část našeho kódu založeného na Win32/GDI nebyla původně napsána s ohledem na DPI. Systém Windows poskytuje rozhraní API pro škálování DPI. Opravy problémů s Win32 by měly tyto problémy používat konzistentně napříč produktem. Visual Studio poskytuje pomocnou knihovnu tříd, která zabraňuje duplikování funkcí a zajištění konzistence napříč produktem.
Obrázky s vysokým rozlišením
Tato část je primárně určená vývojářům, kteří rozšiřují Visual Studio 2013. Pro Visual Studio 2015 použijte službu image, která je integrovaná do sady Visual Studio. Můžete také zjistit, že potřebujete podporovat nebo cílit na mnoho verzí sady Visual Studio, a proto použití služby image v roce 2015 není možnost, protože v předchozích verzích neexistuje. Tato část je také určená pro vás.
Vertikální navýšení kapacity imagí, které jsou příliš malé
Obrázky, které jsou příliš malé, je možné vertikálně navýšit a vykreslit v GDI a WPF pomocí některých běžných metod. Spravované třídy pomocných rutin DPI jsou k dispozici interním a externím integrátorům sady Visual Studio, které řeší ikony škálování, rastrové obrázky, obrázky a seznamy obrázků. Nativní pomocné rutiny C/C++založené na Win32 jsou k dispozici pro škálování HICON, HBITMAP, HIMAGELIST a VsUI::GdiplusImage. Změna měřítka rastrového obrázku obvykle vyžaduje pouze jednořádkovou změnu po zahrnutí odkazu na pomocnou knihovnu. Příklad:
Škálování seznamu imagí závisí na tom, jestli je seznam imagí dokončen v době načtení, nebo je připojený za běhu. Pokud se při načítání dokončí, zavolejte LogicalToDeviceUnits()
seznam obrázků stejně jako rastrový obrázek. Pokud kód před vytvořením seznamu obrázků potřebuje načíst jednotlivé rastrové obrázky, nezapomeňte velikost obrázku seznamu obrázků škálovat:
imagelist.ImageSize = DpiHelper.LogicalToDeviceUnits(imagelist.ImageSize);
V nativním kódu lze dimenze škálovat při vytváření seznamu imagí následujícím způsobem:
ImageList_Create(VsUI::DpiHelper::LogicalToDeviceUnitsX(16),VsUI::DpiHelper::LogicalToDeviceUnitsY(16), ILC_COLOR32|ILC_MASK, nCount, 1);
Funkce v knihovně umožňují zadat algoritmus změny velikosti. Při škálování obrázků, které se mají umístit do seznamů obrázků, nezapomeňte určit barvu pozadí, která se používá pro průhlednost, nebo použít měřítko NearestNeighbor (což způsobí zkreslení při 125 % a 150 %).
DpiHelper Projděte si dokumentaci na webu MSDN.
Následující tabulka ukazuje příklady, jak se mají obrázky škálovat na odpovídající faktory škálování DPI. Obrázky, které jsou popsané oranžově, označují náš osvědčený postup od sady Visual Studio 2013 (škálování DPI 100%-200%):
Problémy s rozložením
Běžné problémy s rozložením se dají vyhnout hlavně tím, že body v uživatelském rozhraní jsou škálované a relativní vzhledem k sobě, nikoli použitím absolutních umístění (konkrétně v pixelových jednotkách). Příklad:
Pozice rozložení a textu je potřeba upravit tak, aby se zohlednily obrázky s vertikálním navýšením kapacity.
Sloupce v mřížce musí mít upravené šířky pro text s vertikálním navýšením kapacity.
Pevně zakódované velikosti nebo mezery mezi prvky bude potřeba vertikálně navýšit kapacitu. Velikosti založené pouze na rozměrech textu jsou obvykle v pořádku, protože písma se automaticky škálují.
Pomocné funkce jsou k dispozici ve DpiHelper třídě, které umožňují škálování na ose X a Y:
LogicalToDeviceUnitsX/LogicalToDeviceUnitsY (funkce umožňují škálování na ose X/Y)
int space = DpiHelper.LogicalToDeviceUnitsX (10);
int height = VsUI::D piHelper::LogicalToDeviceUnitsY(5);
Existují přetížení LogicalToDeviceUnits, které umožňují škálování objektů, jako jsou Rect, Point a Size.
Použití knihovny nebo třídy DPIHelper ke škálování obrázků a rozložení
Pomocná knihovna DPI sady Visual Studio je k dispozici v nativních a spravovaných formulářích a dá se použít mimo prostředí sady Visual Studio jinými aplikacemi.
Pokud chcete knihovnu použít, přejděte do ukázek rozšiřitelnosti sady Visual Studio VSDK a naklonujte ukázku High-DPI_Images_Icons.
Ve zdrojových VsUI::DpiHelper
souborech zahrňte VsUIDpiHelper.h a volejte statické funkce třídy:
#include "VsUIDpiHelper.h"
int cxScaled = VsUI::DpiHelper::LogicalToDeviceUnitsX(cx);
VsUI::DpiHelper::LogicalToDeviceUnits(&hBitmap);
Poznámka:
Nepoužívejte pomocné funkce ve statických proměnných na úrovni modulu nebo na úrovni třídy. Knihovna také používá statické funkce pro synchronizaci vláken a můžete narazit na problémy s inicializací objednávek. Buď tyto statické hodnoty převeďte na nestatické členské proměnné, nebo je zabalte do funkce (aby byly sestaveny při prvním přístupu).
Přístup k pomocným funkcím DPI ze spravovaného kódu, který se spustí v prostředí sady Visual Studio:
Projekt využívající musí odkazovat na nejnovější verzi prostředí MPF. Příklad:
<Reference Include="Microsoft.VisualStudio.Shell.14.0.dll" />
Ujistěte se, že projekt obsahuje odkazy na System.Windows.Forms, PresentationCore a PresentationUI.
V kódu použijte obor názvů Microsoft.VisualStudio.PlatformUI a volejte statické funkce třídy DpiHelper. Pro podporované typy (body, velikosti, obdélníky atd.) jsou k dispozici rozšiřující funkce, které vracejí nové škálované objekty. Příklad:
using Microsoft.VisualStudio.PlatformUI; double x = DpiHelper.LogicalToDeviceUnitsX(posX); Point ptScaled = ptOriginal.LogicalToDeviceUnits(); DpiHelper.LogicalToDeviceUnits(ref bitmap);
Práce s obrázkem WPF fuzziness v zoomable UI
Ve WPF se rastrové obrázky automaticky mění podle WPF pro aktuální úroveň přiblížení DPI pomocí vysoce kvalitní bicubic algoritmus (výchozí), který funguje dobře pro obrázky nebo velké snímky obrazovky, ale je nevhodný pro ikony položek nabídky, protože představuje vnímanou fuzziness.
Doporučení:
U obrázků loga a nápisů lze použít výchozí BitmapScalingMode režim změny velikosti.
U položek nabídky a obrázků ikon by se měly použít, BitmapScalingMode pokud nezpůsobí jiné artefakty zkreslení, aby se eliminovala fuzziness (při 200 % a 300 %).
U velkých úrovní přiblížení nejsou násobky 100 % (například 250 % nebo 350 %), škálování ikonografických obrázků s bibicií má za následek přibližné vymytí uživatelského rozhraní. Lepší výsledek získáte tak, že nejprve škáluje obrázek s nejbližším násobkem 100 % (například 200 % nebo 300 %) a škálováním s bibicikem odtamtud. Další informace najdete v tématu Zvláštní případ: Předběžné škálování imagí WPF pro velké úrovně DPI.
Třída DpiHelper v oboru názvů Microsoft.VisualStudio.PlatformUI poskytuje člena BitmapScalingMode , který lze použít pro vazbu. V závislosti na faktoru škálování DPI umožní prostředí sady Visual Studio řídit rastrový režim škálování v rámci produktu jednotně.
Pokud ho chcete použít v XAML, přidejte:
xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.14.0"
<Setter Property="RenderOptions.BitmapScalingMode" Value="{x:Static vs:DpiHelper.BitmapScalingMode}" />
Prostředí sady Visual Studio již tuto vlastnost nastavuje v oknech a dialogech nejvyšší úrovně. Uživatelské rozhraní založené na WPF spuštěné v sadě Visual Studio už zdědí. Pokud se nastavení nerozšíší do konkrétních částí uživatelského rozhraní, můžete ho nastavit v kořenovém prvku uživatelského rozhraní XAML/WPF. Místa, kde k tomu dochází, patří automaticky otevíraná okna, prvky s nadřazenými nadřazenými prvky Win32 a okna návrháře, u kterých dochází k procesu, jako je Blend.
Některé uživatelské rozhraní se můžou škálovat nezávisle na úrovni přiblížení DPI sady System, jako je textový editor sady Visual Studio a návrháři WPF (WPF Desktop a Windows Store). V těchto případech by se nemělo používat DpiHelper.BitmapScalingMode. Pokud chcete tento problém vyřešit v editoru, vytvořil tým IDE vlastní vlastnost s názvem RenderOptions.BitmapScalingMode. Nastavte hodnotu této vlastnosti na HighQuality nebo NearestNeighbor v závislosti na kombinované úrovni přiblížení systému a uživatelského rozhraní.
Speciální případ: Předběžné škálování imagí WPF pro velké úrovně DPI
U velmi velkých úrovní přiblížení, které nejsou násobky 100 % (například 250 %, 350 % atd.), zvětšování ikonografických obrázků s bibicií má za následek přibližné vymytí uživatelského rozhraní. Dojem těchto obrázků spolu s ostrým textem je téměř podobný optické iluzi. Zdá se, že obrázky jsou blíže k očím a mimo fokus ve vztahu k textu. Výsledek měřítka v této zvětšené velikosti je možné vylepšit tak, že nejprve škáluje obrázek s nejbližším násobkem 100 % (například 200 % nebo 300 %) a škálováním s bibicí na zbytek (dalších 50 %).
Následuje příklad rozdílů ve výsledcích, kdy se první obrázek škáluje pomocí vylepšeného algoritmu dvojitého škálování 100%-200%->>250% a druhý obrázek pouze s bicubic 100%->250%.
Aby bylo možné uživatelské rozhraní používat toto dvojité škálování, bude nutné upravit kód XAML pro zobrazení každého prvku Image. Následující příklady ukazují, jak použít dvojité škálování ve WPF v sadě Visual Studio pomocí knihovny DpiHelper a Shell.12/14.
Krok 1: Předsadíte image na 200 %, 300 % a tak dále pomocí funkce NearestNeighbor.
Předsadíte obrázek pomocí převaděče použitého na vazbu nebo s rozšířením značek XAML. Příklad:
<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" />
Pokud je potřeba, aby byl obrázek také motivovaný (většina, pokud ne všechny), může kód použít jiný převaděč, který napřed provede motivování obrázku a pak před škálováním. Kód může použít buď DpiPrescaleThemedImageConverter nebo DpiPrescaleThemedImageSourceConverterv závislosti na požadovaném výstupu převodu.
<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>
Krok 2: Ujistěte se, že je konečná velikost pro aktuální DPI správná.
Vzhledem k tomu, že WPF škáluje uživatelské rozhraní pro aktuální DPI pomocí bitmap ScaleMode vlastnost nastavená na UIElement, ovládací prvek Obrázek používající předem škálovaný obrázek, protože jeho zdroj bude vypadat dvakrát nebo třikrát větší, než by měl. Toto je několik způsobů, jak tento účinek čítače:
Pokud znáte rozměr původního obrázku na 100 %, můžete určit přesnou velikost ovládacího prvku Obrázek. Tyto velikosti budou odrážet velikost uživatelského rozhraní před tím, než se použije škálování.
<Image Source="{Binding Path=SelectedImage, Converter={StaticResource DpiPrescaleImageSourceConverter}}" Width="16" Height="16" />
Pokud není známa velikost původního obrázku, lze k vertikálnímu snížení kapacity konečného objektu Image použít LayoutTransform. Příklad:
<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>
Povolení podpory HDPI pro WebOC
Ve výchozím nastavení ovládací prvky WebOC (například Ovládací prvek WebBrowser ve WPF nebo IWebBrowser2 rozhraní) neumožňují detekci a podporu HDPI. Výsledkem bude vložený ovládací prvek s obsahem zobrazení, který je příliš malý na displeji s vysokým rozlišením. Následující článek popisuje, jak povolit podporu vysokého DPI v konkrétní instanci webOC.
Implementujte rozhraní IDocHostUIHandler (viz článek MSDN o 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);
}
Volitelně můžete implementovat rozhraní ICustomDoc (viz článek MSDN na ICustomDoc:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B")]
public interface ICustomDoc
{
void SetUIHandler(IDocHostUIHandler pUIHandler);
}
Přidružte třídu, která implementuje IDocHostUIHandler k dokumentu WebOC. Pokud jste implementovali ICustomDoc rozhraní výše, pak jakmile webOC vlastnost dokumentu je platná, přetypujte ji na ICustomDoc a volání SetUIHandler metoda, předání třídy, která implementuje 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);
Pokud jste neimplementovali rozhraní ICustomDoc, pak jakmile je vlastnost dokumentu WebOC platná, budete ji muset přetypovat na IOleObject a volat metodu SetClientSite
, která předává třídu, která implementuje IDocHostUIHandler. Nastavte příznak DOCHOSTUIFLAG_DPI_AWARE u DOCHOSTUIINFO předaný volání GetHostInfo
metody:
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;
}
To by mělo být vše, co potřebujete k získání ovládacího prvku WebOC pro podporu PROSTŘEDÍ HPDI.
Tipy
Pokud se vlastnost dokumentu v ovládacím prvku WebOC změní, možná budete muset znovu asociovat dokument s IDocHostUIHandler třídy.
Pokud výše uvedené informace nefungují, je známý problém s webOC, který změnu příznaku DPI nezabírá. Nejspolehlivější způsob, jak to opravit, je přepnout optické přiblížení webOC, což znamená dvě volání se dvěma různými hodnotami pro procento přiblížení. Kromě toho, pokud je toto alternativní řešení potřeba, může být nutné ho provést při každém volání navigace.
// 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); }