wpf 容器截图

怎么样通将某个容器截图??以及如何将截图拼接成一个?... 怎么样通将某个容器截图??以及如何将截图拼接成一个? 展开
 我来答
RayNCC
2013-11-17 · TA获得超过615个赞
知道小有建树答主
回答量:452
采纳率:0%
帮助的人:604万
展开全部

如果是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
本回答被提问者和网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
AiPPT
2024-09-19 广告
随着AI技术的飞速发展,如今市面上涌现了许多实用易操作的AI生成工具1、简介:AiPPT: 这款AI工具智能理解用户输入的主题,提供“AI智能生成”和“导入本地大纲”的选项,生成的PPT内容丰富多样,可自由编辑和添加元素,图表类型包括柱状图... 点击进入详情页
本回答由AiPPT提供
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式