在一个windows服务中,我试图将一个WPF控件渲染成png,问题出在其中一个控件上(即Oxyplot PlotView)检查控件是否实际可见,如果不可见,则不绘制任何内容。我发现使IsVisible返回true的唯一方法是将控件放置在窗口中并在窗口上调用Show
,所有窗口实际弹出的结果,我不想这样
我试着把控件放在Page
中,但没有帮助。
下面是我使用的基本代码:
var plotView = new PlotView() { Height = height, Width = width, XPS = true, Model = plot };
var view = new ContentControl() { Content = plotView, Width= width, Height = height };
//force bindings
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.ApplicationIdle, new DispatcherOperationCallback(_ => { return null; }), null);
view.Measure(new Size(width, height));
view.Arrange(new Rect(0, 0, width, height));
view.UpdateLayout();
var encoder = new PngBitmapEncoder();
var render = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
render.Render(view);
encoder.Frames.Add(BitmapFrame.Create(render));
using (var s = File.Open(filename, FileMode.Create))
{
encoder.Save(s);
}
这是在STA线程中运行的。渲染其他小部件,这不是一个PlotView
工作,我可以在调试器中看到PlotView如何不画它的视觉效果,因为它检查它是否可见。
1条答案
按热度按时间l0oc07j21#
我可以建议两种解决方案,重点是
UIElement.IsVisible
只有在元素成为元素树的子元素并且可能可见时才被设置为true
(由WPF)。请注意,目标是强制
UIElement.IsVisible
返回true
。第一种解决方案是“伪造”未呈现的元素(在本例中为
PlotView
)是逻辑树的子元素。它通过临时将未呈现的元素声明为新的可视根来实现这一点。缺点是,如果操作时间过长,则会为原始可视根(通常为
Window
)的完整元素树引发Framework.Unloaded
事件。然后在恢复可视根后,会引发FrameworkElement.Loaded
事件。此外,我们会触发完整的布局通道。这也意味着如果迭代足够长,用户将可以看到它。然而,在这个场景中,执行时间只有几微秒,这就是为什么这些性能考虑因素不适用的原因。
第二种解决方案使用“虚拟”渲染主机来使未渲染的元素虚拟可见。这样的主机应该是一个容器,当可用空间为零时,不会将其可用空间强制分配给其子元素。
为了避免对布局造成任何影响,必须将主机容器配置为零大小。这是因为我们必须将主机插入到可视树中。
Canvas
面板是完美的,因为它的初始大小为零,但不限制其子元素的大小。虽然Canvas
是不可见的,不会影响布局,但子元素仍然可以占据它们想要的空间。其他容器在设置为零大小时,例如Border
,将使用此零大小来测量其子元素。这导致布局引擎不认为子元素可见。缺点是我们必须修改元素树来添加额外的虚拟元素容器。
方案一
MainWindow.xaml.cs
方案二
主窗口.xaml
MainWindow.xaml.cs