WinForms window changes dimensions when it encounters an async call

不打扰是莪最后的温柔 提交于 2021-02-07 07:01:23

问题


I have a WinForms project which is several years old and has been retro-fitted with async event-handlers:

private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e)

Inside this method is an async call:

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

When the program runs on a normal resolution screen, it runs as expected. However, when run on a high-DPI screen the window's dimensions - as well as those of all child controls - jump to half-size as soon as it encounters that inner async call. It's as if the program is suddenly run in a compatibility mode or the scaling has been disabled.

Currently, in an effort to debug the problem, the GetProjectTemplateFile method consists simply of

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

It makes no difference whether GetProjectTemplateFile performs an async operation or not.

If I comment-out that async call to GetProjectTemplateFile then the program runs as expected without any jump in dimensions, even though there are still other async calls made in the CellClick event.

I've tried appending .ConfigureAwait(true) to the async call, which makes no difference. Nor does running the call synchronously with .GetAwaiter().GetResult().

Can anyone explain why the window's dimensions are changing with this particular async call, and/or how to prevent this from happening?

Update
As per a request, here is a code sample which elicits the explained behaviour. There's nothing unusual happening here that I can see but I assure you, this very code is causing the explained behaviour.

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 which might be relevant:

  • The FormBorderStyle of the window is "FixedToolWindow"
  • The window is given an explicit width in a startup method
  • AutoSize = False
  • AutoSizeMode = GrowOnly
  • The computer which it's being developed on does not have the Windows 10 1703 (Creator's) update which has new scaling logic
  • If the GetprojectTemplateFile method is not async, i.e. has signature public ProjectTemplateFile GetProjecttemplateFile(...) then there is no problem. This problem appears to exist only when the method call is async - even if I make it a blocking call.

UPDATE 2:
I've found the specific line(s) of code which cause this problem:

MessageBox.Show(...);

The inner async call, GetProjectTemplateFile, calls an API and then checks the response:

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

If I comment-out the MessageBox.Show(...) call then everything is normal, no scaling problems, no jump in dimensions.

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

Furthermore, the API responds with a 200 (OK) so the MessageBox code isn't even being used. My guess is that the JIT compiler sees it as a possibility so... it re-renders the form?

Also, importantly, this code is not in the form's code-behind, it's in a class which the form is given an instance of in its constructor.


回答1:


I guess you are using MessageBox from System.Windows namespace, referenced from PresentationFramework.dll, instead of 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

So try using the standard MessageBox instead.

I've found that whenever any WPF-targeted dll gets loaded into memory, the DPI autoscaling gets reset. The specific function doesn't even need to be actually called - the dll's are loaded as soon as the parent function is called.

I had same problem by just having System.Windows.Input.Keyboard.IsKeyToggled(), which loaded PresentationCore.dll. Thought I was going mad as well...



来源:https://stackoverflow.com/questions/44547762/winforms-window-changes-dimensions-when-it-encounters-an-async-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!