WPF之DrawingVisual绘图:实现仿微信对话框绘制

DrawingVisual类是一个轻量级的绘图类,用于呈现形状、 图像或文本。它通常是在后台或者剪切板中绘制,用来生成一张图片。

这里用DrawingVisual实现类似微信对话框的效果,如图:

WPF之DrawingVisual绘图:实现仿微信对话框绘制_第1张图片

 

绘制原理:将对话框和头像绘制在一张画布上,头像大小固定为36x36像素,根据行高、文字大小等计算绘制文本的大小,再根据文本大小对原始对话框底图进行裁剪和拼接(四个角保留,对四条边进行拉伸,这样可以保留原始底图的样式)。

原对话框底图:

WPF之DrawingVisual绘图:实现仿微信对话框绘制_第2张图片

实现步骤:

1、首先实现绘制图片类DrawDialog.cs,添加全局变量(这些属性可根据具体情况进行调整,或者变成控件实时控制),其中的ImageHelper类参考https://mp.csdn.net/postedit/90257098;

    public class DrawDialog
    {
        private static int ImageWidth = 200;//绘制整体图片的宽
        private static int ImageHeight = 100;//绘制整体图片的高
        private static string DialogBgPath = "";//对话框底图路径
        private static BitmapImage DialogBGImage = null;//对话框底图
        private static string Text = "";//对话内容
        private static Size TextAreaSize = new Size(200, 100);//文本区域大小
        private static int MaxTextWidth = 190;//最大文本宽度
        private static int TextFontsize = 20;//字体大小
        private static Color FontColor = Colors.Black;//字体颜色
        private static int TextEdge = 55;//文本边距,可根据对话框底图进行调整
        private static int LineSpace = 5;//文本行间距
        private static string IconPath = "";//头像路径
        private static BitmapImage IconImage = null;//头像
        private static int IconWidth = 36;//头像宽
        private static int IconHeight = 36;//头像高
        private static int DialogToIconDistance = 5;//对话框距头像的距离

        /// 
	/// 绘制对话框
	/// 
	public static BitmapImage DrawDialogImage(string text,string iconPath,string dialogPath)
        {
            Text = text;
            DialogBgPath = dialogPath;
            IconPath = iconPath;

            InitDrawParam();//初始化绘制参数

            RenderTargetBitmap rtb = new RenderTargetBitmap(ImageWidth, ImageHeight, 96, 96, PixelFormats.Default);
            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext dc = dv.RenderOpen())
            {
                DrawContent(dc);//绘制对话内容
                DrawIcon(dc);//绘制头像
            }
            rtb.Render(dv);
            return ImageHelper.ConventToBitmapImage(rtb);
        }
        /// 
        /// 初始化绘制参数
        /// 
        private static void InitDrawParam()
        {
            //获取头像
            IconImage = ImageHelper.LoadBitmapImageUniform(IconPath, IconWidth, IconHeight);
            if (IconImage == null)
            {
                IconWidth = IconHeight = 0;
            }
            //获取文本大小
            if (string.IsNullOrEmpty(Text))
            {
                TextAreaSize = new Size(0, 0);
            }
            else
            {
                Size textSize = GetTextSize(Text);
                TextAreaSize = new Size((int)Math.Round(textSize.Width), (int)Math.Round(textSize.Height));
            }
            //获取对话框底图
            System.Drawing.Bitmap bmpDialog = (System.Drawing.Bitmap)ImageHelper.LoadImageByPath(DialogBgPath);
            //获取对话框背景图
            DialogBGImage = ImageSplitJoint(bmpDialog, new Size(TextAreaSize.Width + TextEdge * 2, TextAreaSize.Height + TextEdge * 2));
            //获取要绘制图像的宽高
            ImageWidth = IconWidth + DialogBGImage.PixelWidth + DialogToIconDistance;//宽=头像宽+对话框宽
            ImageHeight = Math.Max(IconHeight, DialogBGImage.PixelHeight);//高=头像高和对话框高中的最大值
        }
        /// 
        /// 根据文本内容大小进行对话框底图图像拼接
        /// 
        public static BitmapImage ImageSplitJoint(System.Drawing.Bitmap bit, Size size)
        {
            if (size.Width == 0 || size.Height == 0 || bit == null)
                return null;

            BitmapImage tempImage = null;
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap((int)size.Width, (int)size.Height);

            //获取切割后的图片
            System.Drawing.Bitmap[] bitmaps = new System.Drawing.Bitmap[9];
            //左上角
            bitmaps[0] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(0, 0, TextEdge, TextEdge));
            //左下角
            bitmaps[1] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(0, bit.Height - TextEdge, TextEdge, TextEdge));
            //右上角
            bitmaps[2] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(bit.Width - TextEdge, 0, TextEdge, TextEdge));
            //右下角
            bitmaps[3] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(bit.Width - TextEdge, bit.Height - TextEdge, TextEdge, TextEdge));
            //上边框
            bitmaps[4] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(TextEdge, 0, bit.Width - TextEdge * 2, TextEdge));
            //下边框
            bitmaps[5] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(TextEdge, bit.Height - TextEdge, bit.Width - TextEdge * 2, TextEdge));
            //左边框
            bitmaps[6] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(0, TextEdge, TextEdge, bit.Height - TextEdge * 2));
            //右边框
            bitmaps[7] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(bit.Width - TextEdge, TextEdge, TextEdge, bit.Height - TextEdge * 2));
            //中间区域
            bitmaps[8] = ImageHelper.ClipBitmap(bit, new System.Drawing.Rectangle(TextEdge, TextEdge, bit.Width - TextEdge * 2, bit.Height - TextEdge * 2));

            //将切割后的图片拼接
            System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
            g.DrawImage(bitmaps[0], 0, 0, bitmaps[0].Width, bitmaps[0].Height);
            g.DrawImage(bitmaps[1], 0, bitmap.Height - bitmaps[1].Height, bitmaps[1].Width, bitmaps[1].Height);
            g.DrawImage(bitmaps[2], bitmap.Width - bitmaps[2].Width, 0, bitmaps[2].Width, bitmaps[2].Height);
            g.DrawImage(bitmaps[3], bitmap.Width - bitmaps[3].Width, bitmap.Height - bitmaps[3].Height, bitmaps[3].Width, bitmaps[3].Height);
            ImageAttributes ImgAtt = new ImageAttributes();
            //上下边框横向拉伸
            ImgAtt.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipX);
            g.DrawImage(bitmaps[4], new System.Drawing.Rectangle(bitmaps[0].Width, 0, bitmap.Width - bitmaps[0].Width - bitmaps[2].Width, bitmaps[0].Height), 0, 0, bitmaps[4].Width, bitmaps[4].Height, System.Drawing.GraphicsUnit.Pixel, ImgAtt);
            g.DrawImage(bitmaps[5], new System.Drawing.Rectangle(bitmaps[0].Width, bitmap.Height - bitmaps[5].Height, bitmap.Width - bitmaps[0].Width - bitmaps[2].Width, bitmaps[1].Height), 0, 0, bitmaps[5].Width, bitmaps[5].Height, System.Drawing.GraphicsUnit.Pixel, ImgAtt);
            //左右边框纵向拉伸
            ImgAtt.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipY);
            g.DrawImage(bitmaps[6], new System.Drawing.Rectangle(0, bitmaps[0].Height, bitmaps[0].Width, bitmap.Height - bitmaps[0].Height - bitmaps[1].Height), 0, 0, bitmaps[6].Width, bitmaps[6].Height, System.Drawing.GraphicsUnit.Pixel, ImgAtt);
            g.DrawImage(bitmaps[7], new System.Drawing.Rectangle(bitmap.Width - bitmaps[7].Width, bitmaps[2].Height, bitmaps[7].Width, bitmap.Height - bitmaps[0].Height - bitmaps[1].Height), 0, 0, bitmaps[7].Width, bitmaps[7].Height, System.Drawing.GraphicsUnit.Pixel, ImgAtt);
            //中间区域纵横拉伸
            ImgAtt.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
            g.DrawImage(bitmaps[8], new System.Drawing.Rectangle(bitmaps[0].Width, bitmaps[0].Height, bitmap.Width - bitmaps[6].Width - bitmaps[7].Width, bitmap.Height - bitmaps[0].Height - bitmaps[1].Height), 0, 0, bitmaps[8].Width, bitmaps[8].Height, System.Drawing.GraphicsUnit.Pixel, ImgAtt);
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                tempImage = new BitmapImage();
                tempImage.BeginInit();
                tempImage.StreamSource = new System.IO.MemoryStream(ms.ToArray());
                tempImage.EndInit();
            }
            return tempImage;
        }
        /// 
        /// 绘制头像
        /// 
        private static void DrawIcon(DrawingContext dc)
        {
            if (IconImage != null)
            {
                int tempX = ImageWidth - IconImage.PixelWidth;
                dc.DrawImage(IconImage, new Rect(tempX, 0, IconImage.PixelWidth, IconImage.PixelHeight));
            }
        }
        /// 
        /// 绘制对话内容
        /// 
        private static void DrawContent(DrawingContext dc)
        {
            double tempX, tempY;
            //获取对话框位置
            GetDialogPosition(out tempX, out tempY);
            dc.DrawImage(DialogBGImage, new System.Windows.Rect(tempX, tempY, DialogBGImage.PixelWidth, DialogBGImage.PixelHeight));
            //获取文本图片
            BitmapImage textImage = DrawText(Text, (int)TextAreaSize.Width, (int)TextAreaSize.Height);
            //获取文本位置
            tempX = tempX + TextEdge;
            tempY = tempY + TextEdge;
            if (textImage != null)
            {
                GetTextPosition(out tempX, out tempY, textImage.PixelWidth);
                dc.DrawImage(textImage, new System.Windows.Rect(tempX, tempY, textImage.PixelWidth, textImage.PixelHeight));
            }
        }
        /// 
        /// 获取对话框位置:X坐标=图片宽-底图宽-头像宽-对话框距头像的距离
        /// 
        private static void GetDialogPosition(out double tempX, out double tempY)
        {
            tempX = ImageWidth - (DialogBGImage == null ? 0 : DialogBGImage.PixelWidth) - (IconImage == null ? 0 : IconWidth) - (IconImage == null ? 0 : DialogToIconDistance);
            tempY = 0;
        }
        /// 
        /// 获取文本位置:X坐标=图片宽-头像宽-对话框距头像的距离-文本宽-文本边距
        /// 
        private static void GetTextPosition(out double tempX, out double tempY, double textWidth)
        {
            tempX = ImageWidth - (IconImage == null ? 0 : IconWidth) - (IconImage == null ? 0 : DialogToIconDistance) - textWidth - TextEdge;
            tempY = TextEdge;
        }
        /// 
	/// 绘制对话文本
	/// 
	private static BitmapImage DrawText(string Message, int Width, int Height)
        {
            if (Width == 0 || Height == 0 || Message.Length == 0)
                return null;

            string text = (string)Message.Clone();            
            text = Regex.Replace(text, @"\r\n", IntToCharToASCII(200));//换行

            RenderTargetBitmap rtb;
            DrawingVisual dv;
            DrawingContext dc;
            rtb = new RenderTargetBitmap(Width, Height, 96, 96, new System.Windows.Media.PixelFormat());
            dv = new DrawingVisual();
            dc = dv.RenderOpen();
            SolidColorBrush brush = new SolidColorBrush(FontColor);

            //文本坐标
            int x = 0;
            int y = 0;
            char[] chars;
            //单个字依次解析绘制
            chars = text.ToCharArray();

            FormattedText ft;
            FormattedText tempSize;

            for (int i = 0; i < chars.Length; i++)
            {
                string one = chars[i].ToString();
                string sizeOne = chars[i].ToString();

                if (sizeOne == " ")
                {
                    sizeOne = "a";//单字符统一用字母-a-的大小来计算
                }
                else if (sizeOne == " ")
                {
                    sizeOne = "微";//双字符统一用汉字-微-来计算
                }
                tempSize = new FormattedText(sizeOne, new CultureInfo(2052), System.Windows.FlowDirection.LeftToRight, new Typeface("微软雅黑"), TextFontsize, brush);

                if (chars[i] == 200)//遇到换行 纵坐标需要加上行间距
                {    
                    y += (int)tempSize.Height + LineSpace;
                    x = 0;
                    continue;
                }
                if (x + tempSize.Width > MaxTextWidth)//宽度超出时需要换行,纵坐标需要加上行间距
                {
                    x = 0;
                    y += (int)tempSize.Height + LineSpace;
                }
                ft = new FormattedText(one, new CultureInfo(2052), System.Windows.FlowDirection.LeftToRight, new Typeface("微软雅黑"), TextFontsize, brush, null, TextFormattingMode.Ideal);
                dc.DrawText(ft, new System.Windows.Point(x, y));
                x += (int)tempSize.Width;
            }

            dc.Close();
            rtb.Render(dv);
            return ImageHelper.ConventToBitmapImage(rtb);
        }
        /// 
        /// 获取文本大小
        /// 
        private static Size GetTextSize(string Message)
        {
            string text = (string)Message.Clone();
            //换行
            text = Regex.Replace(text, @"\r\n", IntToCharToASCII(200));

            char[] chars;
            //单个字依次解析绘制
            chars = text.ToCharArray();

            Size SizeResult = new Size();
            SolidColorBrush brush = new SolidColorBrush(FontColor);
            //文本坐标
            int x = 0;
            int y = 0;            
            double LastLineWidth = 0;//文本最终宽度
            double LastLineHeight = 0;//文本最终高度
            FormattedText tempSize;

            for (int i = 0; i < chars.Length; i++)
            {
                string one = chars[i].ToString();
                string sizeOne = chars[i].ToString();
                if (sizeOne == " ")//单字符统一用字母-a-的大小来计算
                {
                    sizeOne = "a";
                }
                else if (sizeOne == " ")//双字符统一用汉字-微-来计算
                {
                    sizeOne = "微";
                }
                tempSize = new FormattedText(sizeOne, new CultureInfo(2052), System.Windows.FlowDirection.LeftToRight, new Typeface("微软雅黑"), TextFontsize, brush);
                
                if (chars[i] == 200)//遇到换行 纵坐标需要加上行间距
                {   
                    LastLineWidth = LastLineWidth < x ? x : LastLineWidth;
                    x = 0;
                    y += (int)tempSize.Height + LineSpace;
                    continue;
                }
                if (x + (int)tempSize.Width > MaxTextWidth)//宽度超出时需要换行,纵坐标需要加上行间距
                {
                    LastLineWidth = LastLineWidth < x ? x : LastLineWidth;
                    x = 0;
                    y += (int)tempSize.Height + LineSpace;
                }
                x += (int)tempSize.Width;
                LastLineHeight = tempSize.Height;
            }

            SizeResult.Width = Math.Max(LastLineWidth, x);
            if (SizeResult.Width > MaxTextWidth)
            {
                SizeResult.Width = MaxTextWidth;
            }
            SizeResult.Height = LastLineHeight + y;
            return SizeResult;
        }
        /// 
	/// 通过一个数值得到ASCII对应的内容
	/// 
	public static string IntToCharToASCII(int num)
        {
            return ((char)num).ToString();
        }
    }

2、主窗体xaml:


    
        
            
            
        
        
            
        
        
            
            
        
    

3、窗体交互逻辑:

    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnSend_Click(object sender, RoutedEventArgs e)
        {
            this.mainCanvas.Children.Clear();
            string iconPath = @"E:\图片\小图\head.jpg";//头像路径
            string dialogBgPath = @"E:\图片\小图\bg.png";//对话框底图路径
            Image newImg = new Image();
            BitmapImage bi = DrawDialog.DrawDialogImage(this.tbSentText.Text.Trim(), iconPath, dialogBgPath);
            if(bi != null)
            {
                newImg.Source = bi;
                Canvas.SetLeft(newImg, this.mainCanvas.ActualWidth - bi.Width - 20);
                Canvas.SetTop(newImg, (this.mainCanvas.ActualHeight - bi.Height) / 2);
                this.mainCanvas.Children.Add(newImg);
            }
        }
    }

 

你可能感兴趣的:(WPF应用实例)