Jak skonfigurować aplikację, aby działała poprawnie na komputerze z ustawieniem wysokiego DPI (np. 150%)?

103

Stworzyłem prostą aplikację Winforms w C #. Kiedy uruchamiam aplikację na maszynie z ustawieniami wysokiego DPI (np. 150%), aplikacja zostaje przeskalowana. Na razie w porządku! Ale zamiast renderowania czcionek o większym rozmiarze, wszystkie teksty również są po prostu powiększane. To oczywiście prowadzi do bardzo rozmytego tekstu (na wszystkich elementach sterujących, takich jak przyciski itp.).

Czy system Windows nie powinien zadbać o prawidłowe renderowanie tekstów? Na przykład pasek tytułu mojej aplikacji jest wyraźny i wyraźny.

Boris
źródło

Odpowiedzi:

132

Po przekroczeniu 100% (lub 125% przy zaznaczonym polu wyboru „Skalowanie DPI w stylu XP”) system Windows domyślnie przejmuje skalowanie interfejsu użytkownika. Dzieje się tak, ponieważ aplikacja renderuje dane wyjściowe do mapy bitowej i rysuje ją na ekranie. Przeskalowanie tej mapy bitowej sprawia, że ​​tekst nieuchronnie wygląda na rozmyty. Funkcja zwana „wirtualizacją DPI” pozwala na używanie starych programów na monitorach o wysokiej rozdzielczości.

Musisz wyraźnie poinformować go, że możesz obsłużyć wyższe ustawienia DPI, dodając <dpiAware>element do manifestu. Strona MSDN jest tutaj, ale nie jest kompletna, ponieważ pomija ustawienia UAC. Projekt + Dodaj nowy element, wybierz „Plik manifestu aplikacji”. Edytuj tekst manifestu lub skopiuj / wklej to:

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <requestedExecutionLevel level="asInvoker" uiAccess="false" />
            </requestedPrivileges>
        </security>
    </trustInfo>
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>true</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>

Możesz również pinvoke SetProcessDPIAware () w swojej metodzie Main (), co jest konieczne na przykład w przypadku wdrażania z ClickOnce:

    [STAThread]
    static void Main() {
        if (Environment.OSVersion.Version.Major >= 6) SetProcessDPIAware();
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());             // Edit as needed
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool SetProcessDPIAware();

UPDATE, ta powszechna potrzeba jest wreszcie trochę łatwiejsza, jeśli używasz VS2015 Update 1 lub nowszej. Dodany manifest ma już odpowiednią dyrektywę, po prostu usuń komentarze.


Słowo kluczowe do wyszukiwania, abym mógł znaleźć ten post z powrotem: dpiAware

Hans Passant
źródło
Dzięki, Twoje rozwiązanie działa dobrze. Jedynym problemem jest to, że wszystkie obrazy mają teraz swoje oryginalne rozmiary. Myślę, że będę musiał znaleźć sposób na dodanie dodatkowych ikon „siatkówki” do mojego GUI ...
Boris,
@HansPassant Mam ten sam problem z rozmytymi czcionkami i po zastosowaniu tego rozwiązania moje kontrolki nie skalują się i nie pasują. Jak sprawić, by oba działały?
gajo357
2
Dla każdego, kto bada to szaleństwo Win8, SetProcessDPIAwarejest przestarzałe, a także nie działa poprawnie (przynajmniej nie w Win8.1), powodując nieprzewidywalne skalowanie na różnych kontrolkach. Zdecydowanie zalecam zamiast tego użycie metody manifestu.
Jason Williams
5
Hmya, Windows 8.1 nabył DPI na monitor. Nie wiedziałem, że tego potrzebuję.
Hans Passant
1
Jeśli zamierzasz użyć ClickOnce do wdrożenia, nie możesz użyć opcji dpiAware w manifeście, zamiast tego użyj SetProcessDPIAware ().
Matías
17

Aplikacje można tworzyć w dwóch różnych trybach.

