【问题标题】:RichTextBox replace string with emoticon / imageRichTextBox 用表情/图像替换字符串
【发布时间】:2017-08-04 07:22:44
【问题描述】:

在 RichtTextBox 中,我想用表情图像自动替换表情字符串(例如 :D)。 到目前为止,我已经成功了,除了当我在现有单词/字符串之间写入表情符号字符串时,图像会插入到行尾。

例如: hello (inserting :D here) this is a message
结果是: hello this is a message ☺

另一个(小)问题是,插入后插入符号的位置设置在图像之前。

这是我已经得到的:

public class Emoticon
{
    public Emoticon(string key, Bitmap bitmap)
    {
        Key = key;
        Bitmap = bitmap;
        BitmapImage = bitmap.ToBitmapImage();
    }

    public string Key { get; }
    public Bitmap Bitmap { get; }
    public BitmapImage BitmapImage { get; }
}

public class EmoticonRichTextBox : RichTextBox
{
    private readonly List<Emoticon> _emoticons;

    public EmoticonRichTextBox()
    {
        _emoticons = new List<Emoticon>
        {
            new Emoticon(":D", Properties.Resources.grinning_face)
        };
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);
        Dispatcher.InvokeAsync(Look);
    }

    private void Look()
    {
        const string keyword = ":D";

        var text = new TextRange(Document.ContentStart, Document.ContentEnd);
        var current = text.Start.GetInsertionPosition(LogicalDirection.Forward);

        while (current != null)
        {
            var textInRun = current.GetTextInRun(LogicalDirection.Forward);
            if (!string.IsNullOrWhiteSpace(textInRun))
            {
                var index = textInRun.IndexOf(keyword, StringComparison.Ordinal);
                if (index != -1)
                {
                    var selectionStart = current.GetPositionAtOffset(index, LogicalDirection.Forward);
                    if (selectionStart == null)
                        continue;

                    var selectionEnd = selectionStart.GetPositionAtOffset(keyword.Length, LogicalDirection.Forward);
                    var selection = new TextRange(selectionStart, selectionEnd) { Text = string.Empty };

                    var emoticon = _emoticons.FirstOrDefault(x => x.Key.Equals(keyword));
                    if (emoticon == null)
                        continue;

                    var image = new System.Windows.Controls.Image
                    {
                        Source = emoticon.BitmapImage,
                        Height = 18,
                        Width = 18,
                        Margin = new Thickness(0, 3, 0, 0)
                    };

                    // inserts at the end of the line
                    selection.Start?.Paragraph?.Inlines.Add(image);

                    // doesn't work
                    CaretPosition = CaretPosition.GetPositionAtOffset(1, LogicalDirection.Forward);
                }
            }

            current = current.GetNextContextPosition(LogicalDirection.Forward);
        }
    }
}

public static class BitmapExtensions
{
    public static BitmapImage ToBitmapImage(this Bitmap bitmap)
    {
        using (var stream = new MemoryStream())
        {
            bitmap.Save(stream, ImageFormat.Png);
            stream.Position = 0;

            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.DecodePixelHeight = 18;
            image.DecodePixelWidth = 18;
            image.StreamSource = stream;
            image.EndInit();
            image.Freeze();

            return image;
        }
    }
}

【问题讨论】:

  • 澄清一下,如果您使用此文本框文本运行:This is :D a message,您会得到:This is a message ☺
  • @IanH。没错,就是这个问题
  • @rmbq 那是关于 WinForms 的。
  • @rmbq 这个不重复,因为另一个问题是关于WinForms里的RichTextBox,差别很大

标签: c# wpf richtextbox emoticons


【解决方案1】:

故障线路是selection.Start?.Paragraph?.Inlines.Add(image);。您将图像附加到段落的末尾。您应该使用InsertBeforeInsertAfter 方法之一。

但是要使用这些方法,您应该遍历内联并找到要在之前或之后插入的正确内联。这并不难。您可以通过将selectionStartselectionEnd 与内联的ElementStartElementEnd 属性进行比较来确定内联。

另一个棘手的可能性是您要插入的位置可能位于内联中。然后您应该拆分该内联并创建另外三个:

  • 一个包含插入位置之前的元素
  • 一个包含图像的
  • 一个包含插入位置之后的元素。

然后,您可以删除内联并将新的三个内联插入到适当的位置。

Wpf 的 RichTextBox 没有最漂亮的 API。有时可能很难处理。还有另一个控件称为AvalonEdit。它比 RichTextBox 更容易使用。你可以考虑一下。

【讨论】:

    【解决方案2】:

    正如@Yusuf Tarık Günaydın 建议的那样,我寻找了AvalonEdit 控件,它相当容易做到这一点。

    example 的帮助下,我只需要创建一个VisualLineElementGenerator 来查找表情匹配并插入图像。

    public static class BitmapExtensions
    {
        public static BitmapImage ToBitmapImage(this Bitmap bitmap)
        {
            using (var stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Png);
                stream.Position = 0;
    
                var image = new BitmapImage();
                image.BeginInit();
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.DecodePixelHeight = 18;
                image.DecodePixelWidth = 18;
                image.StreamSource = stream;
                image.EndInit();
                image.Freeze();
    
                return image;
            }
        }
    }
    
    public class Emoticon
    {
        public Emoticon(string key, Bitmap bitmap)
        {
            Key = key;
            Bitmap = bitmap;
            BitmapImage = bitmap.ToBitmapImage();
        }
    
        public string Key { get; }
        public Bitmap Bitmap { get; }
        public BitmapImage BitmapImage { get; }
    }
    
    public class EmoticonTextBox : TextEditor
    {
        public EmoticonTextBox()
        {
            HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
            VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
    
            TextArea.TextView.ElementGenerators.Add(new ImageElementGenerator());
        }
    }
    
    public class ImageElementGenerator : VisualLineElementGenerator
    {
        // To use this class:
        // textEditor.TextArea.TextView.ElementGenerators.Add(new ImageElementGenerator());
    
        private static readonly Regex ImageRegex = new Regex(@":D", RegexOptions.IgnoreCase);
    
        private readonly List<Emoticon> _emoticons;
    
        public ImageElementGenerator()
        {
            _emoticons = new List<Emoticon>
            {
                new Emoticon(":D", Properties.Resources.grinning_face)
            };
        }
    
        private Match FindMatch(int startOffset)
        {
            var endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
            var relevantText = CurrentContext.Document.GetText(startOffset, endOffset - startOffset);
    
            return ImageRegex.Match(relevantText);
        }
    
        public override int GetFirstInterestedOffset(int startOffset)
        {
            var match = FindMatch(startOffset);
            return match.Success ? startOffset + match.Index : -1;
        }
    
        public override VisualLineElement ConstructElement(int offset)
        {
            var match = FindMatch(offset);
            if (!match.Success || match.Index != 0)
                return null;
    
            var key = match.Groups[0].Value;
            var emoticon = _emoticons.FirstOrDefault(x => x.Key.Equals(key));
    
            var bitmap = emoticon?.BitmapImage;
            if (bitmap == null)
                return null;
    
            var image = new System.Windows.Controls.Image
            {
                Source = bitmap,
                Width = bitmap.PixelWidth,
                Height = bitmap.PixelHeight
            };
    
            return new InlineObjectElement(match.Length, image);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-18
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      • 2022-06-12
      相关资源
      最近更新 更多