如果是ScrollViewer里面,因为内容过多没显示出来的部分,没有必要做这么复杂的“截图”和“拼接”操作,只需要将目标对象做一个VisualBrush再render出来就行了。如下:
public static ImageSource FromVisual(Visual visual)
{
var bounds = VisualTreeHelper.GetDescendantBounds(visual);
var bitmap = new RenderTargetBitmap(
(int)(bounds.Width * dpi),
(int)(bounds.Height * dpi),
96,
96,
PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
var brush = new VisualBrush(visual);
drawingContext.DrawRectangle(brush, null, bounds);
}
bitmap.Render(drawingVisual);
return bitmap;
}
======================分割线========================
看了一下你的帖子,截取的还是DataGrid里面的内容,会比较麻烦:
1、DataGrid内容分两部分,一部分是Header,一部分是Rows,而其中Header是坐在内部的ScrollViewer的ControlTemplate里面的,不能简单按照ScrollViewer内部的内容来截图
2、DataGrid默认开启了Virtualization提高性能,看不见的地方是不渲染的。
因此,需要做的事情:
1、去掉DataGrid的Virtualization,设置
EnableColumnVirtualization="False"
EnableRowVirtualization="False"
2、修改HeadersVisibility,否则表头截图会少掉左侧一段RowHeader的位置
3、拼接图片
private void Button_Click(object sender, RoutedEventArgs e)
{
// 记录当前HeaderVisibility,并设置现在的为Column,否则每行前面会出来一个Button
var oldHeaderVisibility = dg_List.HeadersVisibility;
dg_List.HeadersVisibility = DataGridHeadersVisibility.Column;
// 记录旧的Virtualization设置,并设置为false
var oldRowVirtualization = dg_List.EnableRowVirtualization;
var oldColumnVirtualization = dg_List.EnableColumnVirtualization;
dg_List.EnableRowVirtualization = false;
dg_List.EnableColumnVirtualization = false;
// 放到Loaded优先级执行,让画面完成布局计算
this.Dispatcher.BeginInvoke(new Action(() =>
{
// 拿到内部的ScrollViewer
var scrollViewer = dg_List.GetDesendentChild<ScrollViewer>();
// 表头
var headerPresenter = scrollViewer.GetDesendentChild<DataGridColumnHeadersPresenter>();
var header = headerPresenter.GetDesendentChild<ItemsPresenter>();
var headerImage = ImageSourceHelper.FromVisual(header);
// 表内容
var rows = scrollViewer.Content as Visual;
var rowsImage = ImageSourceHelper.FromVisual(rows);
// 拼接
var bitmap = Join(new[] { headerImage, rowsImage });
using (var fs = File.OpenWrite("Capture.png"))
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(fs);
}
// 还原设置
dg_List.HeadersVisibility = oldHeaderVisibility;
dg_List.EnableRowVirtualization = oldRowVirtualization;
dg_List.EnableColumnVirtualization = oldColumnVirtualization;
}), DispatcherPriority.Loaded);
}
其中,GetDesendentChild是一个扩展方法:
/// <summary>
/// 在VisualTree中往下查找一个指定类型的孩子
/// </summary>
/// <typeparam name="T">要获取的孩子节点类型</typeparam>
/// <param name="target">目标元素</param>
/// <returns>找到的孩子。如果没有找到,返回null</returns>
public static T GetDesendentChild<T>(this DependencyObject target) where T : DependencyObject
{
return GetDesendentChild<T>(target, true);
}
/// <summary>
/// 在VisualTree中往下查找一个指定类型的孩子
/// </summary>
/// <typeparam name="T">要获取的孩子节点类型</typeparam>
/// <param name="target">目标元素</param>
/// <param name="exactlyMatch">如果为true,则孩子类型严格一致才返回。否则,只要是够转为指定类型的,即返回</param>
/// <returns>找到的孩子。如果没有找到,返回null</returns>
public static T GetDesendentChild<T>(this DependencyObject target, bool exactlyMatch) where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(target);
if (childCount == 0) return null;
for (int i = 0; i < childCount; i++)
{
var current = VisualTreeHelper.GetChild(target, i);
if (current is T)
{
if (exactlyMatch)
return current.GetType() == typeof(T) ? current as T : GetDesendentChild<T>(current, exactlyMatch);
else
return (T)current;
}
var desendent = current.GetDesendentChild<T>(exactlyMatch);
if (desendent != null)
return desendent;
}
return null;
}
Join方法是参照楼上修改的(暂时去掉了dpi计算),改进了一下循环的算法,反复调用ElementAt开销是比较大的,既然是IEnumerable,直接foreach就行了啊:
private static BitmapSource Join(IEnumerable<ImageSource> images)
{
if (images == null || !images.Any())
return null;
var dpi = 96.0d;
// 计算上下拼接后的图片大小
var rectAll = new Rect(new Size(
images.Max(m => m.Width),
images.Sum(s => s.Height)));
var bitmap = new RenderTargetBitmap(
(int)(rectAll.Width),
(int)(rectAll.Height),
dpi,
dpi,
PixelFormats.Default);
var drawingVisual = new DrawingVisual();
using (var context = drawingVisual.RenderOpen())
{
// 绘制背景
context.DrawRectangle(Brushes.White, null, rectAll);
// 拼接
var currentY = 0d;
foreach (var image in images)
{
var rect = new Rect(
new Point(0, currentY),
new Point(image.Width, image.Height));
var brush = new ImageBrush(image);
context.DrawRectangle(brush, null, rect);
currentY += image.Height;
}
}
bitmap.Render(drawingVisual);
return bitmap;
}
截图效果:
大神..太复杂了.有么有简单些的方法? 我根本看不懂额..
datagrid不能截图的话我能不能把他放到一个grid中截图?
如果你要放到Grid中截图,会更麻烦。为了完成DataGrid所有行的布局计算,你需要给DataGrid一个“很大”的空间,才不会让它产生滚动条。而你不可能在当前窗口完成这个事情,所以你要做的是,在一个很远的地方(比如-1000,-1000)的位置创建一个窗口(对用户不可见),然后放一个ScrollViewer,把DataGrid从当前容器中挪出来,再放到这个ScrollViewer中,让其完成布局,截图,再放回来……
上面的那个代码也不算复杂,你用Blend看看DataGrid的ControlTemplate就应该清楚是怎么回事了。我给你整理个代码工程给你吧,放百度云盘了:http://pan.baidu.com/s/1nDiQC
2024-09-19 广告