SyntaxEditor Custom TextHighlightTag

2 Answers 141 Views
SyntaxEditor
Simon
Top achievements
Rank 1
Iron
Iron
Simon asked on 04 May 2022, 08:13 PM

Hi,

Is it possible to get an example of how to to create a custom TextHighlightTag? To try and recreate what the 'Find All' achieves in the search dialog.

It's the only TaggerBase I'm having trouble getting working. I've literally copied the source code from TextSearchHighlightTagger.cs but no matter what when I try to use UpdateSearchWord it results in an exception 'Object reference not set to an instance of an object.'

If I don't register a custom tagger and just use:

syntaxEditor1.SyntaxEditorElement.HighlightAllMatches("Test");

It does exactly what I want to do, only that I can't seem to set the TextFormatDefinition.

What I'm hoping to achieve is to have a text field that controls what is highlighted. Custom would be ideal as I could have more than one then but for my intended purpose I'd be happy just to be able to configure the default colors.

Thanks in advanced!

Regards

2 Answers, 1 is accepted

Sort by
1
Accepted
Dess | Tech Support Engineer, Principal
Telerik team
answered on 09 May 2022, 12:49 PM

Hello, Simon, 

I am glad that the provided code snippet was useful for your scenario. 

Please have in mind that RadSyntaxEditor is purposed to offer one common highlight style for the found search results. Hence, multiple HighlightAllMatches rules are not supported. If you need to highlight specific portion of text in a different color (different than the standard search format style), it would be necessary to create a custom tagger to classify the desired words and mark these words as specific. Then, add a custom UI layer to paint the desired format style. As a result, you will have different styles for the different words to be classified. A similar approach is demonstrated here:

https://docs.telerik.com/devtools/winforms/knowledge-base/underline-text-in-syntax-editor

I have prepared a sample code snippet for your reference. Note that this is just a sample approach and it may not cover all possible cases. Feel free to modify and extend it a way which suits your custom requirements best:

public RadForm1()
{
    InitializeComponent();

    TextFormatDefinition def = new TextFormatDefinition(null,
                               new SolidBrush(System.Drawing.Color.Fuchsia),
                               null,
                               new Telerik.WinForms.Controls.SyntaxEditor.UI.Pen(new SolidBrush(System.Drawing.Color.Aqua), 1));
    this.radSyntaxEditor1.SyntaxEditorElement.TextFormatDefinitions.Remove("SelectedWordFormatDefinition");
    this.radSyntaxEditor1.SyntaxEditorElement.TextFormatDefinitions.AddLast(TextSearchHighlightTagger.SelectedWordFormatDefinition.Name, def);


    this.radSyntaxEditor1.SyntaxEditorElement.UILayersBuilder = new CustomUILayersBuilder();
    this.radSyntaxEditor1.TextFormatDefinitions.AddLast(MyTextSearchHighlightTagger.TelerikClassificationType,
    new TextFormatDefinition(new SolidBrush(System.Drawing.Color.Black), new SolidBrush(System.Drawing.Color.Red),
                         null,
                         new Telerik.WinForms.Controls.SyntaxEditor.UI.Pen(new SolidBrush(System.Drawing.Color.Green), 2)));

    MyTextSearchHighlightTagger highlightTagger = new MyTextSearchHighlightTagger(this.radSyntaxEditor1.SyntaxEditorElement);
    this.radSyntaxEditor1.TaggersRegistry.RegisterTagger(highlightTagger);
}
public class MyTextSearchHighlightTagger : WordTaggerBase
{
    public static readonly ClassificationType TelerikClassificationType = new ClassificationType("Telerik");
    public static readonly Dictionary<string, ClassificationType> WordsToClassificationType = new Dictionary<string, ClassificationType>();
    private StringComparison comparison = StringComparison.OrdinalIgnoreCase; 
    public MyTextSearchHighlightTagger(RadSyntaxEditorElement editor)
       : base(editor)
    { 
        WordsToClassificationType.Add("this", TelerikClassificationType);
        WordsToClassificationType.Add("editor", TelerikClassificationType);
    }

    protected override Dictionary<string, ClassificationType> GetWordsToClassificationTypes()
    {
        return MyTextSearchHighlightTagger.WordsToClassificationType;
    }

    public override IEnumerable<TagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
    {

        if (WordsToClassificationType.Count == 0)
        {
            yield break;
        }

        TextSnapshot snapshot = this.Document.CurrentSnapshot;
        foreach (TextSnapshotSpan snapshotSpan in spans)
        {
            string lineString = snapshotSpan.GetText();
            foreach (KeyValuePair<string, ClassificationType> searchWord in WordsToClassificationType)
            {

                int index = lineString.IndexOf(searchWord.Key, comparison);
                while (index != -1)
                {
                    TextSnapshotSpan tempSnapshotSpan = new TextSnapshotSpan(snapshot,
                        new Telerik.WinForms.SyntaxEditor.Core.Text.Span(snapshotSpan.Start + index, searchWord.Key.Length));

                    yield return new TagSpan<ClassificationTag>(tempSnapshotSpan, new ClassificationTag(TelerikClassificationType));

                    index = lineString.IndexOf(searchWord.Key, index + searchWord.Key.Length);
                }
            }
        }
    }
}

