Google WindowsPhone KeyBoard: Prevent page screen scrolling up when soft keyboard is opened(C#-XAML) | SubramanyamRaju Xamarin & Windows App Dev Tutorials

Monday 28 April 2014

WindowsPhone KeyBoard: Prevent page screen scrolling up when soft keyboard is opened(C#-XAML)

Problem:
​Generally in windows phone when TextBox is focus soft-keyboard will be open ,but here problem is by default page screen to shift upwards when keyboard opened and again shift previous position when user closed the keyboard by tap on screen.Actually this question is raised at MSDN WindowsPhone Development Forums.Even  lot of times this question is raised by developers and however more resource's available for solving this issue. 
   Requirement :Page UI having header,body and footer. In page UI there is a TextBox  just above the footer. When user focus on this TextBox the page header is going to invisible.But user doesn't want to page header shift upwards when keyboard is opened


Note: This Sample is targeted on WindowsPhone 7.1

SourceFile at:WpKeyBoardIssueSample

Solution:


 Step1: 
Take Grid layout with 3 rows for Header,Body,Footer.
    

XAML
 <Grid x:Name="LayoutRoot" Grid.Row="1" Margin="12,0,12,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Name="Header" Background="#FF1DA8D1">
            <TextBlock Text="Header" HorizontalAlignment="Center" FontSize="40" />
        </StackPanel>
        <ScrollViewer Grid.Row="1" Background="Orange" Name="Body">
           <Grid >
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                </Grid.RowDefinitions>
                <TextBox Grid.Row="0" Name="txtMessage"
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox VerticalAlignment="Bottom" Grid.Row="1" Name="txtMessage1"
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox Grid.Row="2" 
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox VerticalAlignment="Bottom" Grid.Row="3" 
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox Grid.Row="4" 
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox VerticalAlignment="Bottom" Grid.Row="5" 
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox Grid.Row="6" 
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                <TextBox VerticalAlignment="Bottom" Grid.Row="7" 
                        TextWrapping="Wrap"
                        AcceptsReturn="True"
                        TextChanged="txtMessage_TextChanged"
                        GotFocus="txtMessage_GotFocus"
                        LostFocus="txtMessage_LostFocus"
                        Tap="txtMessage_Tap" />
                  <StackPanel Visibility="Collapsed" Name="footerkeyboard"
 VerticalAlignment="Bottom"               Grid.Row="8" Background="#FF1DA8D1">
                    <TextBlock  Text="Footer" HorizontalAlignment="Center" FontSize="40" />
                </StackPanel>
            </Grid>
         </ScrollViewer>

        <!--mimic the keyboard taking up space-->

        <Grid Grid.Row="2" Name="FooterPlaceholder" Visibility="Collapsed"/>
        <StackPanel Name="withoutkeyboardfooter"  Grid.Row="2" Background="#FF1DA8D1">
            <TextBlock  Text="Footer" HorizontalAlignment="Center" FontSize="40" />
        </StackPanel>

<Grid>
Step2: In Above xaml code main part is Body(Grid.Row=1) content which is having scrollviewer with eight TextBox's for testing purpose.So that here every TextBox is having  following events to handle keyboard issue.
  • TextChanged="txtMessage_TextChanged"
  • GotFocus="txtMessage_GotFocus"
  • LostFocus="txtMessage_LostFocus"
  • Tap="txtMessage_Tap"
 Here we need to use these events for manually specifying the scroll positions.Generally on initially tapping a populated Textbox, scrolling to the point at which the user wants the cursor to be  specifically to an area that would be hidden after the keyboard is shown  requires some manual means to accomplish. This is handled on the Tap event.
C#
double tapOffset;

private void txtMessage_Tap(object sender, System.Windows.Input.
GestureEventArgs e)

  {
       tapOffset = e.GetPosition(txtMessage).Y - 80;
  }
When typing multiple line textbox by default  entered page will  be upwards,so fix this, keep resetting theApplicationRootFrame’s RenderTransformation property whenever the TextBox gets Focus.
C#
 private void txtMessage_GotFocus(object sender, RoutedEventArgs e)
   {
      ((App)Application.Current).RootFrame.RenderTransform =
 new CompositeTransform();

      FooterPlaceholder.Visibility = Visibility.Visible;

      withoutkeyboardfooter.Visibility = Visibility.Collapsed;

      footerkeyboard.Visibility = Visibility.Visible;
   }
Note: The keyboard height differs between different resolutions, so some checking is required to find the correct keyboard height, which can be done on the page’s Loaded event.
C#
void MainPage_Loaded(object sender, RoutedEventArgs e)
  {
    var deviceWidth = this.ActualWidth; 

    var isHdDevice = (deviceWidth > 500 ? true : false);

      if (isHdDevice)

                keyboardHeight = 540;
            else
                keyboardHeight = 336;

     //this will be used to offset other controls on the page into the viewable area
    FooterPlaceholder.Height = keyboardHeight;
  } 
In our UI,every textbox  is having properties like TextWrapping="Wrap" &  AcceptsReturn="True".This is required to so that as text wraps down to new lines the caret is kept in view. We do this by manually scrolling the ScrollViewer as the TextBox size increases. This is done in the TextChangedevent of the TextBox.

C#
private void txtMessage_TextChanged(object sender, TextChangedEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                double CurrentInputHeight = txtMessage.ActualHeight;
 
                //after the user starts typing text, text will eventually wrap to the next line
                //this ensures the textbox height doesnt sink below the bottom of the scrollviewer
                if (CurrentInputHeight > InputHeight)
                {
                    Body.ScrollToVerticalOffset(Body.VerticalOffset + CurrentInputHeight - InputHeight);
                }
 
                InputHeight = CurrentInputHeight;
            });
        }
And we need to do like this  at LostFocus  Event
C#

 private void txtMessage_LostFocus(object sender, RoutedEventArgs e)
        {
            //hide the keyboard placeholder from screen
            //allowing the scrollviewer to re-occupy the available area again
            this.FooterPlaceholder.Visibility = Visibility.Collapsed;
            withoutkeyboardfooter.Visibility = Visibility.Visible;
            footerkeyboard.Visibility = Visibility.Collapsed;

        }

Step3: In Above xaml code another main part is (Grid.Row=2) content which is having Grid(FooterPlaceholder) & StackPanel(withoutkeyboardfooter) .Actually this row content is only for (Footer) which is mimic the keyboard taking up space.

  • Create a FooterPlaceholder UI element that simulates the space the keyboard takes up on the page, which effectively squishes the ScrollViewer into the available space.
  • Only display when the TextBox gets focus (i.e. only the keyboard is visible) and hide it when it doesn’t, using the GotFocus and LostFocus events.
  • Note the keyboard height differs between different resolutions, so some checking is required to find the correct keyboard height, which can be done on the page’s Loaded event.
 Output Screens: 
  
 


Note: Please share your thoughts,what you think about this post,Is this post really helpful for you?otherwise it would be very happy ,if you have any thoughts for to implement this requirement in any another way?I always welcome if you drop comments on this post and it would be impressive.


Follow me always at  

Have a nice day by  :)



C#
void
C#
 private void txtMessage_TextChanged(object sender, TextChangedEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                

2 comments:

  1. Great share! But there is one problem, similar solution doesn't work for landscape mode :|. Can you please guide me in achieving similar soln. for landscape mode?

    ReplyDelete
  2. Hi I am using flipview Control(UWP) and want to stop flip of records in mobile over touch not want to disable. Do you have any exposer on it.

    ReplyDelete

Search Engine Submission - AddMe