做聊天消息时,需要用到文字和表情混合的TextBlock,经过一番百度。。终于实现了,不容易,今天就来分享下。

先看下效果,输入框和textblock绑定了同一个属性,懂的人自然懂:

注:此文较长,代码难度较大,还请您耐心看

话不多说,下面就看看如何实现:

首先,图片表情资源什么的,网上很多,自行下载即可。

为了管理我们添加的图片资源 ,新建两个类GifFace和SysFaces:


     public class GifFace
    {
    public string Name { get; set; }
    public string ImageName { get; set; }

    }
    public class SysFaces
    {
    public static GifFace 微笑 = new GifFace() { Name = "微笑", ImageName = "Expression_1@2x.png" };
    public static GifFace 撇嘴 = new GifFace() { Name = "撇嘴", ImageName = "Expression_2@2x.png" };
    public static GifFace 色色 = new GifFace() { Name = "色色", ImageName = "Expression_3@2x.png" };
    public static GifFace 发呆 = new GifFace() { Name = "发呆", ImageName = "Expression_4@2x.png" };
    public static GifFace 得意 = new GifFace() { Name = "得意", ImageName = "Expression_5@2x.png" };
    public static GifFace 流泪 = new GifFace() { Name = "流泪", ImageName = "Expression_6@2x.png" };
    public static GifFace 害羞 = new GifFace() { Name = "害羞", ImageName = "Expression_7@2x.png" };
    public static GifFace 闭嘴 = new GifFace() { Name = "闭嘴", ImageName = "Expression_8@2x.png" };
    public static GifFace 睡觉 = new GifFace() { Name = "睡觉", ImageName = "Expression_9@2x.png" };
    public static GifFace 大哭 = new GifFace() { Name = "大哭", ImageName = "Expression_10@2x.png" };
    public static GifFace 尴尬 = new GifFace() { Name = "尴尬", ImageName = "Expression_11@2x.png" };
    public static GifFace 发怒 = new GifFace() { Name = "发怒", ImageName = "Expression_12@2x.png" };
    public static GifFace 调皮 = new GifFace() { Name = "调皮", ImageName = "Expression_13@2x.png" };
    public static GifFace 呲牙 = new GifFace() { Name = "呲牙", ImageName = "Expression_14@2x.png" };
    public static GifFace 惊讶 = new GifFace() { Name = "惊讶", ImageName = "Expression_15@2x.png" };
    public static GifFace 难过 = new GifFace() { Name = "难过", ImageName = "Expression_16@2x.png" };
    public static GifFace 酷酷 = new GifFace() { Name = "酷酷", ImageName = "Expression_17@2x.png" };
    public static GifFace 冷汗 = new GifFace() { Name = "冷汗", ImageName = "Expression_18@2x.png" };
    public static GifFace 抓狂 = new GifFace() { Name = "抓狂", ImageName = "Expression_19@2x.png" };
    public static GifFace 呕吐 = new GifFace() { Name = "呕吐", ImageName = "Expression_20@2x.png" };
    public static GifFace 偷笑 = new GifFace() { Name = "偷笑", ImageName = "Expression_21@2x.png" };
    public static GifFace 愉快 = new GifFace() { Name = "愉快", ImageName = "Expression_22@2x.png" };
    public static GifFace 白眼 = new GifFace() { Name = "白眼", ImageName = "Expression_23@2x.png" };
    public static GifFace 傲慢 = new GifFace() { Name = "傲慢", ImageName = "Expression_24@2x.png" };
    public static GifFace 饥饿 = new GifFace() { Name = "饥饿", ImageName = "Expression_25@2x.png" };
    public static GifFace 困倦 = new GifFace() { Name = "困倦", ImageName = "Expression_26@2x.png" };
    public static GifFace 惊恐 = new GifFace() { Name = "惊恐", ImageName = "Expression_27@2x.png" };
    public static GifFace 流汗 = new GifFace() { Name = "流汗", ImageName = "Expression_28@2x.png" };
    public static GifFace 憨笑 = new GifFace() { Name = "憨笑", ImageName = "Expression_29@2x.png" };
    public static GifFace 悠闲 = new GifFace() { Name = "悠闲", ImageName = "Expression_30@2x.png" };
    public static GifFace 奋斗 = new GifFace() { Name = "奋斗", ImageName = "Expression_31@2x.png" };
    public static GifFace 咒骂 = new GifFace() { Name = "咒骂", ImageName = "Expression_32@2x.png" };
    public static GifFace 疑问 = new GifFace() { Name = "疑问", ImageName = "Expression_33@2x.png" };
    public static GifFace 嘘嘘 = new GifFace() { Name = "嘘嘘", ImageName = "Expression_34@2x.png" };
    public static GifFace 晕倒 = new GifFace() { Name = "晕倒", ImageName = "Expression_35@2x.png" };
    public static GifFace 疯了 = new GifFace() { Name = "疯了", ImageName = "Expression_36@2x.png" };
    public static GifFace 衰人 = new GifFace() { Name = "衰人", ImageName = "Expression_37@2x.png" };
    public static GifFace 骷髅 = new GifFace() { Name = "骷髅", ImageName = "Expression_38@2x.png" };
    public static GifFace 敲打 = new GifFace() { Name = "敲打", ImageName = "Expression_39@2x.png" };
    public static GifFace 再见 = new GifFace() { Name = "再见", ImageName = "Expression_40@2x.png" };
    public static GifFace 擦汗 = new GifFace() { Name = "擦汗", ImageName = "Expression_41@2x.png" };
    public static GifFace 抠鼻 = new GifFace() { Name = "抠鼻", ImageName = "Expression_42@2x.png" };
    public static GifFace 鼓掌 = new GifFace() { Name = "鼓掌", ImageName = "Expression_43@2x.png" };
    public static GifFace 糗了 = new GifFace() { Name = "糗了", ImageName = "Expression_44@2x.png" };
    public static GifFace 坏笑 = new GifFace() { Name = "坏笑", ImageName = "Expression_45@2x.png" };
    public static GifFace 左哼 = new GifFace() { Name = "左哼", ImageName = "Expression_46@2x.png" };
    public static GifFace 右哼 = new GifFace() { Name = "右哼", ImageName = "Expression_47@2x.png" };
    public static GifFace 哈欠 = new GifFace() { Name = "哈欠", ImageName = "Expression_48@2x.png" };
    public static GifFace 鄙视 = new GifFace() { Name = "鄙视", ImageName = "Expression_49@2x.png" };
    public static GifFace 委屈 = new GifFace() { Name = "委屈", ImageName = "Expression_50@2x.png" };
    public static GifFace 快哭 = new GifFace() { Name = "快哭", ImageName = "Expression_51@2x.png" };
    public static GifFace 阴险 = new GifFace() { Name = "阴险", ImageName = "Expression_52@2x.png" };
    public static GifFace 亲亲 = new GifFace() { Name = "亲亲", ImageName = "Expression_53@2x.png" };
    public static GifFace 惊吓 = new GifFace() { Name = "惊吓", ImageName = "Expression_54@2x.png" };
    public static GifFace 可怜 = new GifFace() { Name = "可怜", ImageName = "Expression_55@2x.png" };
    public static GifFace 菜刀 = new GifFace() { Name = "菜刀", ImageName = "Expression_56@2x.png" };
    public static GifFace 西瓜 = new GifFace() { Name = "西瓜", ImageName = "Expression_57@2x.png" };
    public static GifFace 啤酒 = new GifFace() { Name = "啤酒", ImageName = "Expression_58@2x.png" };
    public static GifFace 篮球 = new GifFace() { Name = "篮球", ImageName = "Expression_59@2x.png" };
    public static GifFace 乒乓 = new GifFace() { Name = "乒乓", ImageName = "Expression_60@2x.png" };
    public static GifFace 咖啡 = new GifFace() { Name = "咖啡", ImageName = "Expression_61@2x.png" };
    public static GifFace 米饭 = new GifFace() { Name = "米饭", ImageName = "Expression_62@2x.png" };
    public static GifFace 猪头 = new GifFace() { Name = "猪头", ImageName = "Expression_63@2x.png" };
    public static GifFace 玫瑰 = new GifFace() { Name = "玫瑰", ImageName = "Expression_64@2x.png" };
    public static GifFace 凋谢 = new GifFace() { Name = "凋谢", ImageName = "Expression_65@2x.png" };
    public static GifFace 嘴唇 = new GifFace() { Name = "嘴唇", ImageName = "Expression_66@2x.png" };
    public static GifFace 爱心 = new GifFace() { Name = "爱心", ImageName = "Expression_67@2x.png" };
    public static GifFace 心碎 = new GifFace() { Name = "心碎", ImageName = "Expression_68@2x.png" };
    public static GifFace 蛋糕 = new GifFace() { Name = "蛋糕", ImageName = "Expression_69@2x.png" };
    public static GifFace 闪电 = new GifFace() { Name = "闪电", ImageName = "Expression_70@2x.png" };
    public static GifFace 炸弹 = new GifFace() { Name = "炸弹", ImageName = "Expression_71@2x.png" };
    public static GifFace 刀刀 = new GifFace() { Name = "刀刀", ImageName = "Expression_72@2x.png" };
    public static GifFace 足球 = new GifFace() { Name = "足球", ImageName = "Expression_73@2x.png" };
    public static GifFace 瓢虫 = new GifFace() { Name = "瓢虫", ImageName = "Expression_74@2x.png" };
    public static GifFace 便便 = new GifFace() { Name = "便便", ImageName = "Expression_75@2x.png" };
    public static GifFace 月亮 = new GifFace() { Name = "月亮", ImageName = "Expression_76@2x.png" };
    public static GifFace 太阳 = new GifFace() { Name = "太阳", ImageName = "Expression_77@2x.png" };
    public static GifFace 礼物 = new GifFace() { Name = "礼物", ImageName = "Expression_78@2x.png" };
    public static GifFace 拥抱 = new GifFace() { Name = "拥抱", ImageName = "Expression_79@2x.png" };
    public static GifFace 强强 = new GifFace() { Name = "强强", ImageName = "Expression_80@2x.png" };
    public static GifFace 弱弱 = new GifFace() { Name = "弱弱", ImageName = "Expression_81@2x.png" };
    public static GifFace 握手 = new GifFace() { Name = "握手", ImageName = "Expression_82@2x.png" };
    public static GifFace 胜利 = new GifFace() { Name = "胜利", ImageName = "Expression_83@2x.png" };
    public static GifFace 抱拳 = new GifFace() { Name = "抱拳", ImageName = "Expression_84@2x.png" };
    public static GifFace 勾引 = new GifFace() { Name = "勾引", ImageName = "Expression_85@2x.png" };
    public static GifFace 拳头 = new GifFace() { Name = "拳头", ImageName = "Expression_86@2x.png" };
    public static GifFace 差劲 = new GifFace() { Name = "差劲", ImageName = "Expression_87@2x.png" };
    public static GifFace 爱你 = new GifFace() { Name = "爱你", ImageName = "Expression_88@2x.png" };
    public static GifFace NO = new GifFace() { Name = "NO", ImageName = "Expression_89@2x.png" };
    public static GifFace OK = new GifFace() { Name = "OK", ImageName = "Expression_90@2x.png" };
    public static GifFace 扫地 = new GifFace() { Name = "扫地", ImageName = "Expression_91@2x.gif" };

    public static List<GifFace> Faces { get; set; } = new List<GifFace>()
    {
    微笑,撇嘴,色色,发呆,得意,流泪,害羞,闭嘴,睡觉,大哭,尴尬,发怒,调皮,呲牙,惊讶,难过,酷酷,冷汗,抓狂,呕吐,偷笑,愉快,
    白眼,傲慢,饥饿,困倦,惊恐,流汗,憨笑,悠闲,奋斗,咒骂,疑问,嘘嘘,晕倒,疯了,衰人,骷髅,敲打,再见,擦汗,抠鼻,鼓掌,糗了,
    坏笑,左哼,右哼,哈欠,鄙视,委屈,快哭,阴险,亲亲,惊吓,可怜,菜刀,西瓜,啤酒,篮球,乒乓,咖啡,米饭,猪头,玫瑰,凋谢,嘴唇,
    爱心,心碎,蛋糕,闪电,炸弹,刀刀,足球,瓢虫,便便,月亮,太阳,礼物,拥抱,强强,弱弱,握手,胜利,抱拳,勾引,拳头,
    差劲,爱你,NO,OK,扫地
    };

    }

    新建一个图片子类,实现Gif图片:



      using System;
      using System.Collections.Generic;
      using System.IO;
      using System.Linq;
      using System.Net;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows.Media;
      using System.Windows.Threading;
      using System.Windows;
      using System.Windows.Controls;

      namespace WpfDemo
      {
      public class ImageExpender : Image
      {
      string imageLocation;
      public string ImageLocation
      {
      get
      {
      return imageLocation;
      }
      set
      {
      if (imageLocation != value)
      {
      imageLocation = value;

      try
      {
      Load();
      }
      catch (Exception)
      { }
      }
      }
      }

      private String location;
      /// <summary>
      /// 图像路径
      /// </summary>
      public String Location
      {
      get { return location; }
      set { location = value; }
      }

      public System.Drawing.Image Image
      {
      get { return (System.Drawing.Image)GetValue(ImageProperty); }
      set { SetValue(ImageProperty, value); }
      }

      //使用 DependencyProperty 作为后备存储的图像。这使动画、 样式、 绑定等......
      public static readonly DependencyProperty ImageProperty =
      DependencyProperty.Register("Image", typeof(System.Drawing.Image), typeof(ImageExpender), new UIPropertyMetadata(null, new PropertyChangedCallback(ImagePropertyChanged)));

      private static void ImagePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
      ImageExpender imageExpender = d as ImageExpender;

      System.Drawing.Image oldImage = e.OldValue as System.Drawing.Image;
      System.Drawing.Image newImage = e.NewValue as System.Drawing.Image;

      imageExpender.StopAnimate(oldImage);
      imageExpender.Animate(newImage);
      imageExpender.RefreshImageSource();
      }
      bool currentlyAnimating = false;
      #region Animate

      private void Animate(bool animate, System.Drawing.Image image)
      {
      if (animate != this.currentlyAnimating)
      {
      if (animate)
      {
      if (image != null)
      {
      ImageAnimatiorExpender.Animate(image, new EventHandler(this.OnFrameChanged));
      this.currentlyAnimating = animate;
      }
      }
      else
      {
      if (image != null)
      {
      ImageAnimatiorExpender.StopAnimate(image, new EventHandler(this.OnFrameChanged));
      this.currentlyAnimating = animate;
      }
      }
      }
      }

      private void StopAnimate(System.Drawing.Image image)
      {
      Animate(false, image);
      }

      private void Animate(System.Drawing.Image image)
      {
      Animate(IsEnabled && Visibility == Visibility.Visible, image);
      }

      #endregion

      private void OnFrameChanged(object o, EventArgs e)
      {
      this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new System.Threading.ThreadStart(RefreshImageSource));
      }

      private void RefreshImageSource()
      {
      if (this.Image != null && this.Visibility == Visibility.Visible)
      {
      ImageAnimatiorExpender.UpdateFrames(this.Image);
      ImageSource imageSource = ImageAnimatiorExpender.GetImageSource(this.Image);

      if (imageSource == null)
      {
      IntPtr ip = (this.Image as System.Drawing.Bitmap).GetHbitmap();
      imageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
      ip, IntPtr.Zero, System.Windows.Int32Rect.Empty,
      System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
      }

      this.Source = imageSource;
      }
      }

      #region 辅助方法

      private Uri CalculateUri(string path)
      {
      Uri uri;
      try
      {
      uri = new Uri(path);
      }
      catch (UriFormatException)
      {
      // It's a relative pathname, get its full path as a file.
      path = Path.GetFullPath(path);
      uri = new Uri(path);
      }
      return uri;
      }

      public void Load()
      {
      if (ImageLocation == null || ImageLocation.Length == 0)
      {
      return;
      }
      System.Drawing.Image img = null;
      try
      {
      Uri uri = CalculateUri(ImageLocation);
      if (uri.IsFile)
      {
      using (StreamReader reader = new StreamReader(uri.LocalPath))
      {
      //img = System.Drawing.Image.FromStream(reader.BaseStream);
      img = new System.Drawing.Bitmap(uri.LocalPath);
      }
      }
      else
      {
      using (WebClient wc = new WebClient())
      {
      using (Stream s = wc.OpenRead(uri.ToString()))
      {
      img = System.Drawing.Bitmap.FromStream(s);
      }
      }
      }
      }
      catch
      {

      }
      this.Image = img;
      }

      #endregion
      }
      }

      新建一个gif动画支持类:



        using System;
        using System.Collections.Generic;
        using System.Diagnostics;
        using System.Diagnostics.CodeAnalysis;
        using System.Drawing;
        using System.Drawing.Imaging;
        using System.Threading;
        using System.Windows.Media;
        namespace WpfDemo
        {
        public class ImageAnimatiorExpender
        {

        static List<ImageInfoExpender> imageInfoList;

        static bool anyFrameDirty;


        static Thread animationThread;

        static ReaderWriterLock rwImgListLock = new ReaderWriterLock();

        [ThreadStatic]
        static int threadWriterLockWaitCount;


        private ImageAnimatiorExpender()
        {
        }


        public static void UpdateFrames(Image image)
        {
        if (!anyFrameDirty || image == null || imageInfoList == null)
        {
        return;
        }
        if (threadWriterLockWaitCount > 0)
        {
        return;
        }
        rwImgListLock.AcquireReaderLock(Timeout.Infinite);
        try
        {
        bool foundDirty = false;
        bool foundImage = false;
        foreach (ImageInfoExpender imageInfo in imageInfoList)
        {
        if (imageInfo.Image == image)
        {
        if (imageInfo.FrameDirty)
        {
        lock (imageInfo.Image)
        {
        imageInfo.UpdateFrame();
        }
        }
        foundImage = true;
        }

        if (imageInfo.FrameDirty)
        {
        foundDirty = true;
        }

        if (foundDirty && foundImage)
        {
        break;
        }
        }

        anyFrameDirty = foundDirty;
        }
        finally
        {
        rwImgListLock.ReleaseReaderLock();
        }
        }
        public static ImageSource GetImageSource(Image image)
        {
        ImageSource imageSource = null;

        if (imageInfoList != null)
        {
        foreach (ImageInfoExpender imageInfo in imageInfoList)
        {
        if (imageInfo.Image == image)
        {
        imageSource = imageInfo.GetCurrentImageSource();
        break;
        }

        }
        }

        return imageSource;
        }

        public static void Animate(Image image, EventHandler onFrameChangedHandler)
        {
        if (image == null)
        {
        return;
        }

        ImageInfoExpender imageInfo = null;
        lock (image)
        {
        imageInfo = new ImageInfoExpender(image);
        }
        StopAnimate(image, onFrameChangedHandler);

        bool readerLockHeld = rwImgListLock.IsReaderLockHeld;
        LockCookie lockDowngradeCookie = new LockCookie();

        threadWriterLockWaitCount++;

        try
        {
        if (readerLockHeld)
        {
        lockDowngradeCookie = rwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
        }
        else
        {
        rwImgListLock.AcquireWriterLock(Timeout.Infinite);
        }
        }
        finally
        {
        threadWriterLockWaitCount--;
        Debug.Assert(threadWriterLockWaitCount >= 0, "threadWriterLockWaitCount less than zero.");
        }

        try
        {
        if (imageInfo.Animated)
        {
        if (imageInfoList == null)
        {
        imageInfoList = new List<ImageInfoExpender>();
        }
        imageInfo.FrameChangedHandler = onFrameChangedHandler;
        imageInfoList.Add(imageInfo);
        if (animationThread == null)
        {
        animationThread = new Thread(new ThreadStart(AnimateImages50ms));
        animationThread.Name = typeof(ImageAnimator).Name;
        animationThread.IsBackground = true;
        animationThread.Start();
        }
        }
        }
        finally
        {
        if (readerLockHeld)
        {
        rwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
        }
        else
        {
        rwImgListLock.ReleaseWriterLock();
        }
        }
        }

        public static bool CanAnimate(Image image)
        {
        if (image == null)
        {
        return false;
        }

        lock (image)
        {
        Guid[] dimensions = image.FrameDimensionsList;

        foreach (Guid guid in dimensions)
        {
        FrameDimension dimension = new FrameDimension(guid);
        if (dimension.Equals(FrameDimension.Time))
        {
        return image.GetFrameCount(FrameDimension.Time) > 1;
        }
        }
        }

        return false;
        }

        public static void StopAnimate(Image image, EventHandler onFrameChangedHandler)
        {
        if (image == null || imageInfoList == null)
        {
        return;
        }

        bool readerLockHeld = rwImgListLock.IsReaderLockHeld;
        LockCookie lockDowngradeCookie = new LockCookie();

        threadWriterLockWaitCount++;

        try
        {
        if (readerLockHeld)
        {
        lockDowngradeCookie = rwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
        }
        else
        {
        rwImgListLock.AcquireWriterLock(Timeout.Infinite);
        }
        }
        finally
        {
        threadWriterLockWaitCount--;
        Debug.Assert(threadWriterLockWaitCount >= 0, "threadWriterLockWaitCount less than zero.");
        }

        try
        {
        for (int i = 0; i < imageInfoList.Count; i++)
        {
        ImageInfoExpender imageInfo = imageInfoList[i];

        if (image == imageInfo.Image)
        {
        if ((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler)))
        {
        imageInfoList.Remove(imageInfo);
        }
        break;
        }
        }
        }
        finally
        {
        if (readerLockHeld)
        {
        rwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
        }
        else
        {
        rwImgListLock.ReleaseWriterLock();
        }
        }
        }

        [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
        static void AnimateImages50ms()
        {
        Debug.Assert(imageInfoList != null, "Null images list");

        while (true)
        {
        rwImgListLock.AcquireReaderLock(Timeout.Infinite);
        try
        {
        for (int i = 0; i < imageInfoList.Count; i++)
        {
        ImageInfoExpender imageInfo = imageInfoList[i];

        imageInfo.FrameTimer += 5;
        if (imageInfo.FrameTimer >= imageInfo.FrameDelay(imageInfo.Frame))
        {
        imageInfo.FrameTimer = 0;

        if (imageInfo.Frame + 1 < imageInfo.FrameCount)
        {
        imageInfo.Frame++;
        }
        else
        {
        imageInfo.Frame = 0;
        }

        if (imageInfo.FrameDirty)
        {
        anyFrameDirty = true;
        }
        }
        }
        }
        finally
        {
        rwImgListLock.ReleaseReaderLock();
        }

        Thread.Sleep(50);
        }
        }
        }
        }

        新建一个ImageInfoExpender类:



          using System;
          using System.Diagnostics;
          using System.Drawing;
          using System.Drawing.Imaging;
          using System.Windows.Media;

          namespace WpfDemo
          {
          public class ImageSourceExpender
          {
          public int DelayTime { set; get; }
          public ImageSource Source { set; get; }
          public ImageSourceExpender(ImageSource source, int delayTime)
          {
          this.Source = source;
          this.DelayTime = delayTime;
          }
          }
          public class ImageInfoExpender
          {
          const int PropertyTagFrameDelay = 0x5100;

          Image image;
          int frame;
          int frameCount;
          bool frameDirty;
          bool animated;
          EventHandler onFrameChangedHandler;
          int frameTimer;
          ImageSourceExpender[] ImageSourceList = null;
          public ImageInfoExpender(Image image)
          {
          this.image = image;
          animated = ImageAnimator.CanAnimate(image);

          if (animated)
          {
          frameCount = image.GetFrameCount(FrameDimension.Time);
          ImageSourceList = new ImageSourceExpender[frameCount];
          PropertyItem frameDelayItem = image.GetPropertyItem(PropertyTagFrameDelay);
          if (frameDelayItem != null)
          {
          byte[] values = frameDelayItem.Value;
          Debug.Assert(values.Length == 4 * FrameCount, "PropertyItem has invalid value byte array");
          for (int i = 0; i < FrameCount; ++i)
          {
          int delayTime = values[i * 4] + 256 * values[i * 4 + 1] + 256 * 256 * values[i * 4 + 2] + 256 * 256 * 256 * values[i * 4 + 3];
          image.SelectActiveFrame(FrameDimension.Time, i);

          System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(this.Image.Width, this.Image.Height);
          System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
          g.DrawImage(this.Image, new System.Drawing.Rectangle(0, 0, this.Image.Width, this.Image.Height));

          IntPtr ip = bitmap.GetHbitmap();
          ImageSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
          ip, IntPtr.Zero, System.Windows.Int32Rect.Empty,
          System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

          ImageSourceList[i] = new ImageSourceExpender(bitmapSource, delayTime);
          }
          }
          }
          else
          {
          frameCount = 1;
          }

          if (ImageSourceList == null)
          {
          ImageSourceList = new ImageSourceExpender[frameCount];
          }

          }
          public bool Animated
          {
          get
          {
          return animated;
          }
          }
          public int Frame
          {
          get
          {
          return frame;
          }
          set
          {
          if (frame != value)
          {
          if (value < 0 || value >= FrameCount)
          {
          throw (new Exception("错误"));
          }

          if (Animated)
          {
          frame = value;
          frameDirty = true;

          OnFrameChanged(EventArgs.Empty);
          }
          }
          }
          }
          public bool FrameDirty
          {
          get
          {
          return frameDirty;
          }
          }

          public EventHandler FrameChangedHandler
          {
          get
          {
          return onFrameChangedHandler;
          }
          set
          {
          onFrameChangedHandler = value;
          }
          }
          public int FrameCount
          {
          get
          {
          return frameCount;
          }
          }
          public int FrameDelay(int frame)
          {
          return ImageSourceList[frame].DelayTime;
          }
          internal int FrameTimer
          {
          get
          {
          return frameTimer;
          }
          set
          {
          frameTimer = value;
          }
          }
          internal Image Image
          {
          get
          {
          return image;
          }
          }
          public void UpdateFrame()
          {
          if (frameDirty)
          {
          frameDirty = false;
          }

          return;
          }

          public ImageSource GetCurrentImageSource()
          {
          return ImageSourceList[Frame].Source;
          }
          protected void OnFrameChanged(EventArgs e)
          {
          if (this.onFrameChangedHandler != null)
          {
          this.onFrameChangedHandler(image, e);
          }
          }
          }
          }

          最后

          新建一个用户控件 GTextBlock:



            <TextBlock x:Class="WpfDemo.GTextBlock"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            mc:Ignorable="d"
            />

            后台代码为:



              using System;
              using System.ComponentModel;
              using System.IO;
              using System.Linq;
              using System.Text.RegularExpressions;
              using System.Windows;
              using System.Windows.Controls;
              using System.Windows.Documents;
              using System.Windows.Media;

              namespace WpfDemo
              {
              public partial class GTextBlock : TextBlock
              {
              static GTextBlock()
              {
              TextProperty.OverrideMetadata(typeof(GTextBlock), new FrameworkPropertyMetadata(
              (string)TextBlock.TextProperty.GetMetadata(typeof(TextBlock)).DefaultValue,
              (o, e) => (o as GTextBlock).OnTextChanged(e.NewValue as string)));
              }
              private void OnTextChanged(string text)
              {
              Inlines.Clear();
              if (string.IsNullOrEmpty(text))
              return;
              string[] msgs = Regex.Split(text, @"(\[[^\][]+\])");
              foreach (var msg in msgs)
              {
              if (!string.IsNullOrEmpty(msg))
              {
              if (msg.StartsWith("[")&& msg.EndsWith("]"))
              {
              string name = msg.Substring(1, msg.Length - 2);
              GifFace face = SysFaces.Faces.Where(f => f.Name == name).FirstOrDefault();
              if (face != null)
              {
              InlineUIContainer iuc = new InlineUIContainer();
              var image = GegImageExpender(face);
              iuc.Child = image;
              Inlines.Add(iuc);
              continue;
              }
              }
              Inlines.Add(new Run(msg) { BaselineAlignment=BaselineAlignment.Center});
              }
              }
              }
              public ImageExpender GegImageExpender(GifFace face)
              {
              ImageExpender Img_Exp = new ImageExpender();
              Img_Exp.Stretch = Stretch.UniformToFill;
              Img_Exp.Visibility = Visibility.Visible;
              string facePath = $"{face.ImageName}";
              Stream imageStream = Application.GetResourceStream(new Uri("/faces/" + facePath, UriKind.Relative)).Stream;
              System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(imageStream);
              Img_Exp.Image = bitmap;
              Img_Exp.Location = "/faces/" + facePath;
              Img_Exp.Tag = face.Name;
              Img_Exp.Width = 50;
              Img_Exp.Height = 50;
              return Img_Exp;
              }
              public GTextBlock()
              {
              InitializeComponent();
              }
              public new string Text
              {
              get => m_text_dpd.GetValue(this) as string;
              set => m_text_dpd.SetValue(this, value);
              }
              public static new readonly DependencyProperty TextProperty =
              DependencyProperty.Register(nameof(Text), typeof(string), typeof(GTextBlock));
              private static readonly DependencyPropertyDescriptor m_text_dpd =
              DependencyPropertyDescriptor.FromProperty(TextProperty, typeof(GTextBlock));
              }
              }

              最后 ,新建一个窗口TextBlockDemo使用自定义控件:


                <Window x:Class="WpfDemo.TextBlockDemo"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:local="clr-namespace:WpfDemo"
                mc:Ignorable="d"
                x:Name="window"
                Title="ImageBlockDemo" Height="450" Width="800">
                <Grid Background="LightBlue">
                <StackPanel VerticalAlignment="Center"
                HorizontalAlignment="Center" >
                <local:GTextBlock Text="{Binding DemoText,ElementName=window}" FontSize="30" HorizontalAlignment="Center"/>
                <TextBox Width="400" Height="100" FontSize="30" FontFamily="微软雅黑"
                TextWrapping="Wrap"
                Text="{Binding DemoText,UpdateSourceTrigger=PropertyChanged,ElementName=window}"/>
                </StackPanel>
                </Grid>
                </Window>

                窗体后台代码为:


                  using System.ComponentModel;
                  using System.Windows;
                  namespace WpfDemo
                  {
                  public partial class TextBlockDemo : Window, INotifyPropertyChanged
                  {
                  public TextBlockDemo()
                  {
                  InitializeComponent();
                  }
                  public event PropertyChangedEventHandler PropertyChanged;
                  private string _demoText="[微笑]";
                  public string DemoText
                  {
                  get { return _demoText; }
                  set
                  {
                  _demoText = value;
                  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DemoText)));
                  }
                  }
                  }
                  }



                  至此,大功告成!

                  感谢您的观看。下面是效果截图:

                  WPF 制作支持图片和Gif的TextBlock_i++


                  如果喜欢,点个赞呗~