Подтвердить что ты не робот

Отключить распознавание DPI для приложения WPF

Добрый день!

Я уже некоторое время работаю над WPF-приложением (как опыт обучения, а мальчик это был опыт обучения), и он, наконец, готов к выпуску. Release означает установку его на моем HTPC, где он будет использоваться для просмотра моей коллекции фильмов.

Я разработал его на своем ПК с разрешением 1920 * 1080, но при нормальной настройке DPI, в то время как HTPC/TV работает с одинаковым разрешением, но с более высоким значением DPI по понятным причинам.

Проблема заключается в том, что мое приложение работает на HTPC, и все, что касается визуальных эффектов, довольно много. Я знаю, что это связано с плохим дизайном (mea culpa), но поскольку это приложение, которое будет использоваться только мной, я ищу быстрое исправление, а не полную редизайн. Я прочитал, что было бы возможно остановить приложение от DPI, добавив следующее в AssemblyInfo.cs:

[assembly: System.Windows.Media.DisableDpiAwareness]

Однако, похоже, это не имеет никакого эффекта, и поведение приложения остается прежним.

Может ли кто-нибудь указать мне в правильном направлении?

Спасибо, Johan

4b9b3361

Ответ 1

DPIAwareness

Просто некоторые идеи (не пробовал):

Вы работаете на XP? Эта опция может не работать на этой платформе.

Ниже приведены, вероятно, просто разные способы установки одного и того же параметра DpiAwareness:

  • посмотрите на настройки "режима совместимости" на вашем EXE... щелкните правой кнопкой мыши его свойства... и включите "Отключить масштабирование экрана"

  • создайте манифест и скажите, что вы не знаете

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
     ...
      <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
          <dpiAware>false</dpiAware>
        </asmv3:windowsSettings>
      </asmv3:application>
     ...
    </assembly>
    
  • позвоните SetProcessDPIAware (думаю, вы должны позвонить так рано, то есть до создания Window)

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

Вы можете позвонить в IsProcessDPIAware, чтобы проверить, применены ли к процессу вашего приложения настройки или нет.

Выяснение DPI

Вот как вы узнаете, какой DPI вы используете в данный момент:

Получите доступ к CompositionTarget через PresentationSource вашего Window, чтобы узнать, какой DPI масштабирует его, используя..... затем вы можете использовать значения, чтобы сделать некоторые настройки масштабирования, т.е. уменьшить ваши "вещи" (чьи размеры/длина /etc указываются в независимых от устройства единицах измерения), поэтому, когда его масштабирование увеличивается из-за более высокого значения DPI, оно не взрывает использование физических пикселей (... это может быть сделано различными способами, например, ViewBox, или вычисления по ширине и т.д. по элементам).

double dpiX, double dpiY;
PresentationSource presentationsource = PresentationSource.FromVisual(mywindow);

if (presentationsource != null) // make sure it connected
{
    dpiX = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M11;
    dpiY = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M22;
}

Выполнить масштабирование

  • использовать трюк ViewBox

    Посмотрите этот ответ, который я сделал ранее, что позволило Canvas использовать позиции, которые интерпретировались как "родной" пиксель, независимо от масштаба DPI.

    WPFдля ЖК-экрана Full HD

  • получить доступ к матрице масштабирования TransFormToDevice на CompositionTarget, и из этого вычислить новую матрицу, которая просто отменяет это масштабирование, и использовать ее в LayoutTransform или RenderTransform.

  • используйте метод (возможно, даже поместите его в конвертер), который изменяет позицию/длину DIP (независимой от устройства позиции) на явной основе... вы можете сделать это, если хотите, чтобы размер окна соответствовал определенному размеру пикселя.

Ответ 2

В этой статье блога объясняется, как использовать подкласс Decorator для этого.

Короче говоря, создайте такой класс:

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;
            };
        }
    };
};

Затем добавьте что-то вроде xmlns:custom="clr-namespace:MyNamespace" в определение окна верхнего уровня, затем добавьте свой независимый от DPI интерфейс пользователя с <custom:DpiDecorator>... </custom:DpiDecorator>.

Ответ 3

Ни один из вышеперечисленных действительно не отключает масштабирование dpi в WPF.

