c# – RichTextBox用表情符号/图像替换字符串
阅读原文时间:2023年07月08日阅读:1

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

例如:
你好(在这里插入:D)这是一条消息
结果是:
你好,这是一条消息<<图片 另一个(微小的)问题是插入后的插入位置在插入之前设置. 这就是我已经得到的:

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 _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;  
    }  
}  

}

错误的行是选择.启动?.Paragraph?.Inlines.Add(image);.您将图像追加到段落的末尾.您应该使用InsertBefore或InsertAfter方法之一.

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

另一个棘手的可能性是您要插入的位置可能属于内联.然后你应该拆分内联并创建其他三个:

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

然后,您可以删除内联并将新的三个内联插入到正确的位置.

Wpf的RichTextBox没有最漂亮的API.有时可能很难使用.还有一个名为AvalonEdit的控件.它比RichTextBox更容易使用.你可能想要考虑一下.