Reputation: 1
I have ListView, which displays some pictures. Not all pictures are visible on screen (in Window). I want to make something like 'contact sheet' - all pictures.
I got ScrollViewer from ListView, and got its full height (scrVwr.ExtentHeight). So, I call render:
var bmp = new RenderTargetBitmap(scrVwr.Width, scrVwr.ExtentHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(scrVwr);
After saving
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
var fileStream = IO.File.Create(picker.FileName);
encoder.Save(fileStream);
(so only ListView.ActualHeight is rendered)
I want to have rendered whole ListView/ScrollViewer (all items).
Upvotes: 0
Views: 55
Reputation: 28968
I think in your case you should not try to create the image from the rendered UI. This introduces avoidable problems like UI virtualization.
Instead, I recommend creating the tile image from the raw image data sources that the ListView
displays.
The following example uses a WriteableBitMap
to dynamically create a tiled image.
var uris = new List<Uri>
{
new Uri(@"C:\some_image.png", UriKind.Absolute),
new Uri(@"C:\some_image.png", UriKind.Absolute),
new Uri(@"C:\some_image.png", UriKind.Absolute),
};
int minTilePixelWidth = 1000;
int tileRowPixelHeight = 200;
var tileImageCreator = new TileImageCreator();
BitmapSource imageSource = tileImageCreator.CreateTileImage(uris, minTilePixelWidth, tileRowPixelHeight);
SaveToFile(imageSource);
Tile.cs
internal readonly struct Tile
{
public Tile(BitmapImage image, int horizontalOffset, int verticalOffset)
{
this.Image = image;
this.HorizontalOffset = horizontalOffset;
this.VerticalOffset = verticalOffset;
}
public int HorizontalOffset { get; }
public BitmapImage Image { get; }
public int VerticalOffset { get; }
}
TileImageCreator.cs
class TileImageCreator
{
public BitmapSource CreateTileImage(IEnumerable<Uri> imageUris, int minTilePixelWidth, int tileRowPixelHeight)
{
int tileBitmapHeight = tileRowPixelHeight;
int tileBitmapWidth = minTilePixelWidth;
int horizontalOffset = 0;
int verticalOffset = 0;
var tileInfos = new List<Tile>();
foreach (Uri uri in imageUris)
{
var image = new BitmapImage();
image.BeginInit();
image.UriSource = uri;
image.DecodePixelHeight = tileRowPixelHeight;
image.EndInit();
image.Freeze();
tileBitmapWidth = Math.Max(tileBitmapWidth, image.PixelWidth);
int newRowWidth = horizontalOffset + image.PixelWidth;
bool isOverflowing = newRowWidth > tileWidth;
if (isOverflowing)
{
horizontalOffset = 0;
verticalOffset += tileRowPixelHeight;
tileBitmapHeight += tileRowPixelHeight;
}
var tileInfo = new Tile(image, horizontalOffset, verticalOffset);
tileInfos.Add(tileInfo);
horizontalOffset += image.PixelWidth;
}
var tileBitmap = new WriteableBitmap(tileBitmapidth, tileBitmapHeight, 96, 96, PixelFormats.Pbgra32, null);
foreach (Tile tile in tileInfos)
{
BitmapImage tileImage = tile.Image;
int bytesPerPixel = (tileImage.Format.BitsPerPixel + 7) / 8;
int pixelStride = tileImage.PixelWidth * bytesPerPixel;
int bufferSize = tileImage.PixelHeight * pixelStride;
byte[] sourceBuffer = new byte[bufferSize];
tileImage.CopyPixels(sourceBuffer, pixelStride, 0);
tileBitmap.WritePixels(new Int32Rect(0, 0, tileImage.PixelWidth, tileImage.PixelHeight), sourceBuffer, pixelStride, tile.HorizontalOffset, tile.VerticalOffset);
}
tileBitmap.Freeze();
return tileBitmap;
}
}
Upvotes: 0