Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Um número crescente de dispositivos está sendo enviado com telas de "alta resolução". Essas telas normalmente têm mais de 200 pixels por polegada (ppi). Trabalhar com um aplicativo nesses computadores exigirá que o conteúdo seja escalado verticalmente para atender às necessidades de exibição do conteúdo a uma distância normal de exibição para o dispositivo. A partir de 2014, o principal alvo para exibições de alta densidade são dispositivos móveis de computação (tablets, notebooks clamshell e telefones).
O Windows 8.1 e superior contém vários recursos para permitir que esses computadores trabalhem com exibições e ambientes em que o computador está anexado a exibições de alta densidade e densidade padrão ao mesmo tempo.
O Windows permite que você dimensione o conteúdo para o dispositivo usando a configuração "Tornar texto e outros itens maiores ou menores". Esta configuração está disponível desde o Windows XP.
O Windows 8.1 e superior dimensiona automaticamente o conteúdo para que a maioria dos aplicativos seja consistente quando movido entre exibições de densidades de pixel diferentes. Quando a exibição primária for de alta densidade (200% dimensionamento) e a exibição secundária for de densidade padrão (100%), o Windows dimensionará automaticamente o conteúdo da janela do aplicativo para baixo na exibição secundária (1 pixel exibido para cada 4 pixels renderizados pelo aplicativo).
O Windows usará como padrão o dimensionamento correto para a densidade de pixels e a distância de visualização do display (Windows 7 e superior, configurável pelo OEM).
O Windows pode dimensionar automaticamente o conteúdo até 250% em novos dispositivos que excedem 280 ppi (a partir do Windows 8.1 S14).
O Windows possui uma forma de lidar com o escalonamento da interface do usuário para tirar proveito do aumento na quantidade de pixels. Um aplicativo aceita esse sistema declarando-se "ciente do DPI do sistema". Os aplicativos que não fazem isso são escalados verticalmente pelo sistema. Isso pode resultar em uma experiência de usuário "difusa", em que todo o aplicativo é uniformemente estendido por pixels. Por exemplo:
O Visual Studio opta por ter reconhecimento de escala de DPI e, portanto, não é "virtualizado".
O Windows (e o Visual Studio) aproveitam várias tecnologias de interface do usuário, que têm maneiras diferentes de lidar com fatores de dimensionamento definidos pelo sistema. Por exemplo:
O WPF mede os controles de forma independente do dispositivo (unidades, não pixels). A interface do usuário do WPF escala automaticamente de acordo com o DPI atual.
Todos os tamanhos de texto, independentemente da estrutura da interface do usuário, são expressos em pontos e, portanto, são tratados pelo sistema como independentes de DPI. O texto em Win32, WinForms e WPF já é dimensionado corretamente quando desenhado para o dispositivo de exibição.
Caixas de diálogo e janelas Win32/WinForms têm meios para ativar layouts que redimensionam com o texto (por exemplo, utilizando painéis de layout de grade, fluxo e tabela). Elas permitem evitar posições de pixels codificadas de forma fixa que não são ajustadas quando os tamanhos da fonte são aumentados.
Ícones fornecidos pelo sistema ou recursos com base nas métricas do sistema (por exemplo, SM_CXICON e SM_CXSMICON) já estão escalados verticalmente.
Win32 mais antigo (GDI, GDI+) e interface do usuário baseada em WinForms
Embora o WPF já tenha suporte a DPI elevado, grande parte do nosso código baseado em Win32/GDI não foi originalmente escrita com reconhecimento de DPI. O Windows forneceu APIs de dimensionamento de DPI. As correções para problemas do Win32 devem usá-las consistentemente em todo o produto. O Visual Studio forneceu uma biblioteca de classes auxiliar para evitar a duplicação da funcionalidade e garantir a consistência em todo o produto.
Imagens de alta resolução
Esta seção é principalmente para desenvolvedores que estendem o Visual Studio 2013. Para o Visual Studio 2015, use o serviço de imagem integrado ao Visual Studio. Você também pode descobrir que precisa dar suporte/direcionar muitas versões do Visual Studio e, portanto, usar o serviço de imagem na versão de 2015 não é viável porque ele não existe em versões anteriores. Esta seção também é para você.
Redimensionamento de imagens que são muito pequenas
Imagens muito pequenas podem ser dimensionadas e renderizadas em GDI e WPF usando alguns métodos comuns. As classes auxiliares gerenciadas de DPI estão disponíveis para integradores internos e externos do Visual Studio para tratar do dimensionamento de ícones, bitmaps, tiras de imagens e listas de imagens. Os auxiliares C/C++nativos baseados em Win32 estão disponíveis para dimensionar HICON, HBITMAP, HIMAGELIST e VsUI::GdiplusImage. O dimensionamento de um bitmap normalmente requer apenas uma alteração de linha depois de incluir uma referência à biblioteca auxiliar. Por exemplo:
O dimensionamento de uma lista de imagens depende se a lista de imagens está concluída no tempo de carregamento ou é acrescentada em tempo de execução. Se for concluído em tempo de carga, chame LogicalToDeviceUnits() com a lista de imagens da mesma forma que faria com um bitmap. Quando o código precisar carregar um bitmap individual antes de compor a lista de imagens, certifique-se de dimensionar o tamanho da imagem da lista de imagens:
imagelist.ImageSize = DpiHelper.LogicalToDeviceUnits(imagelist.ImageSize);
No código nativo, as dimensões podem ser dimensionadas ao criar a lista de imagens da seguinte maneira:
ImageList_Create(VsUI::DpiHelper::LogicalToDeviceUnitsX(16),VsUI::DpiHelper::LogicalToDeviceUnitsY(16), ILC_COLOR32|ILC_MASK, nCount, 1);
As funções na biblioteca permitem especificar o algoritmo de redimensionamento. Ao redimensionar imagens para serem colocadas em imagelists, especifique a cor de fundo utilizada para fins de transparência ou use a escala NearestNeighbor (que pode causar distorções em 125% e 150%).
Consulte a documentação do DpiHelper no MSDN.
A tabela a seguir mostra exemplos de como as imagens devem ser dimensionadas em fatores de dimensionamento de DPI correspondentes. As imagens descritas em laranja denotam nossa melhor prática a partir do Visual Studio 2013 (100%-200% dimensionamento de DPI):
Problemas de layout
Problemas comuns de layout podem ser evitados principalmente mantendo pontos na interface do usuário dimensionados e relativos uns aos outros, em vez de usar locais absolutos (especificamente, em unidades de pixel). Por exemplo:
As posições de layout/texto precisam ser ajustadas para considerar imagens ampliadas.
As colunas em grades precisam ter larguras ajustadas para o texto ampliado.
Tamanhos embutidos em código ou espaço entre elementos também precisarão ser aumentados. Tamanhos baseados apenas em dimensões de texto geralmente são adequados, pois as fontes são automaticamente redimensionadas.
As funções auxiliares estão disponíveis na classe DpiHelper para permitir o dimensionamento nos eixos X e Y.
LogicalToDeviceUnitsX/LogicalToDeviceUnitsY (as funções permitem dimensionamento no eixo X/Y)
int space = DpiHelper.LogicalToDeviceUnitsX (10);
int height = VsUI::DpiHelper::LogicalToDeviceUnitsY(5);
Há sobrecargas LogicalToDeviceUnits para permitir o dimensionamento de objetos, como Rect, Point e Size.
Usando a biblioteca/classe DPIHelper para dimensionar imagens e layout
A biblioteca auxiliar de DPI do Visual Studio está disponível em formulários nativos e gerenciados e pode ser usada fora do shell do Visual Studio por outros aplicativos.
Para usar a biblioteca, acesse os exemplos de extensibilidade do VSSDK do Visual Studio e clone o exemplo de High-DPI_Images_Icons.
Nos arquivos de origem, inclua VsUIDpiHelper.h e chame as funções estáticas da VsUI::DpiHelper classe:
#include "VsUIDpiHelper.h"
int cxScaled = VsUI::DpiHelper::LogicalToDeviceUnitsX(cx);
VsUI::DpiHelper::LogicalToDeviceUnits(&hBitmap);
Observação
Não use as funções auxiliares em variáveis estáticas de nível de módulo ou de classe. A biblioteca também usa variáveis estáticas para sincronização de threads e você pode encontrar problemas de inicialização de ordem. Converta essas variáveis estáticas em variáveis membro não estáticas ou encapsule-as em uma função (para que sejam construídas apenas quando acessadas pela primeira vez).
Para acessar as funções auxiliares de DPI do código gerenciado que serão executadas dentro do ambiente do Visual Studio:
O projeto de consumo deve fazer referência à versão mais recente do Shell MPF. Por exemplo:
<Reference Include="Microsoft.VisualStudio.Shell.14.0.dll" />Verifique se o projeto tem referências a System.Windows.Forms, PresentationCore e PresentationUI.
No código, use o namespace Microsoft.VisualStudio.PlatformUI e chame funções estáticas da classe DpiHelper. Para tipos com suporte (pontos, tamanhos, retângulos e assim por diante), há funções de extensão fornecidas que retornam novos objetos dimensionados. Por exemplo:
using Microsoft.VisualStudio.PlatformUI; double x = DpiHelper.LogicalToDeviceUnitsX(posX); Point ptScaled = ptOriginal.LogicalToDeviceUnits(); DpiHelper.LogicalToDeviceUnits(ref bitmap);
Lidando com a desfocagem de imagem do WPF na interface do usuário ampliável
No WPF, os bitmaps são redimensionados automaticamente pelo WPF para o nível de zoom de DPI atual usando um algoritmo bicúbico de alta qualidade (padrão), que funciona bem para imagens ou capturas de tela grandes, mas é inadequado para ícones de item de menu porque introduz a percepção de borrão.
Recomendações:
Para a imagem de logotipo e a arte de faixas, o modo de redimensionamento padrão BitmapScalingMode pode ser usado.
Para itens de menu e imagens de iconografia, o BitmapScalingMode deve ser usado quando não causar outros artefatos de distorção, para eliminar o desfoque (com zoom de 200% e 300%).
Para níveis de zoom grandes que não são múltiplos de 100% (por exemplo, 250% ou 350%), redimensionar imagens de iconografia usando algoritmos bicúbicos resulta em uma interface do usuário desfocada e desbotada. Um resultado melhor é obtido dimensionando primeiro a imagem com NearestNeighbor para o maior múltiplo de 100% (por exemplo, 200% ou 300%) e dimensionando com bicubic a partir daí. Consulte o caso especial: pré-dimensionamento de imagens do WPF para níveis de DPI grandes para obter mais informações.
A classe DpiHelper no namespace Microsoft.VisualStudio.PlatformUI fornece um membro BitmapScalingMode que pode ser usado para associação. Ele permitirá que o shell do Visual Studio controle o modo de dimensionamento de bitmap em todo o produto uniformemente, dependendo do fator de dimensionamento de DPI.
Para usá-lo no XAML, adicione:
xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.14.0"
<Setter Property="RenderOptions.BitmapScalingMode" Value="{x:Static vs:DpiHelper.BitmapScalingMode}" />
O shell do Visual Studio já define essa propriedade em caixas de diálogo e janelas de nível superior. A interface do usuário baseada em WPF em execução no Visual Studio já a herdará. Se a configuração não se propagar para suas partes específicas da interface do usuário, ela poderá ser definida no elemento raiz da interface do usuário XAML/WPF. Os locais onde isso acontece incluem pop-ups, elementos com pais Win32 e janelas de designer que são executadas fora do processo, como o Blend.
Algumas interfaces do usuário podem ser dimensionadas independentemente do nível de zoom de DPI definido pelo sistema, como o editor de texto do Visual Studio e designers baseados em WPF (WPF Desktop e Windows Store). Nesses casos, DpiHelper.BitmapScalingMode não deve ser usado. Para corrigir esse problema no editor, a equipe do IDE criou uma propriedade personalizada intitulada RenderOptions.BitmapScalingMode. Defina esse valor de propriedade como HighQuality ou NearestNeighbor, dependendo do nível de zoom combinado do sistema e da interface do usuário.
Caso especial: pré-dimensionamento de imagens do WPF para grandes níveis de DPI
Para níveis de zoom muito grandes que não são múltiplos de 100% (por exemplo, 250%, 350%e assim por diante), dimensionar imagens de iconografia com resultados bicubicos em interface do usuário difusa e lavada. A impressão dessas imagens ao lado de texto nítido é quase como a de uma ilusão óptica. As imagens parecem estar mais próximas do olho e fora de foco em relação ao texto. O resultado de dimensionamento nesse tamanho ampliado pode ser aprimorado primeiro dimensionando a imagem com NearestNeighbor para o maior múltiplo de 100% (por exemplo, 200% ou 300%) e dimensionando com bicubic para o restante (mais 50%).
Veja a seguir um exemplo das diferenças nos resultados, em que a primeira imagem é dimensionada com o algoritmo aprimorado de redimensionamento duplo 100%->200%->250%, e a segunda apenas com bicúbico 100%->250%.
Para permitir que a interface do usuário utilize o escalonamento duplo, a marcação XAML para exibir cada elemento de imagem precisará ser modificada. Os exemplos a seguir demonstram como usar o dimensionamento duplo no WPF no Visual Studio usando a biblioteca DpiHelper e o Shell.12/14.
Etapa 1: Pré-dimensionar a imagem para 200%, 300%e assim por diante usando NearestNeighbor.
Pré-dimensionar a imagem usando um conversor aplicado em uma associação ou com uma extensão de marcação XAML. Por exemplo:
<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" />
Se a imagem também precisar ser tematizada (a maioria, se não todas, deve), a marcação poderá usar um conversor diferente que primeiro tematiza a imagem e, em seguida, faz o pré-escalonamento. A marcação pode usar DpiPrescaleThemedImageConverter ou DpiPrescaleThemedImageSourceConverter, dependendo da saída de conversão desejada.
<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>
Etapa 2: verifique se o tamanho final está correto para o DPI atual.
Como o WPF escalonará a interface do usuário para o DPI atual usando a propriedade BitmapScalingMode definida no UIElement, um controle de imagem usando uma imagem pré-escalonada como sua fonte parecerá ser duas ou três vezes maior do que deveria. Veja a seguir algumas maneiras de combater esse efeito:
Se você souber a dimensão da imagem original em 100%, poderá especificar o tamanho exato do controle Imagem. Esses tamanhos refletirão o tamanho da interface do usuário antes que o dimensionamento seja aplicado.
<Image Source="{Binding Path=SelectedImage, Converter={StaticResource DpiPrescaleImageSourceConverter}}" Width="16" Height="16" />Se o tamanho da imagem original não for conhecido, um LayoutTransform pode ser usado para redimensionar o objeto de imagem final. Por exemplo:
<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>
Habilitando o suporte a HDPI para o WebOC
Por padrão, os controles WebOC (como o controle WebBrowser no WPF ou a interface IWebBrowser2) não habilitam a detecção e o suporte do HDPI. O resultado será um controle inserido com conteúdo de exibição muito pequeno em uma exibição de alta resolução. A seguir, descreve como habilitar o suporte a alta DPI em uma instância webOC específica.
Implemente a interface IDocHostUIHandler (consulte o artigo do MSDN no 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);
}
Opcionalmente, implemente a interface ICustomDoc (consulte o artigo do MSDN no ICustomDoc:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B")]
public interface ICustomDoc
{
void SetUIHandler(IDocHostUIHandler pUIHandler);
}
Associe a classe que implementa IDocHostUIHandler ao documento do WebOC. Se você implementou a interface ICustomDoc acima, assim que a propriedade de documento do WebOC for válida, converta-a em um ICustomDoc e chame o método SetUIHandler, passando a classe que implementa 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);
Se você não implementou a interface ICustomDoc, assim que a propriedade do documento do WebOC for válida, você precisará convertê-la em um IOleObject e chamar o método SetClientSite, passando a classe que implementa IDocHostUIHandler. Defina a flag DOCHOSTUIFLAG_DPI_AWARE no DOCHOSTUIINFO passado para o método de chamada 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;
}
Isso deve ser tudo o que você precisa para fazer com que o controle WebOC ofereça suporte à HPDI.
Dicas
Se a propriedade do documento no controle WebOC for alterada, talvez seja necessário reassociar o documento com a classe IDocHostUIHandler.
Se o acima não funcionar, haverá um problema conhecido com o WebOC não captando a alteração no sinalizador de DPI. A maneira mais confiável de corrigir isso é alternar o zoom óptico do WebOC, o que significa duas chamadas com dois valores diferentes para o percentual de zoom. Além disso, se essa solução alternativa for necessária, talvez seja necessário executá-la em cada chamada de navegação.
// 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); }