6

在 WinForms MDI 应用程序中托管 WPF 用户控件时,当您有多个相互重叠的表单时会出现绘图问题,从而导致非常明显的视觉伪影。在将一个子窗体拖到另一个也承载 WPF 内容的子窗体上之后,或者通过在拖动子窗体时允许主 MDI 父窗体剪裁子窗体的边缘后,这些工件大部分可见。子窗体的拖放完成后,工件通常会保留下来,但我发现将焦点设置到不同的应用程序窗口,然后重新聚焦到我的应用程序窗口,它被重绘并且一切都很好,直到孩子表格再次移动。请参阅下图,它演示了该问题。

描述的工件的屏幕截图

微软的人坚持认为 WinForms MDI 已经是 MDI 的充分解决方案,不需要在 WPF 中重新发明,尽管我发现很难相信他们尝试以这种方式创建 WPF 应用程序,因为存在明显的缺点。

更新:我遗漏的一些额外注意事项是,如果我在不设置 MdiParent 的情况下创建这些表单,它们将被创建为常规表单,并且不会发生此问题。这个问题似乎是 WinForms MDI 方案所独有的。此外,我目前在 Windows 7 Enterprise 上运行,我知道 Windows XP 上的结果可能完全不同,但我无法对此进行测试。

更新:我发现了一些关于这个问题的其他相关资源,我认为我应该分享。

4

4 回答 4

9

似乎另一种解决方法是恢复到软件渲染,而不是利用硬件加速。这是Marco Zhou在 MSDN 论坛上的建议。

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        this.Loaded += delegate
        {
            var source = PresentationSource.FromVisual(this);
            var hwndTarget = source.CompositionTarget as HwndTarget;
            if (hwndTarget != null)
            {
                hwndTarget.RenderMode = RenderMode.SoftwareOnly;
            }
        };
    }
}

我已经对此进行了测试,该解决方案似乎运行良好,到目前为止,这是我在 FoxPro 互操作方案中发现的唯一解决方案,该方案与我最初发布的 WinForms 方案非常相似。现在,我计划在我的 WinForms 项目的 MDI 父解决方案上使用我原来的 Refresh,但是对于我的其他本机互操作应用程序,例如当我的 WPF 控件托管在 Visual FoxPro 中时,我将使用这个解决方案。当然,除非为任何一种情况找到更优雅的解决方案。

另外值得注意的是,据我所知,软件渲染是 XP 系统上的唯一选择,通常 Visual FoxPro nore WinForms 通常利用与原生 WPF 应用程序在 Vista OS 及更高版本上执行的相同类型的硬件加速。因此,当您必须处理互操作时,使用此选项可能并不像听起来那么糟糕。目前我不知道使用此解决方案时有任何相关的副作用,但如果有任何副作用,则必须认真考虑。

于 2010-04-07T16:29:22.103 回答
2

检查视频驱动程序并尝试禁用硬件加速。大多数伪影是由驱动程序错误、显卡故障或完成刷新的时间不足引起的。

第一个故障排除步骤:更新视频驱动程序。很明显,我知道。

我有类似的问题,检查我的视频卡设置(NVidia 控制面板)显示全局设置设置非常高,导致刷新间隔较长,如果花费太长时间可能会中止。将我的设置恢复为默认值可以解决大部分问题。但我也运行大量使用 GPU 的散列程序,所以这可能是我的剩余工件问题的原因,现在很少见,而且大多在 Visual Studio 中显示其丑陋的面貌。

我遇到的另一个故障排除步骤是禁用 WPF 的硬件加速,这可以在“HKEY_CURRENT_USER/SOFTWARE/Microsoft/Avalon.Graphics”中完成,或者应用程序可以做到这一点,但这仅用于故障排除;永远不要在应用程序中设置这些,因为它将禁用所有 WPF 应用程序。我没有这个注册表设置,也没有添加它,所以我不确定它是否成功,但很多人说这解决了他们的问题。另请注意,某些应用程序有此选项可用,如果可用,请尝试禁用它。

另一个故障排除步骤是确保视频卡是适合渲染的层级。任何支持 DX9 或更高版本的卡都应该足够了,但涉及到其他因素(就像我的情况一样),所以仅仅因为它在列表中并不意味着它足以满足您的目的。

最后,您可以使用 Visual Profiler(Windows SDK 的一部分)和其他工具来帮助更准确地确定 WPF 在图形能力方面缺乏性能的情况。

渲染层级注释和 WPF 性能信息 --> http://msdn.microsoft.com/en-us/library/vstudio/ms742196(v=vs.90).aspx

希望这可以帮助某人。

——瑞安·斯特拉斯堡

于 2013-01-17T18:42:15.087 回答
2

好吧,我可能已经找到了解决方案,尽管感觉有点像 hack。看来,如果您在移动子 MDI 窗体时调用 MDI 父级上的 Refresh 方法,那么所提到的工件就会消失。拖动窗口时,视觉上看起来有点紧张,但它似乎比我在原始帖子中展示的示例更容易接受。

private void Form1_Move(object sender, EventArgs e)
{
    this.ParentForm.Refresh();

    System.Diagnostics.Debug.WriteLine(string.Format("Form Moved to: ({0},{1})", this.Left, this.Top));
}

我已经尝试了许多相同的组合,例如仅刷新通过调​​用诸如Update()Invalidate()Refresh()等方法移动的子窗口,并且我在 MDI 父级上尝试了这些相同的方法以及我托管的 WPF 控件上的Dispatcher.Invoke(DispatcherPriority.Render, ...)InvalidateVisual()但这些其他方法都不能用于专门在 MDI 父级上调用Refresh() 。

我意识到这可能不是最佳解决方案,因为每次子窗口移动几个像素时我都会强制整个主应用程序窗口刷新,但就目前而言,这是我发现可行的唯一合理解决方案。如果其他人对此有任何替代解决方案或任何改进,我将很乐意接受您的回答。

于 2010-04-07T15:50:10.840 回答
0

您的用户控件或窗口加载事件;

this.WindowState = System.Windows.WindowState.Minimized;

this.WindowState = System.Windows.WindowState.Normal;

这似乎是不好的解决方案。无需将头撞到墙上。

土耳其谚语说:最好的代码是代码正在运行 :)

于 2015-08-25T15:20:47.967 回答