public class CustomUILayersBuilder : Telerik.WinForms.Controls.SyntaxEditor.UI.Layers.UILayersBuilder
{
    public override void BuildUILayers(UILayerStack uiLayers)
    { 
        uiLayers.AddLast(new MyTextUnderlineUILayer());
        base.BuildUILayers(uiLayers);
       
    }
}
public class MyTextUnderlineUILayer : LineBasedUILayer<ClassificationTag>
{
    public override string Name => "MyTextUnderlineUILayer";

    protected override FrameworkElement GetLinePartUIElement(ClassificationTag tag,
                                                             Telerik.WinForms.SyntaxEditor.Core.Text.Span span,
                                                             UIUpdateContext updateContext)
    {
        if (tag.ClassificationType != MyTextSearchHighlightTagger.TelerikClassificationType)
        {
            return null;
        }

        TextFormatDefinition textFormatting = updateContext.Editor.TextFormatDefinitions.
            GetTextFormatDefinition(MyTextSearchHighlightTagger.TelerikClassificationType);
        Telerik.WinControls.SyntaxEditor.UI.Rect rect = updateContext.Editor.GetLinePartBoundingRectangle(span, true);


        Telerik.WinControls.SyntaxEditor.UI.Rectangle rectangle = this.GetElementFromPool<Telerik.WinControls.SyntaxEditor.UI.Rectangle>();
        rectangle.Width = rect.Width;
        rectangle.Height = rect.Height;
        rectangle.Fill = textFormatting.Background;

        rectangle.Stroke = textFormatting.Border.Brush;
        rectangle.StrokeThickness = textFormatting.Border.Thickness.Left;

        return rectangle;
    }

    protected override void ResetPooledElementProperties(object element)
    {
    }
}

private void radTextBox1_TextChanged(object sender, EventArgs e)
{
    this.radSyntaxEditor1.SyntaxEditorElement.HighlightAllMatches(this.radTextBox1.Text);
}

I believe that it would be helpful for your scenario.

Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

1
Dess | Tech Support Engineer, Principal
Telerik team
answered on 05 May 2022, 07:32 AM
Hello, Simon,   

I would recommend you to have a look at the following KB article which demonstrates a sample approach how to highlight specific text in RadSyntaxEditor making it red and underlined. You can add as many text words as you need in the WordsToClassificationType collection:

https://docs.telerik.com/devtools/winforms/knowledge-base/underline-text-in-syntax-editor 

I hope this information helps. If you need any further assistance please don't hesitate to contact me. 

Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Simon
Top achievements
Rank 1
Iron
Iron
commented on 05 May 2022, 11:32 PM

Hi Dess,

I didn't consider the WordTaggerBase like this but yes I can see how this can work. Though it is not really a word match I'm looking for, it should be a string in general as it may form part of a word but not the whole word. Basically exactly as per the Find All functionality.

The example works at making the text red, though it did not underline (which is fine as I don't want to underline). But actually I want to highlight a text piece and that's where I struggle to make it work at all. Basically the same border around the whole highlighted string with a background color, with the font color untouched.

TextHighlightUILayer.cs shows the use of a border, but again I could not get it to work custom thus why I'm hoping to find an example of what I'm trying to achieve exactly.

Regards

 

Dess | Tech Support Engineer, Principal
Telerik team
commented on 06 May 2022, 10:24 AM

Hi, Simon,

Thank you for the provided additional details about the exact goal that you are trying to achieve.  I have prepared a sample code snippet demonstrating how to customize the highlight style for the search results. It is not necessary to create any custom tagger. It is just required to define a TextFormatDefinition with your colors:
        public RadForm1()
        {
            InitializeComponent();

            TextFormatDefinition def = new TextFormatDefinition(null,
                                       new SolidBrush(Color.Fuchsia),
                                       null,
                                       new Telerik.WinForms.Controls.SyntaxEditor.UI.Pen(new SolidBrush(Color.Aqua), 1));
            this.radSyntaxEditor1.SyntaxEditorElement.TextFormatDefinitions.Remove("SelectedWordFormatDefinition");
            this.radSyntaxEditor1.SyntaxEditorElement.TextFormatDefinitions.AddLast(TextSearchHighlightTagger.SelectedWordFormatDefinition.Name, def);
        }

        private void radTextBox1_TextChanged(object sender, EventArgs e)
        {
            this.radSyntaxEditor1.SyntaxEditorElement.HighlightAllMatches(this.radTextBox1.Text); 
        } 

Default style:

Customized style:

I believe that it would be helpful for your case. Please give it a try and see how it works on your end.

Simon
Top achievements
Rank 1
Iron
Iron
commented on 06 May 2022, 07:02 PM

Hi Dess,

That's awesome, it does exactly what I was trying to achieve. Thanks heaps for that. Apologies for not clearly explaining it originally.

Got one more question on this subject if you don't mind.

Is it possible to have more than one HighlightAllMatches rules? Say I have a radTextBox2.Text that I'd also like to highlight but in a different color?

Regards

 

Tags
SyntaxEditor
Asked by
Simon
Top achievements
Rank 1
Iron
Iron
Answers by
Dess | Tech Support Engineer, Principal
Telerik team
Share this question
or