This project has moved. For the latest updates, please go here.

Smile convertor on richTextBox

Jan 7, 2011 at 7:26 PM
Edited Jan 7, 2011 at 7:30 PM

Hi, I need advice. I make chat window as in skype.  I convert text with emoticons (as string) to  text with images. I have own class wich convert string to text with emoticons and I would like functionality in formatter class if it is posisble.

I have method wich convert string to paragraph with images (smiles) here is it:

 

        private Paragraph ConvertToRpWithEmoticons(string rpText)
        {
            var r = new Run(rpText);
            var para = new Paragraph();

            //get first smile in text
            Smile smile = GetEmoticon(rpText);

            //if paragraph does not contains smile only add plain text to richtextbox 
            if (string.IsNullOrEmpty(smile.Key))
            {
                para.Inlines.Add(new LineBreak());
                para.Inlines.Add(r);
                return para;
            }
            else
            {
                TextPointer tp = r.ContentStart;
                while (!string.IsNullOrEmpty(smile.Key))
                {
                    TextPointer startp = tp;
                    while (!tp.GetTextInRun(LogicalDirection.Forward).StartsWith(smile.Key))
                    {
                        tp = tp.GetNextInsertionPosition(LogicalDirection.Forward);
                    }
                    var separatemsg = new TextRange(startp, tp);

                    var tr = new TextRange(tp, tp.GetPositionAtOffset(smile.Key.Length)) { Text = string.Empty };

                    //relative path to image smile file
                    string path = smile.Value;
                    var image = new Image
                    {
                        Source = new BitmapImage(new Uri(path, UriKind.RelativeOrAbsolute)),
                        Width = 30,
                        Height = 30,
                    };
                    para.Inlines.Add(new Run(separatemsg.Text));
                    //insert smile
                    para.Inlines.Add(new InlineUIContainer(image));
                    if (string.IsNullOrEmpty(tp.GetTextInRun(LogicalDirection.Forward)))
                    {
                        break;
                    }
                    else
                    {
                        smile = GetEmoticon(tp.GetTextInRun(LogicalDirection.Forward));
                    }

                }
            }
            return para;
        }

 

 

I old solution I bind property Document of richTextbox to view model property. And add new paragraph to this property.

I have method which format some string messages, it look like this:

 

 

        
//Rp is class for message
public Table ConvertToTabRp(Rp rp, string avatarNick)
        {
            var tempRp = new Rp { RpText = rp.RpText, Nick = rp.Nick, Time = rp.Time, Your = rp.Your };

            if (tempRp.Your)
                tempRp.Nick = avatarNick;

            string time = string.Format(CultureInfo.CurrentCulture, "{0}", DateTime.ParseExact(tempRp.Time, "yyyy-MM-dd HH:mm:ss", null)
                .ToString("dd-MM-yyyy HH:mm:ss").Replace("-", "."));

            var tab = new Table();

            var gridLenghtConvertor = new GridLengthConverter();


            tab.Columns.Add(new TableColumn { Name = "colNick", Width = (GridLength)gridLenghtConvertor.ConvertFromString("100") });
            tab.Columns.Add(new TableColumn { Name = "colMsg", Width = (GridLength)gridLenghtConvertor.ConvertFromString("Auto") });
            tab.Columns.Add(new TableColumn { Name = "colDt", Width = (GridLength)gridLenghtConvertor.ConvertFromString("150") });

            tab.RowGroups.Add(new TableRowGroup());
            tab.RowGroups[0].Rows.Add(new TableRow());

            var tabRow = tab.RowGroups[0].Rows[0];

            var pNick = new Paragraph(new LineBreak()) { TextAlignment = TextAlignment.Left };
            pNick.Inlines.Add(new Run(tempRp.Nick));

            if (!tempRp.Your)
            {
                pNick.Foreground = Brushes.DodgerBlue;
            }
            tabRow.Cells.Add(new TableCell(pNick));

 //Cell with text and emoticons
tabRow.Cells.Add(new TableCell(ConvertToRpWithEmoticons(tempRp.RpText)) { });
var pTime = new Paragraph(new LineBreak()) { TextAlignment = TextAlignment.Right }; pTime.Inlines.Add(time); tabRow.Cells.Add(new TableCell(pTime)); return tab; }

 

