Thursday, December 21, 2006

New firmware for E61 smartphones

My Nokia E61 got new firmware (3.0.633.09.04). Changes are:

  • Faster UI performance
  • Faster web browsing
  • If you accidentally press the voice record button on the side the application launches but doesn't start to automatically record like before!
  • SMS sending now faster
  • Changes to Internet Telephony Application
  • Better IMAP handling

Some of things you should be awared of

Backup all you have to memory card and to computer. Know, that non of your 3d party sis and java applications will remain

Don't be fool and use Nokia Phone Software Updater to update your phone. You do not need Nokia service provider

All password information (e.g. exchange push, blackberry, etc..) will lost most of settings. Be ware, that you should remember them

If you have problems, use *#7370# for hard reset

You should know your memory card password. After restart you'll have to enter it. Esli you lost all your information.

You should know default PINs for your bluetooth devices. Neither of them will remain paired.

That's it. Happy Symbian to all


UPD Almost forgot about lock code. It become 12345 (as by default). Remember, that default input method is ABC, so press the blue key to enter numbers.

Ink recognition in WPF

Tablets world is coming, but we, who even have no laptop (due my manager's greed), working with workstation. Our clients want top edge technology approaches, such as geometrical forms and inks recognition within WPF code in regular desktop computer. So, let's see how we can do it. Today we'll create hand drawing form with basic geometry and handwrited text analysis

First of all, we'll need to bare all ink-related assemblies. Let's put on Microsoft.Ink and Minrosoft.Ink.Analysis. It comes with IACore and IAWinFX in "foundation world". So we'll go to C:\Program Files\Reference Assemblies\Microsoft\Tablet PC\v1.7\ and take everything from there into our application.

Next thing is to create the place where we'll draw our input. InkCanvas looking promise for me

<InkCanvas Width="Auto" Height="Auto" Name="myInkCanvas"  />

Next we'll create InkAnalyzer to analyze our inking. We'll work asyncronous to get nice performance

InkAnalyzer m_analyzer;

        void onLoaded(object sender, RoutedEventArgs e)
        {
            m_analyzer = new InkAnalyzer();
            m_analyzer.AnalysisModes = AnalysisModes.AutomaticReconciliationEnabled;
            m_analyzer.ResultsUpdated += new ResultsUpdatedEventHandler(m_analyzer_ResultsUpdated);
        }

Fine. Now we should provide to our InkAnalyzer information about what it'll going to work for. So, start collection and erasing strokes will make my live easier. We'll have to tell background engine of ink analysis when we want to do anything. Sure, we'll do it after new stroke arrived.

void onStrokeErasing(object sender, InkCanvasStrokeErasingEventArgs e)
        {
            m_analyzer.RemoveStroke(e.Stroke);
        }
 
 
 
        void onStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
        {
            m_analyzer.AddStroke(e.Stroke);
            m_analyzer.BackgroundAnalyze();
        }

 


Goody. Now let's see what we have. We can draw geometry and we can write text. Let's start with the text. We'll create little helper to draw our stings on the surface

class GistaFigure:FrameworkElement
    {
        FormattedText ft;
        Point cent;
        private GistaFigure() { }
        public GistaFigure(string name, Point center, double size, Brush color)
        {
            init(name, center, color, size);
        }
        public GistaFigure(string name, Point center, Brush color)
        {
            init(name,center,color, 12);
 
        }
 
        void init(string name, Point center, Brush color, double size)
        { 
            ft = new FormattedText(
                name, System.Globalization.CultureInfo.CurrentCulture,
               FlowDirection.LeftToRight,
               new Typeface(new FontFamily(), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), size, color);
 
 
            cent = center;
        }
 
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            cent.X -= ft.Width / 2;
            cent.Y -= ft.Height / 2;
            drawingContext.DrawText(ft,cent);
 
        }
    }

Now we can analyze it. I want to color my result other colors, related to confidence of the detection

