Создание приложения с поддержкой DPI - PullRequest
62 голосов
/ 02 ноября 2010

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

У кого-нибудь есть идея?

Ответы [ 8 ]

102 голосов
/ 02 ноября 2010

РЕДАКТИРОВАТЬ: Начиная с .NET 4.7, в формах Windows улучшена поддержка высокого разрешения. Подробнее об этом можно узнать на сайте docs.microsoft.com. Это работает только для Win 10 Creators Update и более поздних версий, поэтому, возможно, будет нецелесообразно использовать его в зависимости от базы пользователей.


Сложно, но не невозможно. Конечно, лучше всего перейти на WPF, но это может оказаться невозможным.

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

  • Всегда редактируйте / создавайте свои приложения по умолчанию 96 DPI (100%). Если вы проектируете с разрешением 120 точек на дюйм (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 панели с другой размер шрифта, чем он содержит форму, гарантированно создает проблемы. Это может сработать, если форма и все контейнеры в ней используют одинаковый размер шрифта, но я не пробовал.
  • Используйте другую машину или установку виртуальных окон (VMware, Virtual PC, VirtualBox) с более высоким значением DPI для немедленной проверки проекта. Просто запустите скомпилированный файл .exe из папки / bin / Debug на компьютере DEV.

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

РЕДАКТИРОВАТЬ: Эта ссылка содержит много полезной информации, особенно если вы решите использовать AutoScaleMode.DPI: ссылка на связанный вопрос stackoverflow

14 голосов
/ 13 апреля 2012

Я наконец нашел решение проблемы как ориентации экрана, так и обработки 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;

Итак, код для этих 2 методов будет выглядеть так:

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

Работает для меня как шарм.

8 голосов
/ 29 августа 2018

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

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

Теперь перейдите к 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/

4 голосов
/ 30 марта 2017

Похоже, это проблема с Windows. Снятие этих двух строк исправило все.

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

Вот где я получил решение:

3 голосов
/ 02 ноября 2010

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

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

2 голосов
/ 02 ноября 2010

Из опыта:

  • не используйте DPI DPI с окнами, если не критично
  • для этого всегда устанавливайте для свойства AutoScaleMode значение None во всех формах и пользовательских элементах управления в вашем приложении
  • Результат: WYSIWYG-тип интерфейса при изменении настроек DPI
1 голос
/ 09 марта 2015
  1. Если вы хотите, чтобы ваше приложение WinForms было приложением с поддержкой DPI, в дополнение к хорошему ответу Trygve, если у вас большой проект, вы можете автоматически масштабировать свои формы и их содержимое, это можно сделать, создав функцию ScaleByDPI:

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

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

а. Для всех элементов управления с размерами шрифтов по умолчанию вам нужно установить для них размер шрифта 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: isDoizeWidth, isDoSizeHeight, isDoLocationX, isDoLocationY, isDoRefactorSizeWidth, isDoRefactorSizeHeight, isDoRefactorLocationX, isDoRefactorLocationY, isDoClacLocationXBasedOlagLocOgn0LocOnBLO5Ob_Ogn_Location_Loc0.

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

Более подробная информация о вышеуказанных (d.) Переменных bool:

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

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

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

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

б. Также при динамическом добавлении любого элемента управления в вызов Form для 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, вам потребуется пересчитать местоположение окна.

0 голосов
/ 30 декабря 2010

Поскольку форма приложения 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 с разрешением, отличным от исходного,или ВСЕ ВАШИ ОРГАНЫ УПРАВЛЕНИЯ будут перемещаться и изменять размер при открытии каждой формы, и пути назад нет ...

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

...