Создание приложения с поддержкой DPI

У меня есть приложение формы в С#. Когда я меняю монитор DPI, все элементы управления перемещаются. Я использовал код this.AutoScaleMode = AutoScaleMode.Dpi, но это не устранило проблему.

Есть ли у кого-нибудь идеи?

Ответ 1

EDIT: с.NET 4.7 в формах окон улучшена поддержка High DPI. Подробнее об этом см. На docs.microsoft.com. Он работает только для Windows 10 Creators Update и выше, поэтому, возможно, нецелесообразно использовать это, но в зависимости от вашей пользовательской базы.


Трудно, но не невозможно. Ваш лучший вариант - переходить в WPF, но это может оказаться невыполнимым.

Я потратил много времени на эту проблему. Вот несколько правил/рекомендаций, чтобы заставить его работать правильно без FlowLayoutPanel или TableLayoutPanel:

  • Всегда редактируйте/создавайте свои приложения по умолчанию 96 DPI (100%). Если вы создаете в 120DPI (125% f.ex), это будет очень плохо, когда вы вернетесь к 96 DPI, чтобы работать с ним позже.
  • Я успешно использовал AutoScaleMode.Font, я не пробовал AutoScaleMode.DPI много.
  • Убедитесь, что вы используете размер шрифта по умолчанию для всех ваших контейнеров (формы, панели, вкладки, пользовательские элементы управления и т.д.). 8,25 пикс. Предпочтительно он не должен устанавливаться в файле.Designer.cs вообще для всех контейнеров, чтобы он использовал шрифт по умолчанию из класса контейнера.
  • Все контейнеры должны использовать один и тот же AutoScaleMode
  • Убедитесь, что все контейнеры имеют указанную ниже строку в файле Designer.cs:

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);//for design in 96 DPI

  • Если вам нужно установить разные размеры шрифта на ярлыки/текстовые поля и т.д., Установите их для каждого элемента управления вместо установки шрифта в классе контейнера, потому что winforms использует настройку шрифтов контейнеров для масштабирования содержимого и имеющий f.ex панель с другим размером шрифта чем содержащая его форма гарантирована. Это может сработать, если форма и все контейнеры в форме используют один и тот же размер шрифта, но я не пробовал.
  • Используйте другую машину или виртуальную установку Windows (VMware, Virtual PC, VirtualBox) с более высоким параметром DPI, чтобы немедленно протестировать ваш дизайн. Просто запустите скомпилированный файл.exe из папки /bin/Debug на машине DEV.

Я гарантирую, что если вы будете следовать этим рекомендациям, вы будете в порядке, даже если вы разместили элементы управления с конкретными якорями и не используете поточную панель. У нас есть приложение, построенное таким образом, развернутое на сотнях машин с разными настройками DPI, и у нас больше нет никаких жалоб. Все формы/контейнеры/сетки/кнопки/текстовое поле и т.д. Размеры масштабируются правильно, как и шрифт. Изображения тоже работают, но они, как правило, немного пикселируются при высоком DPI.

EDIT: эта ссылка содержит много полезной информации, особенно если вы решите использовать AutoScaleMode.DPI: fooobar.com/questions/95421/...

Ответ 2

Наконец-то я нашел решение проблемы как ориентации экрана, так и обработки DPI.
Microsoft уже предоставила документ, объясняющий его, но с небольшим недостатком, который полностью уничтожит обработку DPI. Просто выполните решение, приведенное в документе ниже в разделе "Создание отдельного кода макета для каждой ориентации" http://msdn.microsoft.com/en-us/library/ms838174.aspx

Тогда ВАЖНАЯ часть! Внутри кода для методов Landscape() и Portrait() в самом конце каждого добавить эти строки:

this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;

Итак, код для этих двух методов будет выглядеть следующим образом:

