问题
I kept rendering a black/blank image of a UserControl not shown in window. The UserControl contains LiveChart's CartesianChart and few UIElements. First I borrowed solutions from SO: C# chart to image with LiveCharts
and Github: Live-Charts/ChartToImageSample.xaml.cs and apply SaveToPng
and EncodeVisual
on a simple UIControl generated in runtime (not added or shown in window), which works.
Canvas control = new Canvas() { Width=100, Height=100, Background = new SolidColorBrush(Colors.Pink)};
control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
control.Arrange(new Rect(0, 0, control.DesiredSize.Width, control.DesiredSize.Height));
control.UpdateLayout();
SaveToPng(control, @"C:\Foo.png");
private void SaveToPng(FrameworkElement visual, string fileName)
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
EncodeVisual(visual, fileName, encoder);
}
private static void EncodeVisual(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (FileStream stream = File.Create(fileName)) encoder.Save(stream);
}
But when I applied the same logic to a UserControl
UserControl control = new UserControl(params)
// canvas.Children.Add(control); <-- if comment out, output blank/black image
control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
control.Arrange(new Rect(0, 0, control.DesiredSize.Width, control.DesiredSize.Height));
foreach (CartesianChart cc in FindVisualChildren<CartesianChart>(control))
{
cc.Update(true, true); //force chart redraw
}
control.UpdateLayout();
SaveToPng(control, @"C:\Foo.png");
<!-- UserControl XAML -->
<UserControl>
<Grid>
<Border>
<Grid>
<Label></Label>
<Label></Label>
<Label></Label>
<CartesianChart DisableAnimations="True"></CartesianChart>
</Grid>
</Border>
</Grid>
</UserControl>
That output a blank/black image. And only when I add UserControl into a UIControl existed and visible on the window/XAML will the UserControl gets rendered properly to an image (e.g. canvas.Children.Add(lc)
). I then also tried the solution described in SO: How to render a WPF UserControl to a bitmap without creating a window, but I get the identical result - that is I need to first add this UserControl to a visible UIControl, whether or not I perform UpdateLayout
on the UserControl.
Why the first example (Canvas) gets rendered, but not the second example (UserControl)? What are the difference and how can I fix this?
回答1:
For me this works fine for only the chart as well as for the whole user control: (Note: be sure your usercontrol has a visible background color, if the background is transparent it will be rendered transparent which most image viewer show as black)
EDIT: ok, I guess my solution won't work if the chart isn't visible. But if you can't get it to work otherwise, why not show the usercontrol in a new window, save it and close that window again?
public static BitmapSource RenderChartAsImage(FrameworkElement chart)
{
RenderTargetBitmap image = new RenderTargetBitmap((int)chart.ActualWidth, (int)chart.ActualHeight,
96, 96, PixelFormats.Pbgra32);
image.Render(chart);
return image;
}
public static void SaveChartImage(FrameworkElement chart)
{
System.Windows.Media.Imaging.BitmapSource bitmap = RenderChartAsImage(chart);
Microsoft.Win32.SaveFileDialog saveDialog = new Microsoft.Win32.SaveFileDialog();
saveDialog.FileName += "mychart";
saveDialog.DefaultExt = ".png";
saveDialog.Filter = "Image (*.png)|*.png";
if (saveDialog.ShowDialog() == true)
{
using (FileStream stream = File.Create(saveDialog.FileName))
{
System.Windows.Media.Imaging.PngBitmapEncoder encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bitmap));
encoder.Save(stream);
}
}
}
来源:https://stackoverflow.com/questions/51823020/render-image-from-usercontrol-that-has-livechart