In my WPF application I have a ScrollViewer
which is dynamically populated by DataBinding
and the use of a UserControl
. Imagine that my UserControl
is something simple that only contains a label, and the value displayed in the label comes from a list. So when I run the application it looks like below:
As you can see there are multiple instances of the UserControl
each with a different value that is dynamically populated. My goal is to export each of the UserControls
as a separate PNG. My EXPORT PNG
button click should do this.
So I looked around and found this example which works fine for exporting the entire content of the ScorllViewer
.
So I tried modifying it to achieve my goal, and I do get it to work to a certain extent, but not quite what I want.
Here's my code:
private void ExportPNG()
{
var dir = Directory.GetCurrentDirectory();
var file = "ITEM_{0}.PNG";
var height = 100.0; // Height of the UserControl
var width = mainSV.ActualWidth;
for (int i = 1; i <= ItemList.Count; i++)
{
var path = System.IO.Path.Combine(dir, string.Format(file, i));
Size size = new Size(width, height);
UIElement element = mainSV.Content as UIElement;
element.Measure(size);
element.Arrange(new Rect(new Point(0, 0), size));
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
VisualBrush sourceBrush = new VisualBrush(element);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(width, height)));
}
renderTarget.Render(drawingVisual);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
encoder.Save(stream);
}
}
}
Now this exports 5 PNGs (when my list has 5 items), bu they all contain the image of the first element:
And I figure the problem is likely where I do the drawingContext.DrawRectangle()
because my rectanble coordinates are always the same. So I tried changing it to the following, which I thought should work, but for some reason it just generates one PNG with the first UserControl
and 4 empty PNGs.
using (drawingContext)
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, (i-1) * height), new Point(width, height + ((i-1) * height))));
}
And the result:
What am I doing wrong here?
If you would like to run the code please find it here.
Please check below two methods. I tested it and it is working very well. 1. First, find children from you Items controls 2. Convert them to PNGs.
private void ExportPNG()
{
var dir = Directory.GetCurrentDirectory();
var file = "ITEM_{0}.PNG";
var height = 100.0;
var width = 100.0;
var children = GetChildrenOfType<UCDisplayItem>(itC);
foreach (var item in children)
{
var path = System.IO.Path.Combine(dir, string.Format(file, DateTime.Now.Ticks));
Size size = new Size(width, height);
UIElement element = item as UIElement;
element.Measure(size);
element.Arrange(new Rect(new Point(0, 0), size));
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
VisualBrush sourceBrush = new VisualBrush(element);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(width, height)));
//drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, (i - 1) * height), new Point(width, height + ((i - 1) * height))));
}
renderTarget.Render(drawingVisual);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
encoder.Save(stream);
}
}
}
public List<T> GetChildrenOfType<T>( DependencyObject depObj)
where T : DependencyObject
{
var result = new List<T>();
if (depObj == null) return null;
var queue = new Queue<DependencyObject>();
queue.Enqueue(depObj);
while (queue.Count > 0)
{
var currentElement = queue.Dequeue();
var childrenCount = VisualTreeHelper.GetChildrenCount(currentElement);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(currentElement, i);
if (child is T)
result.Add(child as T);
queue.Enqueue(child);
}
}
return result;
}