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

时间:2017-06-14 14:35:26

标签: c# winforms async-await scaling highdpi

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

private async void dgvNewOrders_CellClick(object sender, DataGridViewCellEventArgs e)

此方法内部是异步调用:

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

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

目前,为了调试问题,GetProjectTemplateFile方法只包含

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

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
}

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

  • 窗口的FormBorderStyle是“FixedToolWindow”
  • 在启动方法中为窗口指定了显式宽度
  • AutoSize = False
  • AutoSizeMode = GrowOnly
  • 正在开发的计算机没有Windows 10 1703 (创作者)更新具有新的缩放逻辑
  • 如果GetprojectTemplateFile方法异步,即。具有签名public ProjectTemplateFile GetProjecttemplateFile(...),那么就没有问题。仅当方法调用是异步时才会出现此问题 - 即使我将其设置为阻塞调用。

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

MessageBox.Show(...);

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

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

如果我对MessageBox.Show(...)电话进行评论,那么一切正常,没有缩放问题,尺寸没有跳跃。

但问题发生在MessageBox.Show(...)电话就位时。

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

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

1 个答案:

答案 0 :(得分:3)

我猜你使用的是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

因此请尝试使用标准的MessageBox。

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

我只有System.Windows.Input.Keyboard.IsKeyToggled()加载了PresentationCore.dll,我遇到了同样的问题。以为我也疯了......