用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机

 

 


  在项目开发中经常会遇到这样一种情况,就是需要将用户填写的信息排版到一张表单中,供打印或存档。这里就以保存为图片为例,比如我需要实现下面的功能:

将图片用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机   根据用户信息保存为用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机

 

 

总体思路


  1.建个UserControl将背景设置为表单图片。

  2.在姓名的位置添加Textblock控件,将Text绑定到Name属性上。

  3.动态加载UserControl,再将整个UserControl可视树转化为一张图片,保存。

 

 

实现过程


  关于怎么设置背景,和布局Textblock我就不多说了,相信大家都会。要注意的是,因为之后我要用XamlReader.Load()来动态加载XAML所以UserControl要把X:Class="xxx"这段给删了。

  接下来直接看代码,基本每句都写了注释

需要生成图片的XAML

<Grid >

        <Image Source="pack://siteoforigin:,,,/view/Print/Images/XXX.jpg" />

        <TextBlock Height="88" HorizontalAlignment="Left" Margin="928,1624,0,0" Text="{Binding Name}" VerticalAlignment="Top" Width="240" FontSize="64" />

    </Grid>

控件代码:

/// <summary>

        /// 设置需要生成的Xaml的地址

        /// </summary>

        public string XamlUrl

        {

            get { return (string)GetValue(XamlUrlProperty); }

            set { SetValue(XamlUrlProperty, value); }

        }

        public static readonly DependencyProperty XamlUrlProperty =

            DependencyProperty.Register("XamlUrl", typeof(string), typeof(CopXaml2Bmp), null);





        /// <summary>

        /// 生成的图片地址

        /// </summary>

        public string BmpUrl

        {

            get { return (string)GetValue(BmpUrlProperty); }

            set { SetValue(BmpUrlProperty, value); }

        }

        public static readonly DependencyProperty BmpUrlProperty =

            DependencyProperty.Register("BmpUrl", typeof(string), typeof(CopXaml2Bmp),null);

        

        /// <summary>

        /// 设置dpi

        /// </summary>

        public int Dpi

        {

            get { return (int)GetValue(DpiProperty); }

            set { SetValue(DpiProperty, value); }

        }

        public static readonly DependencyProperty DpiProperty =

            DependencyProperty.Register("Dpi", typeof(int), typeof(CopXaml2Bmp), null);



        



        private void UserControl_Loaded(object sender, RoutedEventArgs e)

        {

            try

            {       

                FileStream fs = new FileStream(XamlUrl, FileMode.Open, FileAccess.Read);//动态加载XAML                

                ContentControl myGrid = XamlReader.Load(fs) as ContentControl;

                //myGrid.DataContext = this.DataContext;//设置datacontext

                double width = (this.Parent as Grid).Width;//获取母容器的高宽

                double height = (this.Parent as Grid).Height;



                myGrid.Loaded += new RoutedEventHandler((sender1, e1) =>

                {

                    (this.Parent as Grid).Children.Remove(myGrid);//从母容器中移除该内容



                    var size = new Size(myGrid.Width, myGrid.Height);

                    myGrid.Measure(size);//二次布局,因为只是在内存中没有显示在UI上,需要经过布局才有内容

                    myGrid.Arrange(new Rect(new Point(0, 0), size));



                    System.IO.FileStream fs1 = new System.IO.FileStream(BmpUrl, System.IO.FileMode.Create);//创建一个文件流来存图片

                    RenderTargetBitmap bmp = new RenderTargetBitmap((int)myGrid.Width, (int)myGrid.Height, Dpi, Dpi, PixelFormats.Default);//visual对象转位图

                    bmp.Render(myGrid);//呈现visual

                    BitmapEncoder encoder = new TiffBitmapEncoder();//编码为图像流

                    encoder.Frames.Add(BitmapFrame.Create(bmp));//添加帧

                    encoder.Save(fs1);//保存文件

                    fs1.Close();



                    (this.Parent as Grid).Width = width;//大小还原

                    (this.Parent as Grid).Height = height;

                });

                (this.Parent as Grid).Width = myGrid.Width;//将母容器的高宽设置为需要的大小

                (this.Parent as Grid).Height = myGrid.Height;

                (this.Parent as Grid).Children.Add(myGrid);//添加进母容器让其暂时显示,以显示绑定的信息



                

            }

            catch

            {



            }

        }

使用控件代码(其父容器的datacontext中含有Name属性):

<Assistant:Xaml2Bmp XamlUrl="view/Print/XXX.xaml" BmpUrl="d:/test.bmp" Dpi="96" HorizontalAlignment="Right" Height="100" Margin="0,0,204,44" VerticalAlignment="Bottom" Width="100"/>

这样就将动态加载的XAML生成了d:/test.bmp图片。

  说实话,实现这个功能没什么难的,写这篇博客的目的在于,做这个控件时让我了解到了一个Binding刷新的问题。

  通过XamlReader.Load()加载的XAML对其设置datacontext,对应的Textblock.Text的内容为"",而不是绑定的内容。而当元素在UI上显示时,才会刷新数据绑定,从而让Textblock.Text的内容为绑定到的datacontext对应的节点上。

  所以上面的代码中,我把动态加载的XAML添加进了控件的父容器中,让其刷新绑定,并在其加载完成后从父容器中移除该控件,对于用户来说是什么都看不到的,然后将其转换为图片。父容器的大小也要能够容纳整个控件才行,生成的图片显示的只有父容器的范围部分。

更新:实践证明如果需要生成的元素里面包含自定义style的时候,需要在元素还在界面上显示时进行打印,一旦从UIRemove了该元素后貌似就会把自定义style释放掉,生成的图片有关自定义控件的地方就为空白,也就是说WPF程序当元素在UI上显示时才会加载样式,一旦从UI上移除就卸载掉,以提高性能。所以上面的代码需要把(this.Parent as Grid).Children.Remove(myGrid);这句移到生成图片那一句的后面。

  

你可能感兴趣的:(bind)