Check if user typed specific word after typing the word, in an <Editor>

Viewed 551

So what I'm looking for is a code which will check if the user wrote one of the specific words (Select, From, Where). If true, then the textcolor of that word must change (TextChanged event). I don't know how I have to make it so, that it is possible.

I'm using Xamarin.Forms and made in the Page.xaml the following code:

<Editor x:Name="qEditor" 
     Text="Input"
     TextChanged="Handle_TextChanged"
     BackgroundColor="White" 
     HorizontalOptions="FillAndExpand"
     HeightRequest="75"/>

So whenever someone typed one of the specific words, the color of only that word must directly change to red.

Code in Page.xaml.cs:

void Handle_TextChanged(object sender, TextChangedEventArgs e)
{
    string input = e.NewTextValue;
    string[] qInput = input.Split(' ');
    //Code here
}

I looked a lot around on Google and Stackoverflow and found something, but it uses Contains which did not work for my code.

If you have any suggestions of what I need to change or even if I have to change everything about the code above, give me some suggestions. The project requires me to only work with C# And Xamarin.Forms.

2 Answers

Xamarin forms doesn't support this out of the box.

You will have to extend the Editor control to add support for formatted-text. This can be done by adding a bindable property of type FormattedString. This control will keep the property updated by listening to text changes through TextChanged event (I have used Regex in this case).

As FormattedText will be bindable-property, the custom platform renderers can detect the change, and accordingly render it as Spannable in android and AttributedString in iOS.

enter image description here

Forms Control

public class ExEditor : Editor
{
    public static readonly BindableProperty FormattedTextProperty =
        BindableProperty.Create(
            "FormattedText", typeof(FormattedString), typeof(ExEditor),
            defaultValue: default(FormattedString));

    public FormattedString FormattedText
    {
        get { return (FormattedString)GetValue(FormattedTextProperty); }
        set { SetValue(FormattedTextProperty, value); }
    }

    public ExEditor()
    {
        TextChanged += ExEditor_TextChanged;
    }

    void ExEditor_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(Text))
            return;

        var pattern = @"\b(SELECT|WHERE|AND|OR)\b";
        var words = Regex.Split(Text, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);

        var formattedString = new FormattedString();
        foreach (var word in words)
            formattedString.Spans.Add(new Span
            {
                Text = word,
                BackgroundColor = BackgroundColor,
                FontSize = FontSize,
                FontFamily = FontFamily,
                FontAttributes = FontAttributes,
                ForegroundColor = Regex.IsMatch(word, pattern, RegexOptions.IgnoreCase) ? Color.Red : TextColor
            });

        FormattedText = formattedString; 
    }       
}

iOS Renderer

[assembly: ExportRenderer(typeof(ExEditor), typeof(ExEditorRenderer))]
namespace SampleApp.iOS
{
    public class ExEditorRenderer : EditorRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);

            if (Control == null || Element == null)
                return;

            UpdateTextOnControl();
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == nameof(ExEditor.FormattedText)
                || e.PropertyName == nameof(Editor.FontFamily)
                || e.PropertyName == nameof(Editor.FontSize)
                || e.PropertyName == nameof(Editor.TextColor)
                || e.PropertyName == nameof(Editor.BackgroundColor)
                || e.PropertyName == nameof(Editor.FontAttributes))
                {
                    UpdateTextOnControl();
                }
        }

        void UpdateTextOnControl()
        {
            var caretPos = Control.GetOffsetFromPosition(Control.BeginningOfDocument, Control.SelectedTextRange.Start);

            if (Element is ExEditor formsElement)
                if (formsElement.FormattedText != null)
                    Control.AttributedText = formsElement.FormattedText.ToAttributed(new Font(), Element.TextColor);

            var newPosition = Control.GetPosition(Control.BeginningOfDocument, offset: caretPos);
            Control.SelectedTextRange = Control.GetTextRange(newPosition, newPosition);
        }
    }
}

Android Renderer

[assembly: ExportRenderer(typeof(ExEditor), typeof(ExEditorRenderer))]
namespace SampleApp.Android
{
    public class ExEditorRenderer : EditorRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);

            if (Control == null || Element == null)
                return;

            UpdateTextOnControl();
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == nameof(ExEditor.FormattedText)
                || e.PropertyName == nameof(Editor.FontFamily)
                || e.PropertyName == nameof(Editor.FontSize)
                || e.PropertyName == nameof(Editor.TextColor)
                || e.PropertyName == nameof(Editor.BackgroundColor)
                || e.PropertyName == nameof(Editor.FontAttributes))
            {
                UpdateTextOnControl();
            }
        }

        void UpdateTextOnControl()
        {
            var caretPos = Control.SelectionStart;

            if (Element is ExEditor formsElement)
                if (formsElement.FormattedText != null)
                    Control.SetText(formsElement.FormattedText.ToAttributed(new Font(), Element.TextColor, Control),
                                    TextView.BufferType.Spannable);

            Control.SetSelection(caretPos);
        }
    }
}

Sample Usage

<!-- make sure to map local prefix to control's namespace -->
<local:ExEditor Text="Select * from Table where text='1' and type='str'" 
        VerticalOptions="Start" />

In this case you could use the property FormattedText of the Xamarin.Forms.Label

Example in your case:

    private string[] wordsRed = { "Select", "From", "Where"}; // some examples
    private bool isSpecialColor = false;
    private Span span;

    /// <summary>
    /// Handles the text changed.
    /// </summary>
    /// <param name="sender">Sender.</param>
    /// <param name="e">E.</param>
    void Handle_TextChanged(object sender, TextChangedEventArgs e)
    {
        string input = e.NewTextValue;
        string[] qInput = input.Split(' ');

        CheckInput(qInput);
    }

    /// <summary>
    /// Checks the input.
    /// </summary>
    /// <param name="qInput">Q input.</param>
    private void CheckInput(string[] qInput)
    {
        // Check if the last typed character is a word separator.
        if (input[input.Length-1] == ' ') 
        {
            //
            // Loop through the string[] with the words you want to give a color on.
            //
            for (int i = 0; i < wordsRed.Length; i++)
            {
                //
                // Check on the last word that is typed
                //
                if (qInput[qInput.Length-1] == wordsRed[i])
                {
                    // Create a span with the word and color the foreground red
                    span = new Span
                    {
                        Text = qInput[qInput.Length-1] + " ",
                        ForegroundColor = Color.Green,
                    };

                    // Add the span to the Label
                    aLabel.FormattedText.Spans.Add(item: span);
                    // set boolean true because there is made a span with a red foreground.
                    isSpecialColor = true;
                }
                else
                {
                    // Do nothing.
                }
            }

            // Check if the word altready is colored.
            if (isSpecialColor)
            {
                // Set the boolean to false for the next check.
                isSpecialColor = false;
            }
            else
            {
                // Create span with the word and color it black.
                span = new Span
                {
                    Text = qInput[i] + " ",
                    ForegroundColor = Color.Black,
                };
                aLabel.FormattedText.Spans.Add(item: span);
            }
        }
        else 
        {                
            // Do nothing.
        }
    }
Related