protected void Portrait()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(88, 216);
   this.crawlTime.Size = new System.Drawing.Size(136, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(10, 216);
   this.crawlTimeLabel.Size = new System.Drawing.Size(64, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(88, 200);
   this.crawlStartTime.Size = new System.Drawing.Size(136, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(10, 200);
   this.crawlStartedLabel.Size = new System.Drawing.Size(64, 16);
   this.light1.Location = new System.Drawing.Point(208, 66);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(192, 66);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(88, 182);
   this.linkCount.Size = new System.Drawing.Size(136, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(10, 182);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 84);
   this.currentPageBox.Size = new System.Drawing.Size(214, 90);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 68);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(214, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(10, 48);
   this.noProxyCheck.Size = new System.Drawing.Size(214, 20);
   this.startButton.Location = new System.Drawing.Point(8, 240);
   this.startButton.Size = new System.Drawing.Size(216, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 24);
   this.addressBox.Size = new System.Drawing.Size(214, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

protected void Landscape()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(216, 136);
   this.crawlTime.Size = new System.Drawing.Size(96, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(160, 136);
   this.crawlTimeLabel.Size = new System.Drawing.Size(48, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(64, 120);
   this.crawlStartTime.Size = new System.Drawing.Size(248, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(8, 120);
   this.crawlStartedLabel.Size = new System.Drawing.Size(48, 16);
   this.light1.Location = new System.Drawing.Point(296, 48);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(280, 48);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(80, 136);
   this.linkCount.Size = new System.Drawing.Size(72, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(8, 136);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 64);
   this.currentPageBox.Size = new System.Drawing.Size(302, 48);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 48);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(50, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(168, 16);
   this.noProxyCheck.Size = new System.Drawing.Size(152, 24);
   this.startButton.Location = new System.Drawing.Point(8, 160);
   this.startButton.Size = new System.Drawing.Size(304, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 20);
   this.addressBox.Size = new System.Drawing.Size(150, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

Работает как очарование для меня.

Ответ 3

примечание: это не исправит перемещение элементов управления при изменении dpi. это только исправит размытый текст !!.


Как исправить размытые Windows Forms в настройках с высоким разрешением:

  1. Перейдите в дизайнер форм, затем выберите форму (нажав на строка заголовка)
  2. Нажмите F4, чтобы открыть окно свойств,
  3. затем найдите свойство AutoScaleMode property
  4. Измените его с Font (по умолчанию) на Dpi.

Теперь перейдите в Program.cs (или в файл, где находится ваш метод Main) и измените его так:

namespace myApplication
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            // ***this line is added***
            if (Environment.OSVersion.Version.Major >= 6)
                SetProcessDPIAware();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }

        // ***also dllimport of that function***
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool SetProcessDPIAware();
    }
}

Сохраните и скомпилируйте. Теперь ваша форма снова должна выглядеть хрустящей.


источник:http://crsouza.com/2015/04/13/how-to-fix-blurry-windows-forms-windows-in-high-dpi-settings/

Ответ 5

В Windows Forms действительно сложно создавать приложения с поддержкой DPI. Вам нужно будет использовать макеты компоновки, которые будут правильно изменяться при изменении DPI (например, TableLayoutPanel или FlowLayoutPanel). Все элементы управления также нуждаются в изменении размера. Конфигурация этих контейнеров может быть проблемой.

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

Ответ 6

Из опыта:

  • не использовать информацию о DPI с формами окон, если не критические
  • с этой целью всегда устанавливаем свойство AutoScaleMode в None для всех форм и пользовательских элементов управления в вашем приложении.
  • Результат: тип интерфейса WYSIWYG при изменении настроек DPI

Ответ 7

  • Если вы хотите, чтобы ваше приложение WinForms являлось приложением DPI-Aware, в дополнение к хорошему ответу Trygve. Если у вас есть большой проект, вы можете автоматически масштабировать свои формы и их содержимое, вы можете сделать это, создав функцию ScaleByDPI:

Функция ScaleByDPI получит параметр Control, который обычно является формой, а не рекурсивно итерации через все вспомогательные элементы управления (if (control.HasChildren == true)), а также расположение и размеры шкалы от ваших элементов управления приложениями, а также размеры и размеры шрифты к настроенному ОС DPI. Вы можете попытаться реализовать его также для изображений, значков и графики.

Специальные примечания для функции ScaleByDPI:

а. Для всех элементов управления с размерами шрифта по умолчанию вам необходимо установить их Font.Size в 8.25.

б. Вы можете получить значения devicePixelRatioX и devicePixelRatioY (control.CreateGraphics().DpiX/96) и (control.CreateGraphics(). DpiY/96).

с. Вам понадобится масштаб Control.Size и Control.Location с помощью алгоритма, основанного на control.Dock и control.Anchor. Следует заметить, что control.Dock может иметь 1 из 6 возможных значений и что control.Anchor может иметь 1 из 16 возможных значений.

д. этот алгоритм будет иметь заданные значения для следующих переменных bool isDoSizeWidth, isDoSizeHeight, isDoLocationX, isDoLocationY, isDoRefactorSizeWidth, isDoRefactorSizeHeight, isDoRefactorLocationX, isDoRefactorLocationY, isDoClacLocationXBasedOnRight, isDoClacLocationYBasedOnBottom.

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

Дополнительная информация о выше (d.) переменные bool:

* Иногда группа элементов управления (может быть кнопками) должна быть помещена одна за другой на одну и ту же вертикальную линию, а их значение Якоря включает в себя правое, но не левое, или их нужно размещать один за другим на одной горизонтальной линии, а их значение Anchor включает Bottom, но не Top, в этом случае вам нужно перерасчитать элементы управления. Значения местоположения.

* В случае элементов управления, в которых Anchor содержит верхние и нижние и\или левые и правые, вам нужно будет изменить параметры элементов управления Size и Location.

Использование функции ScaleByDPI:

а. Добавьте следующую команду в конец любого конструктора формы: ScaleByDPI (this);

б. Также при динамическом добавлении какого-либо элемента управления к вызову формы ScaleByDPI ([ControlName]).

  1. Когда вы устанавливаете размер или местоположение любого элемента управления динамически после завершения конструктора, создайте и используйте одну из следующих функций, чтобы получить масштабированные значения размера или местоположения: ScaleByDPI_X\ScaleByDPI_Y\ScaleByDPI_Size\ScaleByDPI_Point

  2. Чтобы отметить ваше приложение как средство DPI, добавьте элемент dpiAware в манифест сборки приложения.

  3. Установите GraphicsUnit для всех Control.Font в System.Drawing.GraphicsUnit.Point

  4. В файлах *.Designer.cs всех контейнеров установите значение AutoScaleMode в System.Windows.Forms.AutoScaleMode.None

  5. в элементах управления, таких как ComboBox и TextBox, изменение Control.Size.Hieght не влияет. В этом случае изменение Control.Font.Size будет определять высоту управления.

  6. Если форма StartPosition имеет значение FormStartPosition.CenterScreen, вам нужно будет пересчитать местоположение окна.

Ответ 8

Поскольку форма заявки Winform может содержать элементы управления контентом и изображения, что позволяет изменять размер вашего ВАШЕГО окна, НЕ является решением, но если вы могли бы иметь одну форму в разрешении DPI, с правильно масштабированными изображениями... И это не хорошая идея, так как при увеличении размера экрана размер шрифта уменьшается.

При использовании другого разрешения DPI система заставляет вашу форму переопределять свой размер, местоположение и шрифт управления, НО НЕ ИЗОБРАЖЕНИЯ, решение заключается в изменении формы DPI во время выполнения, при загрузке, так что все возвращается к исходному размеру и местоположение.

Это возможное решение, которое я тестировал с помощью приложения для карточных игр, где у меня есть 80 кнопок изображений, TabControls и т.д.

В каждом событии form_Load формы добавьте этот фрагмент кода:

  Dim dpi As Graphics = Me.CreateGraphics
    Select Case dpi.DpiX
        Case 120
            '-- Do nothing if your app has been desigbned with 120 dpi
        Case Else
    '-- I use 125 AND NOT 120 because 120 is 25% more than 96
            Me.Font = New Font(Me.Font.FontFamily, Me.Font.Size * 125 / dpi.DpiX)
    End Select

Кроме того, быстрый трюк для тестирования различных разрешений на одном компьютере без перезапуска:

Из панели управления измените разрешение. Не перезапускайте! Вместо этого закройте сеанс и откройте новый с тем же пользователем.

Существует еще одна оговорка: если вы установите размер и положение управления во время выполнения, то вам следует применить тот же коэффициент DPI (например, 125/Dpi.Dpix) к новым координатам. Поэтому вам лучше настроить глобальную переменную DPIFactor из события application.startup.

И последнее, но не менее важное:

НЕ открывайте приложение в Visual Studio из другого разрешения, кроме оригинального, или ВСЕ ВАШИ УПРАВЛЕНИЯ будут перемещаться и изменять размер при открытии каждой формы, и нет пути назад...

Надеюсь, это поможет, счастливое программирование.