WPF 微信 MVVM 【續】發送部分QQ表情

今天主要記錄的就是發送QQ表情, WPF 微信 MVVM裏寫了,後期爲了發送QQ表情,須要把TextBox替換爲RichTextBox,接下來就說說替換的過程。html

1、支持Binding的RichTextBox

RichTextBox雖然支持文字,圖片,連接,可是,原生的它不支持Binding,這個對於MVVM是很不方便的,所以,須要給RichTextBox設置一個依賴屬性Document,來讓它支持綁定,這樣才能繼續下一步。正則表達式

public class BindableRichTextBox : RichTextBox
    {
        public new FlowDocument Document
        {
            get { return (FlowDocument)GetValue(DocumentProperty); }
            set { SetValue(DocumentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Document.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DocumentProperty =
            DependencyProperty.Register("Document", typeof(FlowDocument), typeof(BindableRichTextBox), new FrameworkPropertyMetadata(null,new PropertyChangedCallback(OnDucumentChanged)));

        private static void OnDucumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RichTextBox rtb = (RichTextBox)d;
            rtb.Document = (FlowDocument)e.NewValue;
        }
    }
BindableRichTextBox

2、微信表情列表

作列表的方法寫在了Emoji選項列表這個博客裏,不過由於是針對微信來作,因此,仍是修改了一部分。微信

首先,右鍵查看微信網頁版的源文件,會看到它的表情列表大概是這個樣子的代碼,從裏面獲取title和class裏的內容,作成一個XML文件ide

而後,去網上找QQ的表情壓縮包,這個不是難事,而後根據class進行命名,最後使用Emoji選項列表裏的方法就能夠作成列表了,看下效果圖spa

3、String轉FlowDocument、FlowDocument轉String

網頁版微信,在F12下,發送一條消息,能夠看到發送的報文是「文字+[表情內容]」(指QQ的表情,Emoji如今沒看到規律),由於,在轉換的時候,也就有了目標code

一、FlowDocument轉換String

咱們在點擊發送按鈕以前,是將發送框裏的FlowDocument轉換成String發送出去的。htm

轉換過程是,循環FlowDocument裏面的Blocks(Paragraph都放在Blocks裏面),而後循環Paragraph裏的Inlines,判別裏面的內容是InlineUIContainer(Image)仍是Run(Text)blog

若是是圖片的話,則從Dictionary裏取得Key,放到要發送的報文裏,若是是文字的話,就直接放就能夠了遞歸

private string GetSendMessage(FlowDocument fld)
        {
            if (fld == null)
            {
                return string.Empty;
            }
            string resutStr = string.Empty;
            foreach (var root in fld.Blocks)
            {
                foreach (var item in ((Paragraph)root).Inlines)
                {
                    //若是是Emoji則進行轉換
                    if (item is InlineUIContainer)
                    {
                        System.Windows.Controls.Image img = (System.Windows.Controls.Image)((InlineUIContainer)item).Child;
                        resutStr += GetEmojiName(img.Source.ToString());
                    }
                    //若是是文本,則直接賦值
                    if (item is Run)
                    {
                        resutStr += ((Run)item).Text;
                    }
                }
            }
            return resutStr;
        }
FlowDocument轉String

二、String轉FlowDocument

接收消息的是,拿到的是String類型,可是顯示的時候要顯示成FlowDocument。圖片

由於咱們知道了消息的格式是XXXX[**]這種,所以,就選用正則表達式進行截取就能夠了。

首先,判斷字符串的第一位是否是「[」,若是是的話,則截取[]裏面的內容,轉換爲Image,而後將[**]從接收到的字符串上移除掉;

若是不是的話,則用正則表達式取得"["前面的值,做爲Text,而後,將取到的值從接收到的字符串上移除掉,整個過程進行遞歸,直到字符串的長度變爲0,跳出,將獲得的內容最後添加到FlowDocument裏

private void StrToFlDoc(string str,FlowDocument fld,Paragraph par)
        {
            //當遞歸結束之後,也就是長度爲0的時候,就跳出
            if (str.Length <= 0)
            {
                fld.Blocks.Add(par);
                return;
            }
            //若是字符串裏不存在[時,則直接添加內容
            if (!str.Contains('['))
            {
                par.Inlines.Add(new Run(str));
                str = str.Remove(0, str.Length);
                StrToFlDoc(str,fld, par);
            }
            else
            {
                //設置字符串長度
                int strLength = str.Length;
                //首先判斷第一位是否是[,若是是,則證實是表情,用正則獲取表情,而後將字符串長度進行移除,遞歸
                if (str[0].Equals('['))
                {
                    par.Inlines.Add(new InlineUIContainer(new System.Windows.Controls.Image { Width = 20, Height = 20, Source = ContantClass.EmojiCode[GetEmojiNameByRegex(str)] }));
                    str = str.Remove(0, GetEmojiNameByRegex(str).Length);
                    StrToFlDoc(str,fld, par);
                }
                else
                {//若是第一位不是[的話,則是字符串,直接添加進去
                    par.Inlines.Add(new Run(GetTextByRegex(str)));
                    str = str.Remove(0, GetTextByRegex(str).Length);
                    StrToFlDoc(str,fld, par);
                }
            }
        }
String轉FlowDocument

看下最終的效果圖

4、總結

在把Textbox替換爲RichTextBox過程當中,遇到了很多阻礙,不像剛開始想的那麼簡單。

好比,點選表情添加到RichTextbox中時,發現有的時候光標並不在當前添加完的表情後面,而是在前面,或者是隔了一個跳躍,研究了大半天,在網上找到了解決辦法。

var container=new InlineUIContainer(new Image { Source = EmojiTabControlUC.SelectEmoji.Value, Height = 20, Width = 20 }, rtb.CaretPosition);
rtb.CaretPosition = container.ElementEnd;

獲取當前添加圖片的位置,而後將位置從新定義爲它以後。

還有,就是你們看到個人聊天框裏的RichTextBox的長度是對等的,原來用TextBox時,會根據內容的長度進行變化,而後有一個最大長度,可是,我如今始終也沒有找到如何讓長度Auto的方法,請大神們告知如何搞定。

 

代碼的話,繼續是GitHub,地址的話,在WPF 微信 MVVM帖子裏有,這裏就不寫了。

相關文章
相關標籤/搜索