DrawingVisual类是一个轻量级的绘图类,用于呈现形状、 图像或文本。它通常是在后台或者剪切板中绘制,用来生成一张图片。
这里用DrawingVisual实现类似微信对话框的效果,如图:
绘制原理:将对话框和头像绘制在一张画布上,头像大小固定为36x36像素,根据行高、文字大小等计算绘制文本的大小,再根据文本大小对原始对话框底图进行裁剪和拼接(四个角保留,对四条边进行拉伸,这样可以保留原始底图的样式)。
原对话框底图:
实现步骤:
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);
}
}
}