How to Create a ReadOnly EditText or TextBox in Xamarin.Forms

LearnEverythingLearnEverything USMember ✭✭
edited November 2017 in Xamarin.Forms

I want to have a selectable text from textbox but it is read only and
Support TextSelection.
Like this

Tagged:

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Have you tried monitoring the TextChanged event? Whenever the values changes, set it back

  • LearnEverythingLearnEverything USMember ✭✭

    In UWP it is easy to create a readonly textbox
    //UWP Style

    <Style x:Key="ReadOnlyTextBox" TargetType="TextBox">
            <Setter Property="Foreground" Value="{StaticResource SystemControlBackgroundAccentBrush}" />
            <Setter Property="IsReadOnly" Value="True" />
            <Setter Property="TextWrapping" Value="Wrap" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Focused">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="SelectedGrid">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Gray"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unfocused"/>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid Name="SelectedGrid" Opacity="0.2"/>
                            <Border>
                                <ContentPresenter x:Name="ContentElement"/>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    

    The result

  • LearnEverythingLearnEverything USMember ✭✭

    I found a solution for creating a selectable textbox with TextSelectionChanged and Selected text.
    //Subclass the Xamarin.Android TextView

    namespace AndroidAppDemo.SubClasses
    {
        public class ReadOnlyTextBox : TextView, INotifyPropertyChanged
        {
            public ReadOnlyTextBox(Context context, IAttributeSet attrs) : base(context, attrs)
            {
                Default();
            }
            public ReadOnlyTextBox(Context context) : base(context)
            {
                Default();
            }        
            protected ReadOnlyTextBox(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
            {
                Default();
            }
            protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
            {
                base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
                this.SetMeasuredDimension(this.MeasuredWidth, this.MeasuredWidth);
            }
            //Initialize default style
            private void Default()
            {
                this.SetTextIsSelectable(true);
                //this.CustomSelectionActionModeCallback = new Callback();
            }     
            //Custom EventHandler
            public event EventHandler TextSelectionChanged;       
            protected override void OnSelectionChanged(int selStart, int selEnd)
            {
                base.OnSelectionChanged(selStart, selEnd);
                OnChangedSelection(EventArgs.Empty);
            }
            public virtual void OnChangedSelection(EventArgs e)
            {
                if (TextSelectionChanged != null)
                {               
                    var start = this.SelectionStart;
                    var end = this.SelectionEnd;
                    try
                    {
                        if (this.HasSelection & start != end)
                        {
                            var selected = this.Text.Slice(start, end);
                            this.SelectedText = selected;
                            this.OnPropertyChanged("SelectedText");
                        }
                    }
                    catch 
                    {
                        this.SelectedText = string.Empty;
                        this.OnPropertyChanged("SelectedText");
                    }
                    TextSelectionChanged(this, e);
                }
            }      
            public string SelectedText { get; set; }      
            public event PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
        public static class Extensions
        {
            /// <summary>
            /// Get the string slice between the two indexes.
            /// Inclusive for start index, exclusive for end index.
            /// </summary>
            public static string Slice(this string source, int start, int end)
            {
                if (end < 0) // Keep this for negative end support
                {
                    end = source.Length + end;
                }
                int len = end - start;               // Calculate length
                return source.Substring(start, len); // Return Substring of length
            }
        }
    

    Add to axml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     <AndroidAppDemo.SubClasses.ReadOnlyTextBox  
        android:textIsSelectable="true" 
        android:id="@+id/readonlytextView"
        android:text="The quick brown fox jumped over the lazy dog, The quick brown fox jumped over the lazy dog, The quick brown fox jumped over the lazy dog" android:textSize="20dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </AndroidAppDemo.SubClasses.ReadOnlyTextBox>
      <TextView  android:layout_width="match_parent"
        android:layout_height="match_parent" android:text="Sample text" android:textSize="20dp" android:id="@+id/result" />
    </LinearLayout>
    

    Test the ReadOnlyTexBox on MainActivity

    namespace AndroidAppDemo
    {
        [Activity(Label = "AndroidAppDemo", MainLauncher = true, Icon = "@drawable/icon")]
        public class MainActivity : Activity
        {
            ReadOnlyTextBox readolyTextView;
            TextView result;
            protected override void OnCreate(Bundle bundle)
            {
                base.OnCreate(bundle);
                // Set our view from the "main" layout resource
                SetContentView (Resource.Layout.Main);
                FindViewsIds();
                EventHandlers();
            }
            private void FindViewsIds()
            {          
                this.readolyTextView = FindViewById<ReadOnlyTextBox>(Resource.Id.readonlytextView);
                this.result = FindViewById<TextView>(Resource.Id.result);
            }
            private void EventHandlers()
            {
                this.readolyTextView.TextSelectionChanged += (g, j) =>
                {
                    this.result.Text = readolyTextView.SelectedText;
                };           
            }      
        }
    }
    

    here is the result for android, I need to learn next how to use ViewRenderer.
    I am just absolute beginner in C#.

  • JohnHardmanJohnHardman GBUniversity mod
    edited November 2017

    @LearnEverything - It's surprisingly fiddly to do this well. I have it working on iOS, but on Android I got some unwanted side effects, so I've rolled it back on Android for the time being.

    On both platforms, you need to create a custom Entry class and a corresponding custom EntryRenderer.

    On iOS, it is necessary to replace the native control:

                ReadOnlyUITextField textField = new ReadOnlyUITextField(RectangleF.Empty);
                SetNativeControl(textField);
    

    where ReadOnlyUITextField is as follows:

    public class ReadOnlyUITextField : UITextField
    {
        public ReadOnlyUITextField(RectangleF rect) : base(rect)
        {
        }
    
        public override bool CanPerform(ObjCRuntime.Selector action, NSObject withSender)
        {
            if ((action == new ObjCRuntime.Selector("paste:"))
                || (action == new ObjCRuntime.Selector("cut:")))
            {
                return false;
            }
            else
                return base.CanPerform(action, withSender);
        }
    }
    

    Various events need to be handled:

                textField.EditingChanged += OnEditingChanged; // this one undoes any changes to the custom Entry's Text
                textField.ShouldReturn = OnShouldReturn; // this one calls Control.ResignFirstResponder();
                textField.EditingDidBegin += OnEditingBegan; // this one sets the focus
                textField.EditingDidEnd += OnEditingEnded; // this one unfocuses the custom Entry
    

    It's also necessary to stop the soft keyboard from appearing when the text field receives focus:

                if (e.PropertyName == VisualElement.IsFocusedProperty.PropertyName)
                {
                    if ((customEntry != null) && customEntry.IsFocused)
                    {
                        // hide keyboard
                        UIApplication.SharedApplication.KeyWindow.EndEditing(true);
                    }
                }
    

    On Android, the starting point for the renderer is as follows:

                    FormsEditText nativeEntryEditText = Control;
                    nativeEntryEditText.InputType = InputTypes.Null;
                    nativeEntryEditText.SetTextIsSelectable(true);
    
  • JohnHardmanJohnHardman GBUniversity mod

    @LearnEverything - Can you provide a link for where you got that Android code from please?

  • LearnEverythingLearnEverything USMember ✭✭
    edited November 2017
  • JohnHardmanJohnHardman GBUniversity mod

    @LearnEverything - Ah, I misinterpreted your previous post. I read it as you found the solution on the web, rather than you created the solution :-)

  • anna.domashychanna.domashych USMember ✭✭

    To let a user select and copy a part of the text but not allow to modify it I've used an Editor control from Xamarin.Forms, which I customized with renderers on each platform.

    Check out my post with code samples for Xamarin.Forms.

    You can find more explanation in posts about native Android and native iOS.

Sign In or Register to comment.