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

LearnEverythingLearnEverything USMember ✭✭
edited November 8 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 ✭✭✭✭✭
    edited November 8

    @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 ✭✭✭✭✭

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

  • LearnEverythingLearnEverything USMember ✭✭
    edited November 8
  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

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

Sign In or Register to comment.