WinForms窗口在遇到异步调用时改变尺寸

7

我有一个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编译器认为这是可能的,所以......它重新渲染了表格?
同时,重要的是,这段代码不在表单的代码后台中,而是在一个类中,在构造函数中给表单一个实例。

2
请发布一个完整且最小的示例,以重现您正在看到的行为。包括任何可能相关的内容,例如您正在使用的框架、操作系统等等。如果“async”确实随机更改表单的大小,那么进行调查至少会令人感兴趣。 - JSteward
谢谢您的更新,但我无法重现您所看到的情况。这是我正在使用的代码:dotnet Fiddle - JSteward
即使使用.NET 4.6版本也是这样吗? - awj
@awj, 能否上传一个最小但完整的WinForms项目来展示这种行为?我有Win10 v1703和高DPI屏幕,我想尝试复现。 - noseratio - open to work
把你的项目定位到 .NET 4.7, 并在 Win10 v1703 (创作者更新)上运行它以获得WinForms的DPI改进。 - magicandre1981
显示剩余3条评论
1个回答

6
我猜你正在使用来自PresentationFramework.dll的System.Windows命名空间中的MessageBox,而不是System.Windows.Forms命名空间中的MessageBox?
// 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,出现了同样的问题。一度以为自己疯了...


是的,你说得对。抱歉,我几个月前就发现了这个问题,但从未更新过原始帖子。 - awj
如果您需要使用PresentationCore或PresentationFramework,但这是一个问题,您可以将以下内容添加到AssemblyInfo.cs文件中,以避免从这些库加载类型时进行重置:[assembly: System.Windows.Media.DisableDpiAwareness] - Scott P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接