Pretty much everyone who’ve tried to create some sort of text input page in a Windows Phone app has probably encountered the sometimes annoying way occluded TextBoxes are handled. Whenever a TextBox in focus on a device without a hardware keyboard, a software InputPane is shown so the user can input text. However, if the InputPane covers up the TextBox, the OS slides the whole page up so the TextBox is visible. The problem with this is anything at the top of the page is now unreachable because it has been slid out of view. Luckily, I’ve written a control that mimics more closely to the native OS’ way of handling occluding InputPanes and it’s now available in my toolkit QKit.
The MaterialSAMPLE SOLUTION – see it in action
NUGET – download and use
Let’s say we want to create a chat app. The basic layout of a chat page is a history of messages in chronological order and a TextBox that will allow the user to compose and send a new message. This, obviously, describes the exact layout as the Messaging app found in the OS. If we create this ourselves, we can have it working properly with a TextBox, a ListView, and simple TextBlock for the contact name at the top with little issues. I’ve even created custom templates for the conversation history to look more like the native Messaging app.
Now, in its default state, it looks and works as expected. The conversation history is a scrollable ListView while the TextBox and the TextBlock “John Doe” at the top do not move and is intended to be continuously visible. It all goes downhill when we tap on the TextBox at the bottom show the InputPane.
The first issue we can see is that the top TextBlock “John Doe” is now no longer visible. The user cannot easily check who they’re messaging unless the they close the InputPane. This is due to the sliding up of the page to ensure the TextBox is visible. Not only is any top content unreachable when the InputPane is shown, we also cannot scroll up to the oldest message in the history list for the same reason (the top message reads “Hello”).
With my InputAwarePanel, whenever the InputPane is shown, the page will no longer slide up. Instead, my InputAwarePanel will resize to make room for the InputPane and essentially reduces the working visible area of the page to the region below the StatusBar and above the InputPane.
Notice how both the TextBlock at the top and the first message “Hello” is visible with my panel. The basic set up is as simple as this:
<q:InputAwarePanel> your content here... </q:InputAwarePanel>
The simplest way to use this is to just put all of the page’s content inside the InputAwarePanel. This way, whenever the InputPane shows itself, the whole page acts as if it’s shrinking upwards to make room for the InputPane. Keep in mind that this no longer ensures the focused TextBox will be visible so you have to manually scroll it into view if it’s in a ScrollViewer or do other additional work. In my sample app, I have additional code in the code behind to make sure everything acts properly.
How It Works
The InputAwarePanel has three different animation modes for flexibility. The first and easiest animation mode is None. In this mode, the InputAwarePanel simply pads the bottom of the its contents to make room for the InputPane instantly.
This animation mode is identical to how the None animation mode works except that it is done over a period of time in a similar manner as how the InputPane shows and hide. It has pretty much the same easing and duration as the animation for how the InputPane shows and hides. This animation mode is most like how the native OS handles the InputPane like in the Messaging app. Basically, the end result looks like the InputPane pushes up against the page and forces it to shrink to make room for it. The only issue with this mode is that it needs to run on the UI thread in order to achieve this effect. Because of this, the animation can be choppy in certain scenario and layout set ups. This is, in most cases, the preferred animation mode but I have another animation mode in case the performance on this isn’t ideal.
Finally, this animation mode runs entirely on the compositor thread so it smoother even when the UI thread is blocked by some other process or heavy load. This animation is completely different in order to accomplish this. First, the content of the page is instantly resized to the available height that will be available after the InputPane is shown. Then, the content of the panel slides upward to make room for the InputPane. This is similar to how WP apps normally handles the InputPane but with the added resizing before the sliding. The resizing ensures all of the content is in the visible bounds still. The problem with this is because it does the resizing first instantly, any top item included in the panel will jump around before ending with the correct layout for the InputPane.
In the sample app, there’s a page where you can switch between the three animation modes to see the difference for yourself.