WPF,列表视图。 RenderTargetBitmap - 如何渲染整个ListView,不限于屏幕

问题描述 投票:0回答:1

我有ListView,它显示一些图片。并非所有图片都在屏幕上(在窗口中)可见。我想做一些类似“联系表”的东西 - 所有图片。

我从ListView中获取了ScrollViewer,并获取了它的完整高度(scrVwr.ExtentHeight)。所以,我调用渲染:

    var bmp = new RenderTargetBitmap(scrVwr.Width, scrVwr.ExtentHeight, 96, 96, PixelFormats.Pbgra32);
    bmp.Render(scrVwr);

保存后

    var encoder = new JpegBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bmp));
    var fileStream = IO.File.Create(picker.FileName);
    encoder.Save(fileStream);

我得到了这个: 已保存JPG

(因此仅渲染 ListView.ActualHeight)

我想渲染整个 ListView/ScrollViewer (所有项目)。

wpf listview rendertargetbitmap
1个回答
0
投票

我认为在你的情况下,你不应该尝试从渲染的 UI 创建图像。这引入了可避免的问题,例如 UI 虚拟化。
相反,我建议从

ListView
显示的原始图像源数据创建平铺图像。

以下示例使用

WriteableBitMap
动态创建平铺图像。

使用示例

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 tileHeight = tileRowPixelHeight;
    int tileWidth = 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();

      tileWidth = Math.Max(tileWidth, image.PixelWidth);

      int newRowWidth = horizontalOffset + image.PixelWidth;
      bool isOverflowing = newRowWidth > tileWidth;
      if (isOverflowing)
      {
        horizontalOffset = 0;
        verticalOffset += tileRowPixelHeight;
        tileHeight += tileRowPixelHeight;
      }

      var tileInfo = new Tile(image, horizontalOffset, verticalOffset);
      tileInfos.Add(tileInfo);

      horizontalOffset += image.PixelWidth;
    }

    var tileBitmap = new WriteableBitmap(tileWidth, tileHeight, 96, 96, PixelFormats.Pbgra32, null);
    foreach (Tile tile in tileInfos)
    {
      BitmapImage tileImage = tile.Image;
      int bytesPerPixel = (tileImage.Format.BitsPerPixel + 7) / 8;
      int stride = tileImage.PixelWidth * bytesPerPixel;
      int bufferSize = tileImage.PixelHeight * stride;
      byte[] sourceBuffer = new byte[bufferSize];
      tileImage.CopyPixels(sourceBuffer, stride, 0);
      tileBitmap.WritePixels(new Int32Rect(0, 0, tileImage.PixelWidth, tileImage.PixelHeight), sourceBuffer, stride, tile.HorizontalOffset, tile.VerticalOffset);
    }

    tileBitmap.Freeze();
    return tileBitmap;
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.