WinForms Window Resizes When An Asynchronous Call Is Detected

I have a WinForms project that is already several years old and it was a retro-asynchronous event handler:

private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e) 

Inside this method, an asynchronous call is used:

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

When the program starts up on the screen with a normal resolution, it works as expected. However, when launched on a high DPI screen, the window sizes, as well as the sizes of all child controls, go to half the size as soon as it encounters this internal asynchronous call. It is as if the program suddenly started in compatibility mode or the scaling was turned off.

Currently, to debug a problem, the GetProjectTemplateFile method consists simply of

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

It does not matter whether GetProjectTemplateFile performs an asynchronous operation or not.

If I comment that the asynchronous call to GetProjectTemplateFile , then the program works as expected without any change in size, even if there are still other asynchronous calls made in the CellClick event.

I tried adding .ConfigureAwait(true) to an asynchronous call, which doesn't matter. Also, a synchronous call is not made with .GetAwaiter().GetResult() .

Can someone explain why window sizes are resized using this particular asynchronous call and / or how to prevent this?

Update
As per the request, here is a sample code that invokes the explained behavior. There is nothing unusual here that I can see, but I assure you that this code causes an explanation of the behavior.

 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 } 

Some other information that may make a difference:

  • FormBorderStyle window - "FixedToolWindow"
  • Window gets explicit width in start method
  • AutoSize = False
  • AutoSizeMode = GrowOnly
  • The computer on which it is being developed does not have Windows 10 1703 (Creator's), which has a new scaling logic
  • If the GetProjectTemplateFile method is not asynchronous, that is, it has the signature public ProjectTemplateFile GetProjecttemplateFile(...) , then there is no problem. This problem only exists when the method call is asynchronous, even if I make it a blocking call.

UPDATE 2:
I found specific lines of code that cause this problem:

 MessageBox.Show(...); 

An internal asynchronous call to GetProjectTemplateFile calls the API and then checks the response:

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

If I comment on the call to MessageBox.Show(...) , then everything is fine, without scaling problems, without resizing.

But the problem occurs when the call to MessageBox.Show(...) is in place.

In addition, the API responds with 200 (OK), so the MessageBox code is not even used. I assume the JIT compiler sees this as an opportunity so ... does it re-render the form?

In addition, importantly, this code is not in the code-behind form, it is in the class to which the form is assigned an instance in its constructor.

+5
source share
1 answer

I assume that you are using a MessageBox from the System.Windows namespace referenced from PresentationFramework.dll, instead of the System.Windows.Forms namespace?

 // 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 

Try using the standard MessageBox instead.

I found that whenever any target DLL is loaded into memory, DPI auto-scaling gets reset. You don’t even need to call a specific function - the DLL loads immediately after the parent function is called.

I had the same problem, just having System.Windows.Input.Keyboard.IsKeyToggled () that loaded PresentationCore.dll. Thought I was losing my mind too ...

+3
source

Source: https://habr.com/ru/post/1268847/


All Articles