Desabilitar o reconhecimento de DPI para o aplicativo WPF

Dia bom!

Eu tenho trabalhado em um aplicativo WPF há algum tempo (como uma experiência de aprendizado e oh boy, foi uma experiência de aprendizado) e está finalmente pronto para ser lançado. Liberar significa instalá-lo no meu HTPC, onde ele será usado para navegar na minha coleção de filmes.

Eu projetei no meu PC que roda 1920 * 1080, mas na configuração normal de DPI, enquanto o HTPC / TV está rodando na mesma resolução, mas uma configuração de DPI mais alta, por razões óbvias.

O problema é que o meu aplicativo fica maluco no HTPC, bagunçando praticamente tudo no que diz respeito aos visuais. Eu sei que isso é devido a um mau design (mea culpa), mas como é um aplicativo que só será usado por mim, estou procurando uma solução rápida, não uma reformulação completa. Eu li que seria possível impedir que o aplicativo percebesse DPI, adicionando o seguinte ao AssemblyInfo.cs:

[assembly: System.Windows.Media.DisableDpiAwareness] 

No entanto, não parece ter nenhum efeito e o comportamento do aplicativo permanece o mesmo.

Alguém poderá me indicar a direção correta?

Obrigado Johan

DPIAwareness

Apenas algumas idéias (não tentei):

Você está correndo no XP? Essa opção pode não funcionar nessa plataforma.

O que se segue são, provavelmente, apenas maneiras diferentes de definir a mesma opção DpiAwareness:

  • veja as configurações do “modo de compatibilidade” no seu EXE … clique com o botão direito do mouse em suas propriedades … e ative “Desativar o dimensionamento de exibição”

  • crie um manifesto e diga que você não está ciente

      ...   false   ...  
  • chamar SetProcessDPIAware (acho que você tem que chamar isso antes ou seja antes de a janela ser criada)

    http://msdn.microsoft.com/en-gb/library/windows/desktop/ms633543(v=vs.85).aspx

Você pode chamar o IsProcessDPIAware para verificar se o processo de seus aplicativos teve a configuração aplicada ou não.

Descobrir o DPI

Veja como você descobre qual DPI está usando no momento:

Acesse o CompositionTarget através do PresentationSource de sua Window para descobrir qual escala de DPI ele está usando ….. você pode usar os valores para fazer alguns ajustes de escala, ou seja, reduza seu “material” (cujos tamanhos / comprimento / etc são especificados em Device Independent Units), de modo que quando ele é aumentado devido a um DPI mais alto estar em vigor, ele não explode o uso de pixel físico (… isso pode ser feito de várias maneiras, por exemplo, ViewBox ou cálculos em larguras, etc. em elementos) .

 double dpiX, double dpiY; PresentationSource presentationsource = PresentationSource.FromVisual(mywindow); if (presentationsource != null) // make sure it's connected { dpiX = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M11; dpiY = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M22; } 

Fazer ajustes de escala

  • use o truque do ViewBox

    Veja esta resposta que eu fiz antes que permitiu um Canvas para usar posições que foram interpretadas como pixel “nativo”, não importa o que o dimensionamento DPI.

    WPF para canvas de LCD Full HD

  • acesse a matriz de escalonamento TransFormToDevice no CompositionTarget e, a partir disso, calcule uma nova matriz que apenas desfaça essa escala e use isso em LayoutTransform ou LayoutTransform .

  • use um método (talvez até mesmo coloque-o em um Conversor) que modifique uma posição / comprimento DIP (Device Independent Position) em uma base explícita …. você pode fazer isso se quiser que o seu Tamanho da Janela corresponda a um tamanho de pixel específico.

Este artigo do blog explica como usar uma subclass Decorator para fazer exatamente isso.

Em suma, crie uma class como esta:

 namespace MyNamespace { public class DpiDecorator : Decorator { public DpiDecorator() { this.Loaded += (s, e) => { Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice; ScaleTransform dpiTransform = new ScaleTransform(1 / m.M11, 1 / m.M22); if (dpiTransform.CanFreeze) dpiTransform.Freeze(); this.LayoutTransform = dpiTransform; }; } }; }; 

