I’ve recently blogged on tumblr about a way to plug the missing tab-to-next-control functionaly in wp7 when using the software keyboard (SIP). I’ve reafactored the code to make it a little easier to use, plus added a little bug fix concerning isEnabled=false controls.
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentGrid" Grid.Row="1">
<StackPanel Orientation="Vertical">
<CheckBox IsTabStop="True" TabIndex="4" Content="Some check"></CheckBox>
<TextBox TabIndex="1" Text="start here" KeyUp="TextBox_KeyUp"></TextBox>
<TextBox IsTabStop="True" Text="Won't tab here cause not set tabindex"></TextBox>
<TextBox IsEnabled="False" TabIndex="2" Text="not here cause disabled" KeyUp="TextBox_KeyUp"></TextBox>
<TextBox TabIndex="3" Text="next stop here" KeyUp="TextBox_KeyUp"></TextBox>
</StackPanel>
</Grid>
A little bit of code-behind is needed;
public partial class MainPage : PhoneApplicationPage
{
private KeyboardHelper keyboardHelper;
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
this.IsTabStop = true;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
this.keyboardHelper = new KeyboardHelper(this, LayoutRoot);
}
private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
keyboardHelper.HandleReturnKey(sender);
}
}
}
…and the supporting helper class;
// Wp7Keyboard helper by @pauliom
using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Collections.Generic;
using Microsoft.Phone.Controls;
namespace Wp7Keyboard
{
/// <summary>
/// Helper to provide missing tab-to-next-control functionality
/// </summary>
public class KeyboardHelper
{
private List<Control> tabbedControls;
private Control lastTabbedControl = null;
private Panel layoutRoot;
private PhoneApplicationPage page;
/// <summary>
/// Constructor to support a phone page
/// </summary>
/// <param name="page"></param>
/// <param name="layoutRoot"></param>
public KeyboardHelper(PhoneApplicationPage page, Panel layoutRoot)
{
this.layoutRoot = layoutRoot;
this.page = page;
RefeshTabbedControls(layoutRoot);
}
/// <summary>
/// Refresh the tabbed controls collection, helpful if you dynamically alter the controls
/// </summary>
/// <param name="layoutRoot"></param>
public void RefeshTabbedControls(Panel layoutRoot)
{
this.tabbedControls = GetChildsRecursive(layoutRoot).OfType<Control>().Where(c => c.IsTabStop && c.TabIndex != int.MaxValue).OrderBy(c => c.TabIndex).ToList();
if (this.tabbedControls != null && this.tabbedControls.Count > 0)
{
this.lastTabbedControl = this.tabbedControls[this.tabbedControls.Count - 1];
}
}
// code from 'tucod'
IEnumerable<DependencyObject> GetChildsRecursive(DependencyObject root)
{
List<DependencyObject> elts = new List<DependencyObject>();
elts.Add(root);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
elts.AddRange(GetChildsRecursive(VisualTreeHelper.GetChild(root, i)));
return elts;
}
/// <summary>
/// Process a return key from the client controls key-up event
/// </summary>
/// <param name="sender"></param>
internal void HandleReturnKey(object sender)
{
Control eventSender = sender as Control;
if (eventSender != null)
{
Control thisControlInTabbedList = tabbedControls.FirstOrDefault(c => c == eventSender);
if (thisControlInTabbedList != null)
{
// what's the next control?
SetFocusOnNextControl(thisControlInTabbedList);
}
}
}
private void SetFocusOnNextControl(Control thisControlInTabbedList)
{
if (lastTabbedControl == thisControlInTabbedList)
{
// we've come the end so remove the keyboard
this.page.Focus();
}
else
{
Control nextControl = tabbedControls.FirstOrDefault(c => c.TabIndex > thisControlInTabbedList.TabIndex);
bool wasFocusSet = false;
if (nextControl != null)
{
wasFocusSet = nextControl.Focus();
}
if (!wasFocusSet)
{
SetFocusOnNextControl(nextControl);
}
}
}
}
}
Hope you find it useful, again maybe it won’t be needed after RTM, finders crossed.
BTW I’m trying out new blog engines hence the scatter of links.