wpf 容器截图

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了wpf 容器截图相关的知识,希望对你有一定的参考价值。

怎么样通将某个容器截图??以及如何将截图拼接成一个?

参考技术A

如果是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

本回答被提问者和网友采纳

以上是关于wpf 容器截图的主要内容,如果未能解决你的问题,请参考以下文章

WPF 截图控件之绘制箭头「仿微信」

WPF控件截图

采用WPF开发截图程序,so easy!

采用WPF开发截图程序,so easy!

WPF 对控件进行截图且不丢失范围(转载)

截屏时延迟 C# (WPF)