Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Um número crescente de dispositivos estão sendo enviados 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 dimensionado para atender às necessidades de visualização do conteúdo a uma distância de visualização normal para o dispositivo. Desde 2014, o principal alvo para ecrãs de alta densidade são os dispositivos de computação móvel (computadores portáteis clamshell, tablets e telefones).
O Windows 8.1 e superior contém vários recursos para permitir que essas máquinas trabalhem com monitores e ambientes onde a máquina está conectada a monitores de alta densidade e de densidade padrão ao mesmo tempo.
O Windows pode permitir-lhe ajustar o conteúdo para o dispositivo usando a definição "Ajustar o tamanho do texto e de outros itens" (disponível desde o Windows XP).
O Windows 8.1 e superior dimensionará automaticamente o conteúdo para que a maioria dos aplicativos seja consistente quando movido entre monitores de diferentes densidades de pixels. Quando a tela primária é de alta densidade (200% de escala) e a secundária é densidade padrão (100%), o Windows dimensionará automaticamente o conteúdo da janela do aplicativo para baixo na tela secundária (1 pixel exibido para cada 4 pixels renderizados pelo aplicativo).
O Windows terá como padrão o dimensionamento correto para a densidade de pixels e a distância de visualização da tela (Windows 7 e superior, configurável por OEM).
O Windows pode dimensionar automaticamente o conteúdo até 250% em novos dispositivos que excedam 280 ppi (a partir do Windows 8.1 S14).
O Windows tem uma maneira de lidar com a expansão da interface do usuário para aproveitar o aumento da contagem de pixels. Uma aplicação seleciona este sistema ao declarar-se "consciente do DPI do sistema". As aplicações que não fazem isso são redimensionadas pelo sistema. Isso pode resultar em uma experiência de usuário "confusa", onde todo o aplicativo é uniformemente esticado em pixels. Por exemplo:
O Visual Studio opta por reconhecer o dimensionamento 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 diferentes maneiras 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 de utilizador do WPF redimensiona-se automaticamente para 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 no Win32, WinForms e WPF já é dimensionado corretamente quando desenhado para o dispositivo de exibição.
As caixas de diálogo e janelas do Win32/WinForms possuem recursos para ativar o layout que se ajusta automaticamente ao texto (por exemplo, por meio de painéis de layout de grelha, fluxo e tabela). Isso ajuda a evitar posições de píxeis codificadas de forma fixa que não são dimensionadas quando os tamanhos de fonte são aumentados.
Os ícones fornecidos pelo sistema ou os recursos baseados em métricas do sistema (por exemplo, SM_CXICON e SM_CXSMICON) já são ampliados.
Win32 mais antigo (GDI, GDI+) e interface do usuário baseada em WinForms
Embora o WPF já tenha alto reconhecimento de DPI, grande parte do nosso código baseado em Win32/GDI não foi originalmente escrito com consciência de DPI em mente. O Windows forneceu APIs de dimensionamento de DPI. Correções para problemas do Win32 devem usá-los consistentemente em todo o produto. O Visual Studio forneceu uma biblioteca de classes auxiliar para evitar a duplicação de funcionalidades 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 que é incorporado ao Visual Studio. Você também pode achar que precisa suportar/direcionar muitas versões do Visual Studio e, portanto, usar o serviço de imagem em 2015 não é uma opção, pois ele não existe em versões anteriores. Esta seção também é para você, então.
Aumentar o tamanho de imagens muito pequenas
Imagens muito pequenas podem ser ampliadas e renderizadas em GDI e WPF usando alguns métodos comuns. As classes auxiliares de DPI geridas estão disponíveis para integradores internos e externos do Visual Studio para tratar do dimensionamento de ícones, bitmaps, faixas de imagens e listas de imagens. C/C++helpers 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 uma linha depois de incluir uma referência à biblioteca auxiliar. Por exemplo:
O dimensionamento de uma lista de imagens depende de a lista de imagens estar completa em tempo de carregamento ou de ser acrescentada em tempo de execução. Se concluído no momento do carregamento, chame LogicalToDeviceUnits() com a lista de imagens como 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 na lista de imagens.
imagelist.ImageSize = DpiHelper.LogicalToDeviceUnits(imagelist.ImageSize);
No código nativo, as dimensões podem ser dimensionadas ao criar a imagelist 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 dimensionar imagens para serem colocadas em listas de imagens, certifique-se de especificar a cor do plano de fundo usada para transparência ou use o dimensionamento NearestNeighbor (que causará distorções em 125% e 150%).
Consulte a documentação DpiHelper no MSDN.
A tabela a seguir mostra exemplos de como as imagens devem ser dimensionadas nos fatores de escala de DPI correspondentes. As imagens descritas em laranja denotam nossa prática recomendada a partir do Visual Studio 2013 (escala de 100%-200% DPI):
Problemas de layout
Problemas comuns de layout podem ser evitados principalmente mantendo os 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 levar em conta as imagens ampliadas.
As colunas em grades precisam ter larguras ajustadas para o texto ampliado.
Os tamanhos codificados ou o espaço entre os elementos também precisarão ser ampliados. Os tamanhos baseados apenas nas dimensões do texto geralmente são bons, porque as fontes são dimensionadas automaticamente.
As funções auxiliares estão disponíveis na DpiHelper classe para permitir o dimensionamento nos eixos X e Y:
LogicalToDeviceUnitsX/LogicalToDeviceUnitsY (funções permitem dimensionamento no eixo X/Y)
espaço int = DpiHelper.LogicalToDeviceUnitsX (10);
int altura = VsUI::DpiHelper::LogicalToDeviceUnitsY(5);
Existem sobrecargas da função LogicalToDeviceUnits que permitem 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, vá para os exemplos de extensibilidade VSSDK do Visual Studio e clone o exemplo High-DPI_Images_Icons.
Em 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 estática para sincronização de threads e você pode ter problemas de inicialização de ordem. Converta essas estáticas em variáveis de membros não estáticas ou envolva-as em uma função (para que elas sejam construídas no primeiro acesso).
Para acessar as funções auxiliares de DPI do código gerenciado que será executado dentro do ambiente do Visual Studio:
O projeto consumidor 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 suportados (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 no WPF numa UI com capacidade de zoom
No WPF, os bitmaps são redimensionados automaticamente pelo WPF para o nível de zoom 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 itens de menu porque introduz a perceção de imprecisão.
Recomendações:
Para imagens de logotipo e ilustrações de banners, 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 a imprecisão (a 200% e 300%).
Para grandes níveis de zoom que não são múltiplos de 100% (por exemplo, 250% ou 350%), dimensionar imagens de iconografia usando bicúbico resulta numa interface de utilizador confusa e desbotada. Um resultado melhor é obtido primeiro dimensionando a imagem com NearestNeighbor para o maior múltiplo de 100% (por exemplo, 200% ou 300%) e dimensionando com bicúbico a partir daí. Consulte Caso especial: pré-escalonamento de imagens WPF para níveis de DPI elevados 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 em 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 janelas e caixas de diálogo 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, em elementos com pais Win32 e janelas de designer que ficam sem processo, como o Blend.
Algumas interfaces do usuário podem ser dimensionadas independentemente do nível de zoom 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: dimensionamento de imagens 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 imagens bicúbicas resulta em uma interface do usuário confusa e desbotada. A impressão dessas imagens ao lado de um texto nítido é quase como a de uma ilusão de ótica. As imagens parecem estar mais próximas do olho e fora de foco em relação ao texto. O resultado do dimensionamento nesse tamanho ampliado pode ser melhorado, dimensionando primeiro a imagem com NearestNeighbor para o maior múltiplo de 100% (por exemplo, 200% ou 300%) e, em seguida, dimensionando a imagem com Bicúbico para o restante (50% adicionais).
A seguir está um exemplo das diferenças nos resultados, onde a primeira imagem é dimensionada com o algoritmo de escala dupla melhorado (nova versão) 100%->200%->250%, e a segunda somente com o algoritmo bicúbico 100%->250%.
Para permitir que a interface do utilizador utilize este dimensionamento duplo, a marcação XAML para exibir cada elemento de imagem necessitará de ser modificada. Os exemplos a seguir demonstram como usar o dimensionamento duplo no WPF no Visual Studio usando a biblioteca DpiHelper e Shell.12/14.
Etapa 1: Pré-dimensione a imagem para 200%, 300%e assim por diante usando NearestNeighbor.
Pré-dimensione 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 precisa ter um tema aplicado (a maioria, se não todas, devem ser), o código pode usar um conversor diferente que primeiro faz a aplicação do tema na imagem e, em seguida, o pré-dimensionamento. A marcação pode usar o DpiPrescaleThemedImageConverter ou o 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 dimensionará a interface do utilizador para o DPI atual usando a propriedade BitmapScalingMode definida no UIElement, um controlo de imagem que utiliza uma imagem pré-dimensionada como sua origem parecerá duas ou três vezes maior do que deveria. Seguem-se algumas formas de contrariar este efeito:
Se você souber a dimensão da imagem original em 100%, você pode especificar o tamanho exato do controle Image. 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 reduzir a escala do objeto Image 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 HDPI para o WebOC
Por padrão, os controles WebOC (como o controle WebBrowser no WPF ou a interface IWebBrowser2) não habilitam a deteção e o suporte HDPI. O resultado será um controlo incorporado com conteúdo de visualização demasiado pequeno num ecrã de alta resolução. A seguir descrevemos como habilitar o suporte a DPI alto em uma instância WebOC da Web específica.
Implemente a interface IDocHostUIHandler (consulte o artigo do MSDN sobre 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);
}
Opcionalmente, implemente a interface ICustomDoc (consulte o artigo do MSDN sobre o 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 tiver implementado a interface ICustomDoc acima, logo que a propriedade documento do WebOC estiver 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 SetClientSite método, passando a classe que implementa IDocHostUIHandler. Defina o sinalizador DOCHOSTUIFLAG_DPI_AWARE no DOCHOSTUIINFO passado para a chamada do método 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 o/a utilizador precisa para que o controlo WebOC suporte HPDI.
Sugestões
Se a propriedade do documento no controle WebOC for alterada, talvez seja necessário reassociar o documento à classe IDocHostUIHandler.
Se o acima não funcionar, há um problema conhecido com o WebOC por não reconhecer a alteração no indicador de DPI. A maneira mais fiável de resolver isto é alternar entre os diferentes valores do zoom ótico do WebOC, ou seja, efetuar duas chamadas com dois valores diferentes para a percentagem de zoom. Além disso, se essa solução alternativa for necessária, pode ser necessário executá-la em todas as chamadas 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); }