Pierwszym z nich jest zadeklarowanie naszej aplikacji jako nieobsługującej DPI (brak deklarowania niczego będzie domyślnie). W tym przypadku system operacyjny wyrenderuje naszą aplikację w oczekiwanym 96 DPI, a następnie wykona skalowanie bitmapy, o którym mówiliśmy wcześniej. Rezultatem będzie rozmyta aplikacja, ale z prawidłowym układem.

Drugą opcją jest zadeklarowanie aplikacji jako obsługującej DPI. W takim przypadku system operacyjny nie przeprowadzi żadnego skalowania i pozwoli aplikacji renderować zgodnie z oryginalnym DPI ekranu. W przypadku środowiska DPI na monitor, Twoja aplikacja będzie renderowana z najwyższym DPI ze wszystkich ekranów, a następnie ta mapa bitowa zostanie przeskalowana do odpowiedniego rozmiaru dla każdego monitora. Zmniejszenie rozdzielczości zapewnia lepsze wrażenia wizualne niż skalowanie w górę, ale nadal możesz zauważyć pewne nieostrości.

Jeśli chcesz tego uniknąć, musisz zadeklarować aplikację jako rozpoznającą DPI monitora. Następnie musisz wykryć, kiedy aplikacja jest przeciągnięta na różne monitory i renderować zgodnie z DPI bieżącego.

Deklarowanie świadomości DPI odbywa się w pliku manifestu.

zapoznaj się z poniższym stosem linków

shanthi_karthika
źródło
co się stanie, jeśli użytkownicy mają przywrócony rozmiar okna i przenoszą je tak, aby jego części były na różnych monitorach? czy musimy renderować wszystko dwukrotnie i używać granic monitora jako obwiedni? ile z tego pokrywają biblioteki winforms?
Cee McSharpface,
4

Korzystając z .NET Framework 4.7 i Windows 10 Creators Update (1703) lub nowszej, należy wykonać następujące czynności, aby skonfigurować obsługę wysokiej rozdzielczości DPI dla aplikacji Windows Form:

Zadeklaruj zgodność z systemem Windows 10.

Aby to zrobić, dodaj do swojego manifestpliku:

<compatibility xmlns="urn:schemas-microsoft.com:compatibility.v1">
  <application>
    <!-- Windows 10 compatibility -->
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  </application>
</compatibility>

Włącz rozpoznawanie DPI dla każdego monitora w app.configpliku.

Windows Forms wprowadza nowy element System.Windows.Forms.ApplicationConfigurationSection do obsługi nowych funkcji i dostosowań dodanych począwszy od .NET Framework 4.7. Aby skorzystać z nowych funkcji obsługujących wysokie DPI, dodaj następujące elementy do pliku konfiguracyjnego aplikacji.

<System.Windows.Forms.ApplicationConfigurationSection>
  <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

Ważny

W poprzednich wersjach programu .NET Framework do dodania obsługi wysokiej rozdzielczości DPI był używany manifest. To podejście nie jest już zalecane, ponieważ zastępuje ustawienia zdefiniowane w pliku app.config.

Wywołaj statyczną metodę EnableVisualStyles.

Powinno to być pierwsze wywołanie metody w punkcie wejścia aplikacji. Na przykład:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());   
}

Zaletą tego jest obsługa dynamicznych scenariuszy DPI, w których użytkownik zmienia DPI lub współczynnik skali po uruchomieniu aplikacji Windows Forms.

Źródło: obsługa wysokiej rozdzielczości DPI w Windows Forms

Wollmich
źródło
3

Żadna z tych sugestii nie zadziałała, ale coś się wydarzyło po usunięciu Form.Font = new... z Form.Design.cs, formularz zaczął poprawnie przeskalowywać, działa, jeśli Font jest zdefiniowany w konstruktorze lub wcale. Czemu? Ktoś inny może być w stanie wyjaśnić, po prostu mogę opowiedzieć o zmianach, które wprowadziłem, i zajęło mi kilka minut, aby zorientować się, że to była podstawowa przyczyna dla formularza, nad którym pracowałem. Mam nadzieję, że to pomoże.

Vasco Bonilla
źródło
2

Od przynajmniej Visual Studio 2017 wystarczy dodać plik manifestu i odkomentować tę sekcję:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </windowsSettings>
</application>
Aranxo
źródło