Em seguida, adicione algo como xmlns:custom="clr-namespace:MyNamespace" à sua definição de janela de nível superior e, em seguida, coloque sua interface de usuário independente de DPI com .

Uma solução rápida seria apenas para envolver o conteúdo da sua janela em um Viewbox . Se o filho imediato do Viewbox tiver uma Largura e Altura explícitas, e se a razão de aspecto for a mesma em ambas as máquinas, isso deve colocá-lo em funcionamento praticamente imediatamente.

DpiAwareness afeta apenas a virtualização de DPI. No Windows, ele aparece como “Usar escala de estilo do Windows XP” na checkbox de resolução personalizada do Windows 7 e 8, onde marcada significa desativado e desmarcado significa ativado. (Lembre-se de aplicar na canvas principal do tamanho da fonte, se você alterá-lo! OK, a checkbox não é suficiente.)

Se você tem a Virtualização de DPI desativada, não faz diferença o que seu aplicativo informa ao SO que ele suporta, ele sempre usará escala padrão. Quando está ligado, faz a diferença entre o real dimensionamento (ciente) e apenas o uso de um redimensionamento bilinear em todo o aplicativo (inconsciente).

Acabei de me lembrar de outra advertência, o DPI virt não funcionará de maneira alguma sem o Aero Glass funcionar.

Nenhuma das opções acima realmente desativa o escalonamento de dpi no WPF.

É assim que o escalonamento de dpi é calculado em .net 4.6 pelo WPF: 1) HwndTarget, que é o CompositionTarget usado por todos os elementos visuais. 2) UIElement, que é a class base para visuais e o local onde os resultados dos cálculos de escala de dpi são armazenados.

O WPF garante que o dimensionamento global seja calculado uma vez quando o primeiro visual é preparado. Isso é controlado por um booleano em HwndTarget {private static bool _setDpi = true}. Depois de calcular o dpi, o campo _setDpi é definido como false e os methods de reconhecimento de dpi são atalhos usando código como este {if (! HwndTarget._setDpi) return;}

Uma coisa semelhante acontece para cada UIElement, que tem o mesmo padrão usando {private static bool _setDpi = true} para permitir o cálculo pela primeira vez.

A próxima verificação vem de ProcessDpiAwareness, que pode ser Nenhum, Processar ou Monitorar. Para impedir que o WPF considere monitores individuais, é necessário definir ProcessDpiAwareness to Process (int 1).

Finalmente, quando o dpi é calculado, o resultado é armazenado em duas listas chamadas DpiScaleXValues ​​e DpiScaleYValues ​​no UIElement. Então você precisa inicializá-los para corrigir os valores.

Aqui está um exemplo de código que eu uso para mim mesmo (só funciona para o .net 4.6):

  var setDpiHwnd = typeof(HwndTarget).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic); setDpiHwnd?.SetValue(null, false); var setProcessDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic); setProcessDpiAwareness?.SetValue(null, 1, null); var setDpi = typeof(UIElement).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic); setDpi?.SetValue(null, false); var setDpiXValues = (List)typeof(UIElement).GetField("DpiScaleXValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); setDpiXValues?.Insert(0, 1); var setDpiYValues = (List)typeof(UIElement).GetField("DpiScaleYValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); setDpiYValues?.Insert(0, 1); 

Para aqueles que usam o VB.NET, a maneira de acessar o app.manifest é:

insira a descrição da imagem aqui

e, em seguida, remova o comentário dessa parte e defina-a como “false”

insira a descrição da imagem aqui

A resposta de Calin Pirtea realmente desativa o escalonamento de DPI.

Para quem quer esticar o aplicativo para uma escala de exibição de mais de 100% e aceitar borrada, a resposta de Carlos Borau funciona. Aqui está um caminho para o .NET 4.6 (C #):

1 Adicione um arquivo de manifesto ao seu projeto de aplicativo

  1. clique com o botão direito do mouse em projeto, Adicionar-> Novo Item
  2. Escolha Geral-> Arquivo de Manifesto de Aplicação

2 Uncomment following e set dpiAware = false:

    false   
Intereting Posts