Make a custom Editor render to bind Focus

MulflarMulflar ESUniversity ✭✭

My problem is I want to manage the Focus of a Editor from Mvvm (I want to focus or not on Editor depending from a constructor parameter). I was looking for options but the only one it seems functional is to use MessagingCenter, but, as the event will only be fired once (on launch), and I'm thinking on use this control on more screens I decided that better option is to use a Custom Renderer (not sure if is a good option).

I'm searching for custom renderer information but I can't find any topic that helps me.
Can anyone give me some info.

Best Answer

  • MulflarMulflar ESUniversity ✭✭
    Accepted Answer

    Yaw! I solved by myself. I noticed that I always want to get focus when the control becomes visible, so my render in android looks like:

    class FocusableEditorRenderer : EditorRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);
        }
    
        protected override void OnVisibilityChanged(Android.Views.View changedView, [GeneratedEnum] ViewStates visibility)
        {
            base.OnVisibilityChanged(changedView, visibility);
            if (visibility == ViewStates.Visible)
            {
                changedView.RequestFocus();
                InputMethodManager inputMethodManager = this.Control.Context.GetSystemService(Android.Content.Context.InputMethodService) as InputMethodManager;
                inputMethodManager.ShowSoftInput(this.Control, ShowFlags.Forced);
                inputMethodManager.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
            }
        }
    }
    

