WinForms窗口在遇到异步调用时会更改维度

awj*_*awj 5 c# scaling highdpi winforms async-await

我有一个几年前的WinForms项目,并且已经改装了异步事件处理程序:

private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e)
Run Code Online (Sandbox Code Playgroud)

在这个方法里面是一个异步调用:

var projectTemplate = await GetProjectTemplateFile(companyId, sourceLang, targetLang);
Run Code Online (Sandbox Code Playgroud)

当程序在普通分辨率屏幕上运行时,它会按预期运行.但是,当在高DPI屏幕上运行时,窗口的尺寸 - 以及所有子控件的尺寸 - 一旦遇到内部异步调用,就会跳到一半大小.就像程序突然以兼容模式运行或者已禁用缩放一样.

目前,为了调试问题,该GetProjectTemplateFile方法仅包括

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

无论是否GetProjectTemplateFile执行异步操作都没有区别.

如果我注释掉异步调用,GetProjectTemplateFile则程序按预期运行而不会有任何跳转,即使在CellClick事件中仍有其他异步调用.

我已尝试附加.ConfigureAwait(true)到异步调用,这没有任何区别.也不会同步运行呼叫.GetAwaiter().GetResult().

任何人都可以解释为什么窗口的尺寸正在改变这个特定的异步调用,和/或如何防止这种情况发生?

更新
根据请求,这是一个代码示例,它引出了解释的行为.这里没有什么不寻常的事情我可以看到,但我向你保证,这个代码正在引起解释的行为.

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
}
Run Code Online (Sandbox Code Playgroud)

其他一些可能相关的信息:

  • FormBorderStyle窗口是"FixedToolWindow"
  • 在启动方法中,窗口具有显式宽度
  • AutoSize =假
  • AutoSizeMode = GrowOnly
  • 它正在开发的计算机没有具有新扩展逻辑的Windows 10 1703(创建者)更新
  • 如果GetprojectTemplateFile方法不是异步,具有签名public ProjectTemplateFile GetProjecttemplateFile(...)则没有问题.仅当方法调用是异步时,此问题似乎才存在 - 即使我将其设置为阻塞调用.

更新2:
我找到了导致此问题的特定代码行:

MessageBox.Show(...);
Run Code Online (Sandbox Code Playgroud)

内部异步调用,GetProjectTemplateFile调用API然后检查响应:

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

如果我注释掉MessageBox.Show(...)调用,则一切正常,没有缩放问题,没有尺寸跳跃.

但是当MessageBox.Show(...)呼叫到位时会出现问题.

此外,API以200(OK)响应,因此甚至没有使用MessageBox代码.我的猜测是JIT编译器认为它是一种可能性......它重新呈现表单?

此外,重要的是,这个代码不是代码隐藏的形式,它在一个类中,表单在其构造函数中被赋予了一个实例.

jtm*_*mnt 5

我猜您正在使用 System.Windows 命名空间中的 MessageBox,从 PresentationFramework.dll 引用,而不是 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
Run Code Online (Sandbox Code Playgroud)

因此,请尝试改用标准 MessageBox。

我发现每当任何以 WPF 为目标的 dll 加载到内存中时,DPI 自动缩放都会重置。甚至不需要实际调用特定的函数——只要调用父函数,就会加载 dll。

我只使用 System.Windows.Input.Keyboard.IsKeyToggled() 就遇到了同样的问题,它加载了 PresentationCore.dll。还以为我要疯了……