void m_analyzer_ResultsUpdated(object sender, ResultsUpdatedEventArgs e)
        {
            if (e.Status.Successful)
            {
                ContextNodeCollection nodes = ((InkAnalyzer)sender).FindLeafNodes();
                foreach (ContextNode node in nodes)
                {
                    if (node is InkWordNode)
                    {
                        InkWordNode t = node as InkWordNode;
                        Rect l = t.Location.GetBounds();
                        Point a = new Point(l.Left + l.Width / 2, l.Top + l.Height / 2);
                        double de = l.Height;
                        Brush be = Brushes.Blue;
 
                        switch (t.InkRecognitionConfidence)
                        { 
                            case InkRecognitionConfidence.Intermediate:
                                be = Brushes.Green;
                                break;
                            case InkRecognitionConfidence.Poor:
                                be = Brushes.Red;
                                break;
                            case InkRecognitionConfidence.Unknown:
                                be = Brushes.Brown;
                                break;
                        }
                        GistaFigure figure = new GistaFigure(t.GetRecognizedString(), a,de , be);
                        myInkCanvas.Children.Add(figure);
                    }

Guta! We have "HELLO" recognized, but I want to recognize geometry. Else, my client will throw me out 'cos the way I'm writing text really horrible with the mouse (I have no tablet, you remember?). Let's recognize geometry. Those are other nodes, named InkDrawingNodes. Do it

else if (node is InkDrawingNode)
                    {
                        InkDrawingNode d = node as InkDrawingNode;
                        GistaFigure figure = new GistaFigure(d.GetShapeName(), d.Centroid, Brushes.Red);
                        Shape shape = d.GetShape();
                        if (shape != null)
                        {
                            shape.Stroke = Brushes.Blue;
                            shape.StrokeThickness = 2;
                            myInkCanvas.Children.Add(shape);
                        }
                        myInkCanvas.Children.Add(figure);
                    }

Donno. I have nothing more to add here. So let's draw (for those, who know to do it with only mouse)


Source code for this article

Tuesday, December 19, 2006

Running Action Script under WPF

Can we run Adobe ActionScript from WPF? YES! we can. Just look at this screenshot

Nice, let's see how we do it. First let's take out  our textbox with syntax hightlighing and use it within the project. We not really do it with ActionScript, but with MS JScript (which is seemed to be almost compatible to AS2.0).

So the next step (after we wrote JS) is to compile it. We'll use System.Reflection and System.Reflection.Emit namespaces. Let's take our source and compile it

public static CompilerResults Compile(string source)
        {
 
            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateInMemory = true;
 
            CompilerResults results = (new Microsoft.JScript.JScriptCodeProvider()).CompileAssemblyFromSource(parameters, source);

Fine, now we have results. If the result has errors we do not want to process so

if (results.Errors.HasErrors)
            {
                return results;
            }

In order to make the use know about errors, we'll build panel like in Visual Studio with all of our exceptions (if there are). We'll make XMLDataProvider resource for error list

<XmlDataProvider x:Key="myDebugData" XPath="/myXmlData">
      <x:XData>
        <myXmlData xmlns=""/>
      </x:XData>
    </XmlDataProvider>

and add there all of our erorrs


lstErrors.SetValue(ListView.HeightProperty, (double)0);
            errors = Resources["myDebugData"] as XmlDataProvider;
            errors.Document.SelectSingleNode(errors.XPath).RemoveAll();
 
            System.CodeDom.Compiler.CompilerResults results = JSCompiler.Compile(source);
            if (results.Errors.HasErrors)
            {
                WriteErrors(results.Errors);
                return;
            }
            //no errors. Show methods
            myMethods = Resources["myMethods"] as JSMethods;
            myMethods.Add(new JSMethod(JSCompiler.LastDelegate));

 


As you can see, I change Height dependency property of ListView in order to recieve the behaviour of VS error panel. This ListView is binded to our data source, so we do not need anything except it

<ListView DockPanel.Dock="Bottom" Height="0" Name="lstErrors" ItemsSource="{Binding Source={StaticResource myDebugData}, XPath=Error}">
      <ListView.View>
        <GridView x:Name="debugView">
          <GridViewColumn Header="Description" Width="300" DisplayMemberBinding="{Binding XPath=@Description}"/>
          <GridViewColumn Header="File" Width="120" DisplayMemberBinding="{Binding XPath=@File}"/>
          <GridViewColumn Header="Line" DisplayMemberBinding="{Binding XPath=@Line}"/>
          <GridViewColumn Header="Column" DisplayMemberBinding="{Binding XPath=@Column}"/>
          <GridViewColumn Header="Project" DisplayMemberBinding="{Binding XPath=@Project}"/>
        </GridView>
      </ListView.View>
    </ListView>

 


Let's make it fun!


void ShowErrors()
        {
            DoubleAnimation errAni = new DoubleAnimation(0, this.ActualHeight / 6, new Duration(TimeSpan.FromSeconds(0.5)));
            Storyboard.SetTargetName(errAni, "lstErrors");
            Storyboard.SetTargetProperty(errAni, new PropertyPath(ListView.HeightProperty));
            Storyboard errStory = new Storyboard();
            errStory.Children.Add(errAni);
            errStory.Begin(this);
        }

And usefull

void WriteError(System.CodeDom.Compiler.CompilerError error)
        {
            XmlElement item = errors.Document.CreateElement("Error");
            item.SetAttribute("Description", error.ErrorText);
            item.SetAttribute("File", error.FileName);
            item.SetAttribute("Line", error.Line.ToString());
            item.SetAttribute("Column", error.Column.ToString());
            item.SetAttribute("Project", currentFileName);
 
            errors.Document.SelectSingleNode(errors.XPath).AppendChild(item);
        }

 


If we have no errors, let's  process to our code compilation.


else
{
    Assembly result = results.CompiledAssembly;
    Type[] resultTypes = result.GetTypes();
    for (int j = 0; j < resultTypes.Length; j++)
    {
        if (resultTypes[j].BaseType != typeof(Microsoft.JScript.GlobalScope))
        {
            MethodInfo[] mi = resultTypes[j].GetMethods();
            for (int i = 0; i < mi.Length; i++)
            {
                //we do not want default object's methods
                if (mi[i].Name != "GetType" &
                    mi[i].Name != "ToString" &
                    mi[i].Name != "Equals" &
                    mi[i].Name != "GetHashCode")
                {
                    Type delegateType = CreateDelegate(mi[i]);
 
                    object instance = Activator.CreateInstance(resultTypes[j]);
 
                    if ((mi[i].Attributes & MethodAttributes.Static) == MethodAttributes.Static)
                    {
                        instance = null;
                    }
                    else
                    {
                        instance = Activator.CreateInstance(resultTypes[j]);
                    }
 
                    LastDelegate = Delegate.CreateDelegate(delegateType, instance, mi[i]);
 
                }
            }
        }
    }

We do not really want to compile our code into assemby and reference it to our binaries, so we'll have to create a list of delegates in order to be able to execute those IL methods. The tricky part is to know if the method, I want to create delegate for is not static, 'cos in this case we wont create an instance of hosing application. So let's create a delegate type from MethodInfo for our method.


static Type CreateDelegate(MethodInfo method)
        {
            AssemblyName assembly = Assembly.GetExecutingAssembly().GetName();
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(method.Module.Name);
            TypeBuilder typeBuilder = moduleBuilder.DefineType(method.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(System.MulticastDelegate));
            ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { method.ReturnType });
            constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
 
            ParameterInfo[] parameters = method.GetParameters();
            Type[] parameterTypes = new Type[parameters.Length];
 
            for (int i = 0; i < parameters.Length; i++)
            {
                parameterTypes[i] = parameters[i].ParameterType;
            }
 
            MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.PrivateScope | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask, method.ReturnType, parameterTypes);
            methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed | method.GetMethodImplementationFlags());
 
            Type type = typeBuilder.CreateType();
 
            return type;
        }

Fine, now the only thing we should do is create the instance of this delegate and execute it when needed. In order to make me able to bind and manage it and later use it for automatic executions and WPF code generation. I will want to compare those methods in order to know if the method I just created is not the method I already have. So overroding Method operators will really help me with it.


public class JSMethod
    {
        private Delegate executer;
        public Delegate Executer
        {
            get { return executer; }
            set { executer = value; }
        }
 
        private string methodName;
        public string MethodName
        {
            get { return methodName; }
            set { methodName = value; }
        }
 
        private JSCompiler.Parameter[] parameters;
        public JSCompiler.Parameter[] Parameters
        {
            get { return parameters; }
            set { parameters = value; }
        }
 
        private string className;
        public string ClassName
        {
            get { return className; }
            set { className = value; }
        }
        public JSMethod(Delegate source)
        {
            this.executer = source;
            if (this.executer.Target != null)
            {
                this.className = this.executer.Target.GetType().ToString();
            }
            else {
                this.className = "Static Class";
            }
            parameters = JSCompiler.GetParameters(source);
            string sps = "";
            for (int i = 0; i < parameters.Length; i++)
            {
                sps += parameters[i].ToString();
            }
            this.methodName = this.executer.Method.Name+"( "+sps+" )";
        }
        public JSMethod(string cName)
        {
            className = cName;
        }
 
        public static bool operator ==(JSMethod method1, JSMethod method2)
        {
            object o1 = method1;
            object o2 = method2;
            if (o1 == null & o2 == null)
                return true;
            else if (o1 == null | o2 == null)
                return false;
            else if (method1.ClassName == method2.ClassName &&
           method1.MethodName == method2.MethodName)
                return true;
            return false;
        }
        public static bool operator !=(JSMethod method1, JSMethod method2)
        {
            return !(method1 == method2);
        }
 
    }

The ObservableCollection of all methods will be later binded to hierarcical tree view to group it by classes


public class JSMethods : ObservableCollection<JSMethod>
    {
        public JSMethods()
        {
 
        }
 
 
        public new void Add(JSMethod method)
        {
            for (int i = 0; i < base.Count; i++)
            {
                if (this[i] == method)
                    return;
            }
            base.Add(method);
        }
    }

Ok, now how to group it? If my data source was XML data or any other hierarcical data, I was able just bind it to treeview, and smart Binding will parse it as needed, but my data is not hierarcical, so I'll have to make some woodoo in order to "tell" control how to show it. First of all I need a source and this is really simple touch.

<local:JSMethods x:Key="myMethods"/>

If I want to bing it as is, I would use something like this


<ObjectDataProvider x:Key="myMethods" ObjectType="{x:Type local:JSMethods}"/>


But, because of my data is hierarcical, I'll need something like this

<CollectionViewSource x:Key="methodsViewSource" Source="{Binding Source={StaticResource myMethods}}">
      <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="ClassName"/>
      </CollectionViewSource.GroupDescriptions>        
    </CollectionViewSource>

Even if I told my data how to group, it's not so clear for the control, so I'll need additional templates to use

<HierarchicalDataTemplate x:Key="classTemplate"
      ItemTemplate="{StaticResource methodTemplate}"
      ItemsSource="{Binding Path=Items}">
      <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
    </HierarchicalDataTemplate>

and one "regulat" template for items

<DataTemplate x:Key="methodTemplate" DataType="{x:Type local:JSMethod}">
      <ContentControl  MouseDoubleClick="onMethodClick">
        <TextBlock Text="{Binding Path=MethodName}"/>
      </ContentControl>
    </DataTemplate>

If I'd like to continue hierarcy, I'll need to use another Hierarchical data template for it, but in this case, what I have is prety enough for me. Bind 'em all


<TreeView Name="methodsTree" DataContext="{Binding Source={StaticResource methodsViewSource}}" ItemsSource="{Binding Path=Groups}" ItemTemplate="{StaticResource classTemplate}" Width="0">


That's all folks. I got a error list, delegates, bindings. Now I want to execute it. That mean, I'll have to build custom user interface, that will be built according my data types and all of my parameters for all methods. Do it.


While clicking on the method descriptor, I will exchange its content with new generated panel, incuding everything I know about the method and will pass it within right types into delegate and evaluate it


 


protected void onMethodClick(object sender, MouseButtonEventArgs e)
        {
            JSMethod method = methodsTree.SelectedItem as JSMethod;
            if (method == null)
                return;
            StackPanel sp = new StackPanel();
            sp.Width = this.Width - 50;
            sp.Height = method.Parameters.Length*40+50;
            sp.Orientation = Orientation.Vertical;
            for (int i = 0; i < method.Parameters.Length; i++)
            {
                StackPanel s = new StackPanel();
                s.Width = this.Width - 50;
                s.Height = 40;
                s.Orientation = Orientation.Horizontal;
                TextBox t = new TextBox();
                t.Height = 20;
                t.Width = this.Width / 2 - 50;
                t.Text = method.Parameters[i].Name+"("+method.Parameters[i].Type.ToString()+")";
 
                TextBox tb = new TextBox();
                tb.Height = 20;
                tb.Width = this.Width / 2 - 50;
                tb.Name=method.Parameters[i].Name;
 
                s.Children.Add(t);
                s.Children.Add(tb);
                sp.Children.Add(s);
            }
            Button b = new Button();
            b.VerticalAlignment = VerticalAlignment.Bottom;
            b.Click += new RoutedEventHandler(b_Click);
            b.Height = 50;
            b.Width = this.Width-50;
            b.Content = "Execute Method";
 

How, I have to return my content of what I had in this line after method execution. So I'll save it with tags

sp.Tag = method;
            sp.Children.Add(b);
 
            ContentControl cc = sender as ContentControl;
            object[] obs = {
                cc,
                cc.Content
            };
            b.Tag = obs;
            cc.Content = sp;

On click the execute button, I'll want to execute method and return all I had


void b_Click(object sender, RoutedEventArgs e)
        {
            Button b = sender as Button;
            StackPanel sp = b.Parent as StackPanel;
            if (sp != null)
            { 
                JSMethod method = sp.Tag as JSMethod;
                if(method != null)
                {
                    object[] obs = b.Tag as object[];
                    ContentControl cc = obs[0] as ContentControl;
                    sp = cc.Content as StackPanel;
 
                    for (int i = 0; i < method.Parameters.Length; i++)
                    { 
                        StackPanel ssp = sp.Children[i] as StackPanel;
                        if (ssp != null)
                        {
                            method.Parameters[i].Value = GetValueString(method.Parameters[i].Name, ssp, method.Parameters[i].Type);
                        }
                    }
                    object o = JSCompiler.ExecuteMethod(method);
 
                    MessageBox.Show("The result value of " + method.MethodName + " is " + o.ToString(), "Result value of method evaluation", MessageBoxButton.OK, MessageBoxImage.Information);
                    object obb = obs[1];
                    cc.Content = obb;
                }
            }
        }

 


The other thing I should do is to find what parametes the user entered. Using VisualTree it become very easy

object GetValueString(string elementName, Visual elementParent, Type elementType)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(elementParent); i++)
            {
                Visual v = (Visual)VisualTreeHelper.GetChild(elementParent, i);
                if(v is TextBox)
                {
                    TextBox t = v as TextBox;
                    if(t.Name == elementName)
                    {
                        return Convert.ChangeType(t.Text, elementType);
                    }
                }
            }
            return null;
        }

 


The final salute is just execute it. You don't believe me. It's just one line of code. Here it comes

public static object ExecuteMethod(Delegate source, out Type returnType, params Parameter[] parameters)
        {
            returnType = source.Method.ReturnType;
            object[] ps = new object[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                ps[i] = parameters[i].Value;
            }
            return source.DynamicInvoke(ps);
        }

That's really all now. Thank you reflection, emit and WPF to beat evil Adobe with their "Ultra dynamic and useful tools". We really dont need it. We can do everything ourself!


Source code for this article

Thursday, December 14, 2006

RichTextBox syntax highlighting

Today we'll learn how to create RichTextBox with syntax highlight ing. Syntax highlighting is a feature of some text editors that displays text—especially source code—in different colors and fonts according to the category of terms. This feature eases writing in a structured language such as a programming language or a markup language as both structures and syntax errors are visually distinct.

The first thing we need is RichTextBox. So, do it. I did some trick on it in order to prevent from <P> (Paragraph) inserting on each enter (with is default behavior of RichTextBox). I want only one line down when I pressed my Enter key.

<RichTextBox ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Name="TextInput" AcceptsReturn="True" TextChanged="TextChangedEventHandler">
      <RichTextBox.Resources>
        <Style TargetType="{x:Type Paragraph}">
          <Setter Property="Margin" Value="0"/>
        </Style>
      </RichTextBox.Resources>
    </RichTextBox>

Ok, not I want only plaintext in my TextBox TextChanged event 'cos I'm going to color it myself.

private void TextChangedEventHandler(object sender, TextChangedEventArgs e)
        {
            if (TextInput.Document == null)
                return;
 
            TextRange documentRange = new TextRange(TextInput.Document.ContentStart, TextInput.Document.ContentEnd);
            documentRange.ClearAllProperties();

 


Now let's create navigator to go though the text and hightlight it


TextPointer navigator = TextInput.Document.ContentStart;
            while (navigator.CompareTo(TextInput.Document.ContentEnd) < 0)
            {
                TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward);
                if (context == TextPointerContext.ElementStart && navigator.Parent is Run)
                {
                    CheckWordsInRun((Run)navigator.Parent);
 
                }
                navigator = navigator.GetNextContextPosition(LogicalDirection.Forward);
            }

 


So the only thing we have to do is to check each word within my navigator, compare it to Highlighting dictionary and color it. Huh, where is my dictionary? Here it comes. I'll do it for ActionScript for my client.

class JSSyntaxProvider
   {
       static List<string> tags = new List<string>();
       static List<char> specials = new List<char>();
       #region ctor
       static JSSyntaxProvider()
       {
           string[] strs = {
               "Anchor",
               "Applet",
               "Area",
               "Array",
               "Boolean",.....
tags = new List<string>(strs);
 
We also want to know all possible delimiters so adding this stuff.
 
            char[] chrs = {
                '.',
                ')',
                '(',
                '[',
                ']',
                '>',
                '<',
                ':',
                ';',
                '\n',
                '\t'
            };
            specials = new List<char>(chrs);
 

Now I should check statically if the string I passed is legal and constants in my dictionary

public static bool IsKnownTag(string tag)
        {
            return tags.Exists(delegate(string s) { return s.ToLower().Equals(tag.ToLower()); });
        }

Also, I'll do kind of Intellisense later, so I want to check the beginnings of my tags as well

public static List<string> GetJSProvider(string tag)
        {
            return tags.FindAll(delegate(string s) { return s.ToLower().StartsWith(tag.ToLower()); });
        }

Wow. Great. Now I should separate words, that equals to my tags. For this propose we'll create new internal structure named Tag. This will help us to save words and its' positions.

new struct Tag
        {
            public TextPointer StartPosition;
            public TextPointer EndPosition;
            public string Word;
 
        }

How, let's go through our text and save all tags we have to save.

int sIndex = 0;
            int eIndex = 0;
            for (int i = 0; i < text.Length; i++)
            {
                if (Char.IsWhiteSpace(text[i]) | JSSyntaxProvider.GetSpecials.Contains(text[i]))
                {
                    if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | JSSyntaxProvider.GetSpecials.Contains(text[i - 1])))
                    {
                        eIndex = i - 1;
                        string word = text.Substring(sIndex, eIndex - sIndex + 1);
 
                        if (JSSyntaxProvider.IsKnownTag(word))
                        {
                            Tag t = new Tag();
                            t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
                            t.EndPosition = run.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward);
                            t.Word = word;
                            m_tags.Add(t);
                        }
                    }
                    sIndex = i + 1;
                }
            }

How this works. But wait. If the word is last word in my text I'll never hightlight it, due I'm looking for separators. Let's add some fix for this case

string lastWord = text.Substring(sIndex, text.Length - sIndex);
            if (JSSyntaxProvider.IsKnownTag(lastWord))
            {
                Tag t = new Tag();
                t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex, LogicalDirection.Forward);
                t.EndPosition = run.ContentStart.GetPositionAtOffset(eIndex + 1, LogicalDirection.Backward);
                t.Word = lastWord;
                m_tags.Add(t);
            }

How I have all my words and its' positions in list. Let's color it! Dont forget to unsubscribe! text styling fires TextChanged event.


TextInput.TextChanged -= this.TextChangedEventHandler;
 
            for(int i=0;i<m_tags.Count;i++)
            {
                TextRange range = new TextRange(m_tags[i].StartPosition, m_tags[i].EndPosition);
                range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
                range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
            }
            m_tags.Clear();
 
            TextInput.TextChanged += this.TextChangedEventHandler;

That's all. We have nice RichTextBox, that understands and color any syntax we want. Maybe later we'll add Intellisense for it.


Source code of this article