WPF 精准大小的消息气泡生成

下载:https://pan.baidu.com/s/1NkRguBs9Bfm5NcTs7NcxFQ
在其他地方发布过,都是这里写的。
为Wpf方式,生成的气泡宽度高度较为精确。
适用任意字体以及字号,可设置图像字体、字号、行间距、字间距、阴影、气泡宽度等,在RichtextboxHelper.cs内查看。
生成的气泡宽度高度较为精确。
如有高度不一致问题,是因为文字高度超出了插入图像的高度,可对文字大小或图像进行调整。
注意:
green_left_mess.png、green_right_mess.png、white_left_mess.png、white_right_mess.png
4个文件设置为“嵌入的资源”。
本例子附带5种文字,均为外部调用,不用安装或加入资源文件。

附带消息框photoshop文件,除不能调整大小外,可以ps设置任意方向、透明度、颜色、阴影等在程序中调用。
主要是操作Richtextbox组件
RichtextboxHelper.cs

/// <summary>
/// ** 文字高度 必须小于 插入的图像的高度,这里不做检测
/// * RichtextBox使用默认字体,如设定字体,修改替换的字符串
/// * Example:
///     RichtextboxHelper rtbHelper = new RichtextboxHelper();
///     // 可设置行间距、字间距、dpi、图像表示以及图像大小(宽度高度相同)
///
///     rtbHelper.Coordinates(rtbHelper.RtbToSubstitution(RichtextBox控件), 宽度, 字体, 字体大小); // 取得RichTextBox内容并转换
///     Grid grid = new Grid(); // 建立Grid
///     grid.SetValue(Canvas.LeftProperty,400.0); // 左边距
///     grid.SetValue(Canvas.TopProperty, 200.0); // 顶边距
///     rtbHelper.DrawText("图像资源文件所在的命名空间.图像路径.", grid,方向(左、右), 限定宽度, 字体, 字体大小, 颜色, 是否显示阴影,阴影模糊半径,阴影透明度);
///     控件.Children.Add(grid); // 添加到窗体控件
/// </summary>

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;

public class RichtextboxHelper
{
    public double objectWidth, objectHeight; // 结果图像的宽度、高度

    public double Dpix { get; set; } = 96;
    public double Dpiy { get; set; } = 96;
    public double lineSpacing { get; set; } = 0; // 行间距
    public double wordsSpacing { get; set; } = 0; // 字间距
    public string flag { get; set; } = "▣"; // 图像标识
    public int imagesize { get; set; } = 28; // 插入的图像大小

    /// <summary>
    /// 屏蔽回车键
    /// </summary>
    /// <param name="rtb">richtextbox控件</param>
    public void DisableEnterKey(RichTextBox rtb)
    {
        rtb.PreviewKeyDown += Rtb_PreviewKeyDown;
    }