Так вычисляется масштабирование dpi в .net 4.6 WPF: 1) HwndTarget, который является CompositionTarget, используемым всеми визуальными эффектами. 2) UIElement, который является базовым классом для визуальных эффектов и местом хранения результатов масштабирования dpi.

WPF гарантирует, что глобальное масштабирование рассчитывается один раз, когда будет подготовлен первый визуал. Это контролируется логическим значением на HwndTarget {private static bool _setDpi = true}. После вычисления dpi в поле _setDpi установлено значение false, а методы распознавания dpi - это ярлык с использованием кода, подобного этому {if (! HwndTarget._setDpi) return;}

Аналогичная ситуация наблюдается для каждого UIElement, который имеет один и тот же шаблон, используя {private static bool _setDpi = true}, чтобы разрешить вычисления в первый раз.

Следующая проверка происходит из ProcessDpiAwareness, которая может быть None, Process или Monitor. Чтобы остановить WPF от рассмотрения отдельных мониторов, вам необходимо установить ProcessDpiAwareness для процесса (int 1).

Наконец, когда вычисляется dpi, результат сохраняется в 2 списках, называемых DpiScaleXValues ​​и DpiScaleYValues ​​на UIElement. Поэтому вам нужно инициализировать их для правильных значений.

Вот пример кода, который я использую для себя (работает только для .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<double>)typeof(UIElement).GetField("DpiScaleXValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiXValues?.Insert(0, 1);

        var setDpiYValues = (List<double>)typeof(UIElement).GetField("DpiScaleYValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiYValues?.Insert(0, 1);

Ответ 4

Быстрое исправление будет состоять только в том, чтобы обернуть содержимое окна в Viewbox. Если у непосредственного дочернего элемента Viewbox есть явная ширина и высота, и если соотношение сторон одинаково на обеих машинах, то это должно заставить вас работать и работать почти сразу.

Ответ 5

DpiAwareness влияет только на виртуализацию DPI. В Windows это отображается как "Использовать масштабирование стиля Windows XP" в окне пользовательского разрешения Windows 7 и 8, где отмечено флажок "отключено" и "Непроверено" означает "включено". (Не забудьте применить на главном экране размер шрифта, если вы его измените! OK, чтобы окно было недостаточно.)

Если вы отключили виртуализацию DPI, то имеет значение, отличное от того, что ваше приложение сообщает поддерживаемой ОС, оно всегда будет использовать стандартное масштабирование. Когда он включен, он делает разницу между истинным масштабированием (знанием) и просто ударом билинейного изменения размера для всего приложения (не осознавая).

Я только что вспомнил еще одно предостережение, DPI virt не будет работать вообще без работы Aero Glass.

Ответ 6

Для тех, кто использует VB.NET, способ доступа к app.manifest:

введите описание изображения здесь

а затем раскомментируйте эту часть и установите ее "false"

введите описание изображения здесь

Ответ 7

Calin Pirtea ответ действительно отключить масштабирование DPI.

Для тех, кто хочет растянуть приложение для отображения масштаба более 100% и принять размытость, Carlos Borau ответ работает. Вот способ для .NET 4.6 (С#):

1 Добавьте файл манифеста в проект приложения

  1. щелкните правой кнопкой мыши проект, Add-> Новый элемент
  2. Выберите General-> Файл манифеста приложения

2 Раскомментируйте следующее и установите dpiAware = false:

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
          <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
      </windowsSettings>
  </application>

Ответ 8

После обновления до среды выполнения .NET 4.8 в Windows 10 я получил следующее исключение с подходом Calin для настройки ProcessDpiAwareness

System.ArgumentException: "Объект типа" System.Int32 "не может быть преобразован в тип" System.Nullable "1 [MS.Win32.NativeMethods + PROCESS_DPI_AWARENESS] '.'

Изменение кода на приведенное ниже разрешает исключение, но я не уверен, что он достигает того же результата.

var processDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic);

if (processDpiAwareness != null)
{
      var underlyingType = Nullable.GetUnderlyingType(processDpiAwareness.PropertyType);

      if (underlyingType != null)
      {
           var val = Enum.Parse(underlyingType, "PROCESS_SYSTEM_DPI_AWARE");
           processDpiAwareness.SetValue(null, val, null);
      }
 }