So if I get string message  I do this:

 

        //rtbFlowDocument is bind on Document property of richTextBox
        rtbFlowDocument.Blocks.Add(ConvertToTabRp(rp,"mynick");

 

It is possible create some kind of formatter which automaticaly add smiles to richTextbox when user typing? I am helpless and confuse.

I have method ConvertToRpWithEmoticons which can convert  text to text to emoticons but I don’t know how can I apply this method in formatter class? Any example?

 

Another question, if use typing in richTextBox is method SetText calling? or when is this method calling?

 

public class MyFormatter : ITextFormatter
{
        public string GetText(System.Windows.Documents.FlowDocument document)
        {
            return new TextRange(document.ContentStart, document.ContentEnd).Text;
        }

        public void SetText(System.Windows.Documents.FlowDocument document, string text)
        {
            new TextRange(document.ContentStart, document.ContentEnd).Text = text;
        }
 }

Jan 7, 2011 at 8:17 PM

If you need this behavior as the user is typing you should handle the TextChanged event and add your logic there.  That way when the user type in :) you can replace it with your image.  The SetText method is used to populate the RichTextBox, the GetText method converts the RichTextBox document into a string that can be returned to the bound property.  By default the GetText method executes when the RichTextBox loses focus to update the bound property.  So I don't think you will need to use a custom formatter.

Jan 7, 2011 at 8:29 PM
brianlagunas wrote:

If you need this behavior as the user is typing you should handle the TextChanged event and add your logic there.  That way when the user type in :) you can replace it with your image.  The SetText method is used to populate the RichTextBox, the GetText method converts the RichTextBox document into a string that can be returned to the bound property.  By default the GetText method executes when the RichTextBox loses focus to update the bound property.  So I don't think you will need to use a custom formatter.

I try this:

In WPF app I bind properties on Text and Document properties of richTextBox control.

    //bind on Text property of richTextBoxControl
    public string RtbText
    {
        get { return _rtbText; }
        set
        {
            _rtbText = value;
            NotifyPropertyChanged("RtbText");
        }
    }

    //bind on Document property of richTextBoxControl
    public FlowDocument RtbFlowDocument
    {
        get { return _rtbFlowDocument; }
        set
        {
            _rtbFlowDocument = value;
            NotifyPropertyChanged("RtbFlowDocument");
        }
    }

I use Reactive Extensions for .NET (Rx) and make Observer on property RtbText

        Observable.FromEvent<PropertyChangedEventArgs>(this, "PropertyChanged")
.Where(e => e.EventArgs.PropertyName == "RtbText")
.Select(_ => this.RtbText)
.Where(text => text.Length > 1)
.Do(AddSmiles)
.Throttle(TimeSpan.FromSeconds(500))
.Subscribe(AddSmiles);
My problem is how elegant replace text with smiles to text with emoticons. I try this:

   private void AddSmiles(string text)
   {
        Paragraph paragraph = _smileConverter.ConvertToMessageWithEmoticons(RtbText);

        RtbFlowDocument.Blocks.Clear();

        RtbFlowDocument.Blocks.Add(paragraph);
   }


I think use TextChanged mehod is no good. Because you something write and this method is constantly call. I think much more better is make a observer on property which is bind on text of richTextBox.

But my solution doesn’t work ... :[
Jan 7, 2011 at 8:46 PM

Have you made modifications to the control, because there is no Document dependency property that allows you to databind to the Document property?  If so I cannot accurately help because I don't kow what changes have been made ot the code.

So I am imagining you have 2 RichTextBoxes, one on the top for displaying the conversation, and one on the bottom where the user types in.  If you don't want to update the emotioncon as the user is typing a message in the bottom RTB, then do not handle the TextChanged.  If you do then you will have to handle the TextChanged, and although it wll fire everytiime a user type a word, it will only run your emotioncon method if the text matches a symbol, otherwise it will ignore it and there will be no noticable performance hit.

Now if you don't want to show the emotioncon as the user types, then you would want to write a custom converter, and in the SetText you would want to parse out the symbols and replace with your images for the top RTB only.

Sep 5, 2012 at 5:04 AM

Can I have your source code with this function ConvertToRpWithEmoticons.

if so, please send me, piseysen_up@yahoo.com

Thanks.