    /// <summary>
    /// 按键事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Rtb_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyStates == Keyboard.GetKeyStates(Key.Enter))
        {
            e.Handled = true;
        }
    }

    /// <summary>
    /// richtextbox清除粘贴格式
    /// </summary>
    /// <param name="rtb">richtextbox控件</param>
    public void ClearPasteformat(RichTextBox rtb)
    {
        CommandBinding PasteCommand = new CommandBinding();
        PasteCommand.Command = ApplicationCommands.Paste;
        PasteCommand.Executed += PasteCommand_Executed;
        rtb.CommandBindings.Add(PasteCommand);
    }

    /// <summary>
    /// 清除粘贴格式
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        RichTextBox rtb = (RichTextBox)sender;
        string paste = Clipboard.GetText();
        paste = paste.Replace("\n", "").Replace("\t", "").Replace("\r", "").
            Replace("<Paragraph>", "").
            Replace("</Paragraph>", "").
            Replace("<Run xml:lang=\"zh-cn\">", "").
            Replace("<Run xml:lang=\"en-gb\">", "").
            Replace("<Run>", "").
            Replace("</Run>", "");
        Clipboard.Clear();
        Clipboard.SetText(paste);
        rtb.Paste();
        e.Handled = true;
    }

    /// <summary>
    /// richtextbox添加图像
    /// </summary>
    /// <param name="rtb">richtextbox控件</param>
    /// <param name="imageUrl">图像地址</param>
    public void AddRtbImage(RichTextBox rtb, string imageUrl)
    {
        Image image = new Image()
        {
            Width = imagesize,
            Height = imagesize,
            UseLayoutRounding = true,
            Source = new BitmapImage(new Uri(imageUrl, UriKind.RelativeOrAbsolute))
        };
        new InlineUIContainer(image, rtb.Selection.Start);
        rtb.Focus();
        /* // 光标移动
        rtb.RaiseEvent(
            new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Right)
            { RoutedEvent = UIElement.KeyDownEvent }
            );
            */
    }

    /// <summary>
    /// 文字属性
    /// </summary>
    private class CharacterAttribte
    {
        public string character; // 文字
        public double characterX; // 文字位置
        public int characterY; // 坐标 y
        public double characterWidth; // 文字宽度
        public double characterHeight; // 文字高度
        public double offsetX; // 坐标位置 x

        public CharacterAttribte(string s, double x, int y, double w, double h)
        {
            character = s;
            characterX = x;
            characterY = y;
            characterWidth = w;
            characterHeight = h;
            offsetX = characterX - characterWidth;
        }
    }
    private List<CharacterAttribte> characterXY = new List<CharacterAttribte>(); // 全部文字集合
    private List<int> lineHasImage = new List<int>(); // 有图像的行
    private List<double> lineWidth = new List<double>(); // 每行的宽度
    private List<double> lineOffsety = new List<double>(); // 每行的y坐标
    private double fontHeight = 0; // 字体高度

    /// <summary>
    /// richtextbox内部图像
    /// </summary>
    public class RtbImage
    {
        public Image image;
        public RtbImage(Image img)
        {
            image = img;
        }
    }
    public static List<RtbImage> rtbImages = new List<RtbImage>(); // richtextbox内部图像集合

    /// <summary>
    /// 取得全部文字相关信息
    /// </summary>
    /// <param name="content">字符串</param>
    /// <param name="width">限定的宽度</param>
    /// <param name="ftype">字体</param>
    /// <param name="fsize">字体大小</param>
    public void Coordinates(string content, double width, string ftype, int fsize)
    {
        int charaY = 0;
        double charaX = 0, _length = 0;

        for (int i = 0; i < content.Length; i++)
        {
            double[] _getsize = new double[2];
            if (content.Substring(i, 1) == flag)
            {
                _length = imagesize;
                lineHasImage.Add(charaY);
                _getsize[0] = imagesize;
                _getsize[1] = imagesize;
            }
            else
            {
                _getsize = ObtainCharSize(content.Substring(i, 1), ftype, fsize);
                _length = _getsize[0];
            }
            if ((charaX + _length) > width)
            {
                lineWidth.Add(charaX);
                charaX = _length;
                charaY += 1;
            }
            else
            {
                charaX += _length;
            }
            characterXY.Add(new CharacterAttribte(content.Substring(i, 1), charaX, charaY, _getsize[0], _getsize[1]));
        }
        lineWidth.Add(charaX);
        lineHasImage = new HashSet<int>(lineHasImage).ToList();
    }

    /// <summary>
    /// 清除记录的文字信息
    /// </summary>
    private void ClearInfo()
    {
        characterXY.Clear();
        lineHasImage.Clear();
        lineWidth.Clear();
        lineOffsety.Clear();
        rtbImages.Clear();
    }

    /// <summary>
    /// 取得richtextbox全部图像
    /// </summary>
    /// <param name="rtb">richtextbox控件</param>
    private static void ObtainRtbImages(RichTextBox rtb)
    {
        foreach (Block block in rtb.Document.Blocks)
        {
            if (block is Paragraph)
            {
                Paragraph paragraph = (Paragraph)block;
                foreach (Inline inline in paragraph.Inlines)
                {
                    if (inline is InlineUIContainer)
                    {
                        InlineUIContainer uiContainer = (InlineUIContainer)inline;
                        if (uiContainer.Child is Image)
                        {
                            rtbImages.Add(new RtbImage((Image)uiContainer.Child));
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// 转换richtextbox内容为替代字符串
    /// </summary>
    /// <param name="rtb"></param>
    /// <returns></returns>
    public string RtbToSubstitution(RichTextBox rtb)
    {
        ObtainRtbImages(rtb);

        string xaml = string.Empty;
        TextRange textRange = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
        using (MemoryStream ms = new MemoryStream())
        {
            textRange.Save(ms, DataFormats.Xaml);
            ms.Seek(0, SeekOrigin.Begin);
            StreamReader sr = new StreamReader(ms);
            xaml = sr.ReadToEnd();
        };

        int begin = xaml.IndexOf("<Paragraph>") + 11;
        int end = xaml.LastIndexOf("</Paragraph>");
        return xaml.Substring(begin, end - begin).
            Replace("<Run> </Run>", flag).
            Replace("</Paragraph><Paragraph>", "\r").
            Replace("<Run xml:lang=\"zh-cn\">", "").
            Replace("<Run xml:lang=\"en-gb\">", "").
            Replace("<Run>", "").
            Replace("</Run>", "");
    }

    /// <summary>
    /// 取得文字的高度、宽度
    /// </summary>
    /// <param name="content">文字</param>
    /// <param name="ftype">字体</param>
    /// <param name="fsize">字体大小</param>
    /// <returns></returns>
    private double[] ObtainCharSize(string content, string ftype, double fsize)
    {
        double[] size = new double[2];
        FormattedText formattext = new FormattedText(content, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(ftype), fsize, Brushes.Black);
        size[0] = formattext.Width + wordsSpacing;
        size[1] = formattext.Height;
        fontHeight = formattext.Height;
        return size;
    }

    /// <summary>
    /// richtextbox内容生成图像
    /// </summary>
    /// <param name="name_space">图像资源所在命名空间</param>
    /// <param name="control">Grid控件</param>
    /// <param name="fwidth">限定的宽度</param>
    /// <param name="fnt">字体</param>
    /// <param name="fntsize">字体大小</param>
    /// <param name="fontColor">文字颜色</param>
    /// <param name="shadow">内容是否有阴影</param>
    /// <param name="blur">阴影模糊半径</param>
    /// <param name="opacity">阴影透明度</param>
    /// <returns></returns>
    public void DrawText(
        string name_space,
        Grid control,
        int type,
        int fwidth, 
        string fnt, double fntsize, Brush fontColor, 
        bool shadow = false, double blur = 10, double opacity = 0.4)
    {
        double _imagewidth = lineWidth.Max() > fwidth ? fwidth : lineWidth.Max();
        double _imageheight = 0;

        int box_width, box_height;
        double vx, vy, _sety = 0;

        for (int i = 0; i < lineWidth.Count; i++)
        {
            lineOffsety.Add(_sety);
            if (lineHasImage.Contains(i))
            {
                _sety += imagesize + lineSpacing;
                _imageheight += imagesize + lineSpacing;
            }
            else
            {
                _sety += fontHeight + lineSpacing;
                _imageheight += fontHeight + lineSpacing;
            }
        }

        vx = type == 0 ? 32 : 22;
        if (shadow)
        {
            _imageheight += blur;
            _imagewidth += blur;
            box_width = (int)Math.Ceiling(_imagewidth) - 35 < 1 ? 1 : (int)Math.Ceiling(_imagewidth) - 35;
            box_height = (int)Math.Ceiling(_imageheight) - 35 < 1 ? 1 : (int)Math.Ceiling(_imageheight) - 35;
            vy = 19.0;
        }
        else
        {
            box_width = (int)Math.Ceiling(_imagewidth) - 35 < 1 ? 1 : (int)Math.Ceiling(_imagewidth) - 35 + (int)Math.Ceiling(blur);
            box_height = (int)Math.Ceiling(_imageheight) - 35 < 1 ? 1 : (int)Math.Ceiling(_imageheight) - 35 + (int)Math.Ceiling(blur);
            vy = 20.0;
        }

        RenderTargetBitmap composeImage = new RenderTargetBitmap((int)Math.Ceiling(_imagewidth), (int)Math.Ceiling(_imageheight), Dpix, Dpiy, PixelFormats.Default);
        DrawingVisual drawingVisual = new DrawingVisual();

        int img_index = 0;
        for (int i = 0; i < characterXY.Count; i++)
        {
            FormattedText formattext = new FormattedText(characterXY[i].character, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(fnt), fntsize, fontColor);
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                if (shadow)
                {
                    drawingVisual.Effect = new DropShadowEffect() { BlurRadius = blur, Opacity = opacity };
                }

                double ih = 0;
                if (lineHasImage.Contains(characterXY[i].characterY))
                {
                    ih = (imagesize - fontHeight) / 2;
                }
                double offsetY = lineOffsety[characterXY[i].characterY] + ih;

                if (characterXY[i].character == flag)
                {
                    drawingContext.DrawImage(rtbImages[img_index].image.Source, new Rect(characterXY[i].offsetX, offsetY - ih, imagesize, imagesize));
                    img_index += 1;
                }
                else
                {
                    drawingContext.DrawText(formattext, new Point(characterXY[i].offsetX, offsetY));
                }
            }
            composeImage.Render(drawingVisual);
        }
       
        BitmapSource box = DrawMessage(name_space, box_width, box_height, type);
        Image imageBox = new Image()
        {
            Source = box,
        };
        imageBox.SetValue(Canvas.LeftProperty,0.0);
        imageBox.SetValue(Canvas.TopProperty, 0.0);
        Image imageText = new Image()
        {
            Source = composeImage,
        };
        imageText.SetValue(Canvas.LeftProperty, vx);
        imageText.SetValue(Canvas.TopProperty, vy);
        Canvas canvas = new Canvas();
        canvas.Children.Add(imageBox);
        canvas.Children.Add(imageText);

        control.Children.Add(canvas);
        ClearInfo();

        objectWidth = box.Width;
        objectHeight = box.Height;
    }

    /// <summary>
    /// 绘制对话框
    /// </summary>
    /// <param name="name_space"> 图像资源文件命名空间.路径. </param>
    /// <param name="_width">宽度</param>
    /// <param name="_height">高度</param>
    /// <param name="type">0-左方向,1-右方向</param>
    /// <returns></returns>
    private BitmapImage DrawMessage(string name_space,  int _width, int _height, int type)
    {
        System.Drawing.Rectangle[,] rect = new System.Drawing.Rectangle[2, 9] {
                {   new System.Drawing.Rectangle(0, 0, 35, 40), new System.Drawing.Rectangle(35, 0, 145, 40), new System.Drawing.Rectangle(180, 0, 43, 40),
                    new System.Drawing.Rectangle(0, 66, 35, 22),new System.Drawing.Rectangle(35, 66, 145, 22),new System.Drawing.Rectangle(180, 66, 43, 22),
                    new System.Drawing.Rectangle(0, 40, 35, 26),new System.Drawing.Rectangle(35, 40, 145, 26) ,new System.Drawing.Rectangle(180, 40, 43, 26)
                },
                {   new System.Drawing.Rectangle(0, 0, 43, 40), new System.Drawing.Rectangle(43, 0, 145, 40), new System.Drawing.Rectangle(188, 0, 35, 40),
                    new System.Drawing.Rectangle(0, 66, 43, 22), new System.Drawing.Rectangle(43, 66, 145, 22), new System.Drawing.Rectangle(188, 66, 35, 22),
                    new System.Drawing.Rectangle(0, 40, 43, 26), new System.Drawing.Rectangle(43, 40, 145, 26),new System.Drawing.Rectangle(188, 40, 35, 26),
                }
            };
        System.Drawing.Bitmap bmp;
        if (type == 1)
        {
            Stream strm = GetType().Assembly.GetManifestResourceStream(name_space + "green_right_mess.png");
            bmp = System.Drawing.Image.FromStream(strm) as System.Drawing.Bitmap;
        }
        else
        {
            Stream strm = GetType().Assembly.GetManifestResourceStream(name_space + "green_left_mess.png");
            bmp = System.Drawing.Image.FromStream(strm) as System.Drawing.Bitmap;
        }
        System.Drawing.Bitmap TopLeft = bmp.Clone(rect[type, 0], System.Drawing.Imaging.PixelFormat.DontCare);
        System.Drawing.Bitmap MidTop = bmp.Clone(rect[type, 1], System.Drawing.Imaging.PixelFormat.DontCare);
        System.Drawing.Bitmap TopRight = bmp.Clone(rect[type, 2], System.Drawing.Imaging.PixelFormat.DontCare);
        MidTop = ResetImageSize(MidTop, _width, MidTop.Height) as System.Drawing.Bitmap;
        System.Drawing.Bitmap BottomLeft = bmp.Clone(rect[type, 3], System.Drawing.Imaging.PixelFormat.DontCare);
        System.Drawing.Bitmap MidBottom = bmp.Clone(rect[type, 4], System.Drawing.Imaging.PixelFormat.DontCare);
        System.Drawing.Bitmap BottomRight = bmp.Clone(rect[type, 5], System.Drawing.Imaging.PixelFormat.DontCare);
        MidBottom = ResetImageSize(MidBottom, _width, MidBottom.Height) as System.Drawing.Bitmap;
        System.Drawing.Bitmap CentralLeft = bmp.Clone(rect[type, 6], System.Drawing.Imaging.PixelFormat.DontCare);
        System.Drawing.Bitmap MidCentral = bmp.Clone(rect[type, 7], System.Drawing.Imaging.PixelFormat.DontCare);
        System.Drawing.Bitmap CentralRight = bmp.Clone(rect[type, 8], System.Drawing.Imaging.PixelFormat.DontCare);
        CentralLeft = ResetImageSize(CentralLeft, CentralLeft.Width, _height) as System.Drawing.Bitmap;
        MidCentral = ResetImageSize(MidCentral, MidCentral.Width, _height) as System.Drawing.Bitmap;
        MidCentral = ResetImageSize(MidCentral, _width, _height) as System.Drawing.Bitmap;
        CentralRight = ResetImageSize(CentralRight, CentralRight.Width, _height) as System.Drawing.Bitmap;
        System.Drawing.Bitmap bmp_block = new System.Drawing.Bitmap(_width + 78, _height + 66);
        System.Drawing.Graphics gblock = System.Drawing.Graphics.FromImage(bmp_block);
        gblock.DrawImage(TopLeft, 0, 0, TopLeft.Width, TopLeft.Height);
        gblock.DrawImage(MidTop, TopLeft.Width, 0, MidTop.Width, MidTop.Height);
        gblock.DrawImage(TopRight, TopLeft.Width + MidTop.Width, 0, TopRight.Width, TopRight.Height);
        gblock.DrawImage(CentralLeft, 0, TopLeft.Height, CentralLeft.Width, CentralLeft.Height);
        gblock.DrawImage(MidCentral, CentralLeft.Width, MidTop.Height, MidCentral.Width, MidCentral.Height);
        gblock.DrawImage(CentralRight, CentralLeft.Width + MidCentral.Width, TopRight.Height, CentralRight.Width, CentralRight.Height);
        gblock.DrawImage(BottomLeft, 0, TopLeft.Height + CentralLeft.Height, BottomLeft.Width, BottomLeft.Height);
        gblock.DrawImage(MidBottom, BottomLeft.Width, TopLeft.Height + CentralLeft.Height, MidBottom.Width, MidBottom.Height);
        gblock.DrawImage(BottomRight, BottomLeft.Width + MidBottom.Width, TopLeft.Height + CentralLeft.Height, BottomRight.Width, BottomRight.Height);

        return BitmapToImageSource(bmp_block);
    }

    private static bool ImageCallback()
    {
        return false;
    }

    /// <summary>
    /// 图像缩放
    /// </summary>
    /// <param name="AtImage">图像</param>
    /// <param name="ImageWidth">宽度</param>
    /// <param name="ImageHeight">高度</param>
    /// <returns></returns>
    private System.Drawing.Image ResetImageSize(System.Drawing.Image AtImage, int ImageWidth, int ImageHeight)
    {
        if (AtImage == null) return null;
        System.Drawing.Image.GetThumbnailImageAbort imgCallback = new System.Drawing.Image.GetThumbnailImageAbort(ImageCallback);
        System.Drawing.Image Img = null;
        Img = AtImage.GetThumbnailImage(ImageWidth, ImageHeight, imgCallback, IntPtr.Zero);
        return Img;
    }

    /// <summary>
    /// 转换bitmap 为 BitmapImage
    /// </summary>
    /// <param name="bitmap">bitmap位图</param>
    /// <returns></returns>
    public BitmapImage BitmapToImageSource(System.Drawing.Bitmap bitmap)
    {
        using (MemoryStream memory = new MemoryStream())
        {
            bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
            memory.Position = 0;
            BitmapImage bitmapimage = new BitmapImage();
            bitmapimage.BeginInit();
            bitmapimage.StreamSource = memory;
            bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapimage.EndInit();
            return bitmapimage;
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值