2017-06-14 100 views
5

Mam projektu WinForm, który ma już kilka lat i został retro wyposażony asynchronicznych Event-ładowarki:okno WinForms zmienia wymiarów po napotkaniu połączenia asynchronicznego

private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e) 

Wewnątrz tej metody jest wywołanie asynchroniczne:

var projectTemplate = await GetProjectTemplateFile(companyId, sourceLang, targetLang); 

Gdy program działa na ekranie o normalnej rozdzielczości, działa zgodnie z oczekiwaniami. Jednak po uruchomieniu na ekranie o wysokiej czułości, wymiary okna - jak również wszystkie kontrolki podrzędne - przeskakują do połowy rozmiaru zaraz po napotkaniu tego wewnętrznego połączenia asynchronicznego. To tak, jakby program nagle uruchomił się w trybie zgodności lub skalowanie zostało wyłączone.

Obecnie, w celu debugowania problemu, metoda GetProjectTemplateFile składa się po prostu z

private async Task<ProjectTemplateFile> GetProjectTemplateFile(long companyId, string sourceLanguage, string targetLanguage) 
{ 
    return null; 
} 

Nie ma znaczenia, czy GetProjectTemplateFile wykonuje operację asynchronicznej lub nie.

Jeśli skomentuję to połączenie asynchroniczne z GetProjectTemplateFile, program będzie działał zgodnie z oczekiwaniami bez żadnego przeskoku wymiarów, mimo że w przypadku zdarzenia CellClick są jeszcze inne wywołania asynchroniczne.

Próbowałem dołączyć .ConfigureAwait(true) do wywołania asynchronicznego, co nie ma znaczenia. Połączenia nie można też synchronicznie synchronizować z .GetAwaiter().GetResult().

Czy ktoś może wyjaśnić, dlaczego wymiary okna zmieniają się za pomocą tego konkretnego połączenia asynchronicznego i/lub jak temu zapobiec?

Aktualizacja
Zgodnie z prośbą, oto przykładowy kod, który wywołuje wytłumaczyć zachowanie. Nie dzieje się tutaj nic niezwykłego, ale mogę zapewnić, że ten kodeks powoduje wyjaśnione zachowanie.

private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e) 
{ 
    var result = await _templateInteraction.GetProjectTemplateFile(1, 
                    "en-US", 
                    "de-CH"); 
    return; 
} 

public class TemplateInteraction : ITemplateInteraction 
{ 
    public async Task<ProjectTemplateFile> GetProjectTemplateFile(long companyId, string sourceLanguage, string targetLanguage) 
    { 
     return null; 

     // elided code 
    } 

    // other methods 
} 

Niektóre inne informacje, które mogą być istotne:

  • FormBorderStyle okna jest "FixedToolWindow"
  • Okno jest podane wyraźne szerokość w sposobie uruchamiania
  • AutoSize = false
  • AutoSizeMode = Wzrost tylko
  • The Komputer którym jest rozwijany na nie posiada Windows 10 1703 (Stwórcy) aktualizację, która ma nowa logika skalowanie
  • Jeśli metoda GetprojectTemplateFile jest nie asynchroniczny, tj ma podpis public ProjectTemplateFile GetProjecttemplateFile(...) wtedy nie ma problemu. Ten problem wydaje się istnieć tylko wtedy, gdy wywołanie metody jest asynchroniczne - nawet jeśli wywołam blokowanie.

UPDATE 2:
znalazłem konkretnej pozycji (-y) kodu, które powodują ten problem:

MessageBox.Show(...); 

Wewnętrzna połączenia asynchronicznego, GetProjectTemplateFile, wywołuje API i sprawdza odpowiedź:

var responseMessage = await client.GetAsync(uri); 
if (!responseMessage.IsSuccessStatusCode) 
{ 
    MessageBox.Show(...); 
    return null; 
} 

Jeśli komentarz out połączenia MessageBox.Show(...) wtedy wszystko jest normalne, bez skalowania proble ms, bez skoków w wymiarach.

Ale problem występuje, gdy wywołanie MessageBox.Show(...) jest na miejscu.

Co więcej, API odpowiada 200 (OK), więc kod MessageBox nie jest nawet używany. Zgaduję, że kompilator JIT widzi to jako możliwość, więc ... ponownie renderuje formularz?

Co ważne, ten kod nie znajduje się w zadeklarowanym kodzie formularza, jest w klasie, której formularz jest podany w swoim konstruktorze.

+2

Proszę zaksięgować kompletną i minimalną próbkę, która odtworzy zachowanie, które widzisz. Uwzględnij wszystko, co może być istotne, jakiego framework używasz, os itd. Jeśli 'asynchroniczne' jest naprawdę losowo zmieniające rozmiar formularza, to byłoby to interesujące dla ciebie, aby dowiedzieć się co najmniej. – JSteward

+0

Dziękuję za aktualizację, ale nie mogę odtworzyć tego, co widzisz. Oto kod, którego używam: [dotnet Fiddle] (https://dotnetfiddle.net/6Sn4cj) – JSteward

+0

Nawet z .NET 4.6? – awj

Odpowiedz

3

Domyślam się, że korzystasz z MessageBox z obszaru nazw System.Windows, do którego odwołuje się PresentationFramework.dll, zamiast obszaru nazw System.Windows.Forms?

// Causes DPI scaling problems: 
System.Windows.MessageBox.Show() // loads WPF version from PresentationFramework.dll 

// no DPI scaling issues: 
System.Windows.Forms.MessageBox.Show() // uses standard winforms messagebox 

Spróbuj użyć standardowego MessageBox.

Znalazłem, że gdy jakikolwiek plik DLL kierowany do WPF zostanie załadowany do pamięci, autoskalowanie DPI zostanie zresetowane. Określona funkcja nie musi nawet być wywoływana - pliki dll są ładowane natychmiast po wywołaniu funkcji nadrzędnej.

Miałem ten sam problem po prostu mając System.Windows.Input.Keyboard.IsKeyToggled(), który załadował PresentationCore.dll. Pomyślałem, że zwariowałem również ...

+0

Tak, masz rację. Przepraszam, znalazłem to kilka miesięcy temu, ale nigdy nie aktualizowałem OP. – awj