O problemach życia ze sprzętem z wysokim DPI pisał Scott Hanselman już jakiś czas temu (Living a High-DPI desktop lifestyle can be painful). Ja, dotychczas nie miałem z tym problemów jako, że nie posiadałem takowego. Od jakiegoś czasu pracuję jednak na MacBook Pro Retina, który z rozdzielczością 2560×1600 wystawia mnie na wszystkie te problemy, o których w swoim poście pisał Scott. Dziwne to było na początku, ale po chwili przyzwyczajenia jakoś one szczególnie mi nie przeszkadzają. Ot kolejne problemiki codziennej pracy. To prawda są one irytujące, szczególnie w przypadku niektórych narzędzi. Okno wyboru pliku w SQL Management Studio wygląda tak:

sql-management
Jakby ktoś pytał – to jest w pełni załadowane okno. Tak się wyświetla :). Dopiero po zmianie rozmiaru okna pojawiają się kontrolki wyboru pliku. Da się przeżyć, choć trzeba być świadomym takich rzeczy bo inaczej co drugie narzędzie będzie nieużywalne. Dla mnie największy problem jest z narzędziami nagrywającymi ekran. Z tego co zaznaczę do nagrania na filmie będzie 1/4 całości. 1/4 i tyle. Lewy górny fragment. Camtasia 6, której dotychczas używałem ma taki feler (ok wiem, stara wersja i najnowsza działa ok). Na usprawiedliwienie Jing, tego samego producenta, działa dobrze, ale z limitem 5 min, pozwala tylko na nagrywanie krótkich filmików omawiających istotne sprawy w kodzie w ramach review, które w firmie od pewnego czasu wprowadziliśmy. Ostatnio znalazłem przydatne narzędzie – Cropper – umożliwiające zrobienie zrzutu ekranu (bądź fragmentu) i zapisanie go w jednym z popularnych formatów – jpg, png czy gif. Przydatne gdy potrzebujemy screen’a na blog’a czy do maila. Przydatne, ale oczywiście nie na moim sprzęcie. U mnie screenshot zajmował będzie tylko 1/4 faktycznego zaznaczonego fragmentu. Poniżej jak to wygląda na ekranie oraz wynikowy rezultat:

compare

Lewa strona – to co chciałem uchwycić; prawa – rezultat

Tak, to nie pomyłka, tylko tyle widać :/
Jako, że program jest z kodem źródłowym postanowiłem zerknąć do niego i zobaczyć czy prostym sposobem nie da się tego naprawić. Pierwsze zdziwienie przyszło gdy podejrzałem wartość DPI w aplikacji – 96. Hmm..czyżbym dał się oszukać co do Retiny?

check-dpi

96 dpi na Retina? Coś tu nie gra…

Szybka aplikacja w WinForms potwierdziła najgorsze – 96 jak nic! Steve mnie oszukał! Co ciekawe taka sama aplikacja w WPF zwraca prawidłowe wartości – 192. Ktoś tu mnie robi w balona. Okazuje się, że to WinForms.

Domyślnie aplikacje te nie są tworzone jako DPI-Aware. Zawsze zwracają wartość 96 nawet gdy ta jest wyższa. Aby zwracały prawidłową wartość musimy powiedzieć systemowi, że nasz proces będzie DPI-Aware. Co ciekawe nie ma na to metody w .NET (albo jej nie znalazłem) – musimy skorzystać z WinAPI. Funkcja nazywa się SetProcessDPIAware i znajduje się w bibliotece user32.dll.

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetProcessDPIAware();
check-dpi2

Prawidłowe wartości DPI po wywołaniu metody SetProcessDPIAware.

Po takiej operacji nasza aplikacja będzie zachowywać się prawidłowo tj. będzie zwracać poprawne wartości DPI. Mając taką wiedzę udałem się do kodu Cropper’a chcąc sprawdzić jaki efekt będzie miało na niego wywołanie ww. funkcji. Jak się okazało skutek był rewelacyjny – Cropper zaczął działać tak jak powinien. Zrzut ekranu zawierał cały zaznaczony obszar. Zwycięstwo? W tym wypadku możliwe, że tak choć nie całkowite. Nadal są widoczne pozostałe artefakty takie jak rozmyte czcionki czy ucięte fragmenty tekstu. Temat HighDPI nie jest tak banalny i nie sprowadza się do wywołania jednej metody. Trzeba zmienić sposób projektowania aplikacji – pozycjonowanie za pomocą Left i Top w pixelach w WinForms nie pomaga a pewnie dlatego WPF domyślnie tworzy już aplikacje DPI-Aware. I na koniec mały smaczek screenshot kodu Cropper’a zrobiony poprawioną wersją Cropper’a 🙂

CropperCapture[11]

Nice 🙂 Dla chcących pogłębić swoją wiedzę z tematu – link. Na dole jest PDF do pobrania.

passionate dev, blogger, occasionally speaker, one of the leaders of Wroc.NET – local .NET community.