【问题标题】:How to avoid visual artifacts when hosting WPF user controls within a WinForms MDI app?在 WinForms MDI 应用程序中托管 WPF 用户控件时如何避免视觉伪影?
【发布时间】:2010-04-07 04:10:42
【问题描述】:
在 WinForms MDI 应用程序中托管 WPF 用户控件时,当您有多个相互重叠的表单会导致非常明显的视觉伪影时,会出现绘图问题。在将一个子窗体拖到另一个也承载 WPF 内容的子窗体上之后,或者通过在拖动子窗体时允许主 MDI 父窗体剪裁子窗体的边缘后,这些工件大部分可见。子窗体的拖放完成后,工件通常会保留下来,但我发现将焦点设置到不同的应用程序窗口,然后重新聚焦到我的应用程序窗口,它被重绘并且一切都很好,直到孩子表格再次移动。请看下面的图片来说明问题。
Microsoft 的那些人坚持认为 WinForms MDI 已经是 MDI 的充分解决方案,不需要在 WPF 中重新发明,尽管由于明显的缺点,我很难相信他们尝试以这种方式创建 WPF 应用程序。
更新:我遗漏的一些额外注意事项是,如果我在不设置 MdiParent 的情况下创建这些表单,它们将被创建为常规表单,并且不会发生此问题。这个问题似乎是 WinForms MDI 方案所独有的。此外,我目前在 Windows 7 Enterprise 上运行,我知道 Windows XP 上的结果可能完全不同,但我无法对此进行测试。
更新:我发现了一些我认为应该分享的关于这个问题的其他相关资源。
【问题讨论】:
标签:
wpf
mdi
winforms-interop
visual-artifacts
【解决方案1】:
似乎另一种解决方法是恢复为软件渲染,而不是利用硬件加速。这是 MSDN 论坛上的 suggestion by Marco Zhou。
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 中时,我将使用这个解决方案。当然,除非为任何一种情况找到更优雅的解决方案。
【解决方案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 父级以及 Dispatcher.Invoke(DispatcherPriority.Render, ...) 和 上尝试了这些相同的方法InvalidateVisual() 在我托管的 WPF 控件上,但没有其他方法可以接受专门在 MDI 父级上调用 Refresh()。
我意识到这可能不是最佳解决方案,因为每次子窗口移动几个像素时,我都会强制整个主应用程序窗口刷新,但就目前而言,这是我发现唯一可行的合理解决方案.如果其他人对此有任何替代解决方案或任何改进,我将很乐意接受您的回答。
【解决方案3】:
检查视频驱动程序并尝试禁用硬件加速。大多数伪影是由于驱动程序错误、显卡故障或完成刷新的时间不足造成的。
故障排除第一步:更新视频驱动程序。很明显,我知道。
我遇到了类似的问题,检查我的视频卡设置(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
希望这对某人有所帮助。
--瑞安·斯特拉斯堡
【解决方案4】:
您的用户控件或窗口加载事件;
this.WindowState = System.Windows.WindowState.Minimized;
this.WindowState = System.Windows.WindowState.Normal;
这似乎是不好的解决方案。无需用头撞墙。
土耳其谚语说:最好的代码是代码在运行:)