Answers

  • N_BauaN_Baua INMember ✭✭✭✭✭

    Hi @Mulflar

    Why not just use the event like shown below.

       SomeContentPage.Appearing += (object sender, System.EventArgs e) => SomeEntry.Focus ();
    

    Do you have some specific reason to control the focus(may be showing-popping up keyboard or something)?

    -- N Baua

  • MulflarMulflar ESUniversity ✭✭

    The functionality is, I have a listview, if the user taps the row, it goes to the detal with the Editor hidded and unfocused. If you press a "edit" button then the editor appears. But, if you press a button in the listview row, it goes to the detail with the editor visible, the button text changed to "save" and (I want) the editor focused. All of this is managed in the viewmodel, and I want to manage the focus in the viewmodel too.

  • N_BauaN_Baua INMember ✭✭✭✭✭

    @Mulflar said:
    The functionality is, I have a listview, if the user taps the row, it goes to the detal with the Editor hidded and unfocused. If you press a "edit" button then the editor appears. But, if you press a button in the listview row, it goes to the detail with the editor visible, the button text changed to "save" and (I want) the editor focused. All of this is managed in the viewmodel, and I want to manage the focus in the viewmodel too.

    Is your Detail shown is in another View/Content page other than the View which holds ListView? (Post some screenshot)

  • MulflarMulflar ESUniversity ✭✭

    Ok lets see, I have this list in a view:

    <ListView
                x:Name="lstView"
                Grid.Row="1"
                Margin="10,0"
                CachingStrategy="RecycleElement"
                GroupDisplayBinding="{Binding LongName}"
                GroupShortNameBinding="{Binding ShortName}"
                HasUnevenRows="True"
                IsGroupingEnabled="true"
                IsRefreshing="{Binding IsLoading}"
                IsVisible="{Binding WorklistItems.Count, Converter={StaticResource CountToBoolConverter}}"
                ItemsSource="{Binding WorklistItems}"
                SeparatorColor="Transparent">
                <ListView.Behaviors><!-- This is the command to open the detail in a "normal" way-->
                    <behaviors:EventToCommandBehavior
                        Command="{Binding ToDetailCommand}"
                        EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}"
                        EventName="ItemTapped" />
                </ListView.Behaviors>
                <ListView.GroupHeaderTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ViewCell.View>
                                <templates:IcHeader_Template />
                            </ViewCell.View>
                        </ViewCell>
                    </DataTemplate>
                </ListView.GroupHeaderTemplate>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ViewCell.ContextActions>
                                <MenuItem
                                    Command="{Binding BindingContext.ReadOrCloseCommand, Source={x:Reference WorkList_Page}}"
                                    CommandParameter="{Binding .}"
                                    IsDestructive="False"
                                    Text="{Binding Pendiente_opcion}" />
                            </ViewCell.ContextActions>
                            <ViewCell.View>
                <!-- This is the template with a binding to manage the button inside the row-->
                                <templates:IcListItem_Template ParentContext="{Binding BindingContext, Source={x:Reference lstView}}" />
                            </ViewCell.View>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    

    This is the relevant code of the detail view

        <!-- This is the button to change from edit to save mode-->
         <Render:ShadowFrame
                            Grid.Column="3"
                            Padding="-10"
                            OutlineColor="{StaticResource VerdeQuiron}">
                            <Render:ShadowFrame.GestureRecognizers>
                                <TapGestureRecognizer Command="{Binding SetEditingCommand}" />
                            </Render:ShadowFrame.GestureRecognizers>
                            <Label
                                HorizontalOptions="Center"
                                Text="{Binding EntryButtonText}"
                                VerticalOptions="Center" />
                        </Render:ShadowFrame>
    
                        <Grid
                            Grid.Row="1"
                            Grid.Column="1"
                            Grid.ColumnSpan="3"
                            Margin="0,10,0,0">
            <!-- This is the content in view mode-->
                            <ScrollView IsVisible="{Binding IsEditing, Converter={StaticResource InverseBoolConverter}}">
                                <Label Text="{Binding Detalle.Comentarios}" />
                            </ScrollView>
            <!-- This is the content in edit mode-->
                            <ScrollView IsVisible="{Binding IsEditing}">
                                <FocusableEditor
                                    HeightRequest="180"
                                    Text="{Binding Detalle.Comentarios}"
                                    VerticalOptions="Center" />
                            </ScrollView>
                        </Grid>
    

    And that's the relevant code of the initialization of the viewmodel of the detail. As I'm currently just trying how to do the feature I just changed the id from long to string to change the source of the call

    public override Task InitializeAsync(object navigationData)
        {
            if (navigationData is long || navigationData is int) //that's when the detail is called in view mode
            {
                Title = AppResources.Title;
                SetStartProps();
                FillDetail((long)navigationData);
            }
            else if (navigationData is string) //that's when the detail is called in edit mode
            {
                Title = AppResources.Title;
                SetStartProps();
                FillDetail(long.Parse((string)navigationData));
                SetEditing();
            }
            return base.InitializeAsync(navigationData);
        }
    void SetStartProps() //here I initialice the changeable parts of the view
        {
            viewExtra = false;
            isEditing = false;
            Rotation = 0;
        }
        void FillDetail(long id) //here I get and fill the data
        {
            IsBusy = true;
            Detalle = servicio.getWorklist()[(int)id - 1];
            IsBusy = false;
        }
    private void SetEditing() //here is where I switch from editing to non editing 
        {
            IsEditing = !isEditing;
            RaisePropertyChanged(() => Detalle);
        }
    

    In the last function SetEditing is where I want to, if IsEditing is true, call the focus of the entry object.

  • N_BauaN_Baua INMember ✭✭✭✭✭
    edited October 4

    Hmm,

    I was just thinking (may be too bold) what if your FocusableEditor can just declare a bool property like isFocused set to true/false and while isEditing the binding triggers the event as SomeEntry.Focus ();

    What I can see is you need to tweak your code based on some Triggers (Event Triggers you should be looking for I guess).

    -- N Baua

  • MulflarMulflar ESUniversity ✭✭

    @N_Baua said:
    I was just thinking (may be too bold) what if your FocusableEditor can just declare a bool property like isFocused set to true/false and while isEditing the binding triggers the event as SomeEntry.Focus ();

    That's exactly what I want to do, but I don't know how to do it in a render. All the examples I found are more related to override existing content rather than add new.

  • MulflarMulflar ESUniversity ✭✭
    Accepted Answer

    Yaw! I solved by myself. I noticed that I always want to get focus when the control becomes visible, so my render in android looks like:

    class FocusableEditorRenderer : EditorRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);
        }
    
        protected override void OnVisibilityChanged(Android.Views.View changedView, [GeneratedEnum] ViewStates visibility)
        {
            base.OnVisibilityChanged(changedView, visibility);
            if (visibility == ViewStates.Visible)
            {
                changedView.RequestFocus();
                InputMethodManager inputMethodManager = this.Control.Context.GetSystemService(Android.Content.Context.InputMethodService) as InputMethodManager;
                inputMethodManager.ShowSoftInput(this.Control, ShowFlags.Forced);
                inputMethodManager.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
            }
        }
    }
    
Sign In or Register to comment.