今天主要記錄的就是發送QQ表情, WPF 微信 MVVM裡寫了,後期為了發送QQ表情,需要把TextBox替換為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
做列表的方法寫在了Emoji選項列表這個博客裡,不過因為是針對微信來做,所以,還是修改了一部分。
首先,右鍵查看微信網頁版的源文件,會看到它的表情列表大概是這個樣子的代碼,從裡面獲取title和class裡的內容,做成一個XML文件


然後,去網上找QQ的表情壓縮包,這個不是難事,然後根據class進行命名,最後使用Emoji選項列表裡的方法就可以做成列表了,看下效果圖

網頁版微信,在F12下,發送一條消息,可以看到發送的報文是“文字+[表情內容]”(指QQ的表情,Emoji現在沒看到規律),因為,在轉換的時候,也就有了目標

我們在點擊發送按鈕之前,是將發送框裡的FlowDocument轉換成String發送出去的。
轉換過程是,循環FlowDocument裡面的Blocks(Paragraph都放在Blocks裡面),然後循環Paragraph裡的Inlines,判別裡面的內容是InlineUIContainer(Image)還是Run(Text)
如果是圖片的話,則從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。
因為我們知道了消息的格式是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
看下最終的效果圖

在把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帖子裡有,這裡就不寫了。