Set focus to TextBox in a TabItem when the TabItem is selected
I have a Silverlight application that has a TabControl with several TabItems. When the user selects a tab item I want to set the focus to a particular control in that TabItem. How do I do this?
I tried creating an event handler for the TabControl's SelectionChanged
event and added the following code:
private void tcTabs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (tcTabs != null)
{
switch (tcTabs.SelectedIndex)
{
case 0:
txtTextBox1.Focus();
break;
case 1:
txtTextBox2.Focus();
break;
...
}
}
}
Where txtTextBox1
and txtTextBox2
are TextBox controls in the tabs in question.
If I set a breakpoint on the Focus
method calls I see they are being called when I switch from one tab to another, but the control is not focused when the tab displays. My pr开发者_运维知识库esumption is that I need to call Focus
at some later point, but I'm at a loss as to when to call it or how.
Any help greatly appreciated.
Thanks
Fun fact of the TabControl: every time you switch tabs, the child control of the TabItem is loaded again. That is, its Loaded event is raised. So you can attach an event there instead.
That said, my preference is to use the Trigger/Action behaviors available in the Expression SDK so I can hook all this up via XAML (to me, it feels more reusable than having to attach events every time I need this).
Add a reference to System.Windows.Interactivity.dll if you don't already have one.
Use this trigger action subclass:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace SilverlightApplication1 {
public class SetFocusAction : TriggerAction<DependencyObject> {
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register( "Target", typeof( Control ), typeof( SetFocusAction ), new PropertyMetadata( null ) );
public Control Target {
get { return (Control) GetValue( TargetProperty ); }
set { SetValue( TargetProperty, value ); }
}
protected override void Invoke( object parameter ) {
if( Target != null ) {
Target.Focus();
}
}
}
}
And hook it up like so:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:SilverlightApplication1">
<Grid x:Name="LayoutRoot">
<sdk:TabControl>
<sdk:TabItem Header="Tab 1">
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<local:SetFocusAction Target="{Binding ElementName=tb1}"></local:SetFocusAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox Width="200" Height="30" x:Name="tb1"></TextBox>
</Grid>
</sdk:TabItem>
<sdk:TabItem Header="Tab 2">
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<local:SetFocusAction Target="{Binding ElementName=tb2}"></local:SetFocusAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox Width="200" Height="30" x:Name="tb2"></TextBox>
</Grid>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
</UserControl>
Note that when you first run an app with this in it the Silverlight object itself may not have focus, so you'd have to hook up javascript to focus on it manually. But once the user clicks in the Silverlight app (or your javascript sets focus) this action will do its job.
And for fun, here's a full Behavior subclass that will work with a DataForm and should work on other templated controls.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
namespace SilverlightApplication1 {
public class SetFocusBehavior : Behavior<FrameworkElement> {
public static readonly DependencyProperty TargetNameProperty =
DependencyProperty.Register( "TargetName", typeof( string ), typeof( SetFocusBehavior ), new PropertyMetadata( null ) );
private bool _setFocusOnLayoutUpdated;
public string TargetName {
get { return (string) GetValue( TargetNameProperty ); }
set { SetValue( TargetNameProperty, value ); }
}
protected override void OnAttached() {
base.OnAttached();
this.AssociatedObject.LayoutUpdated += new EventHandler( AssociatedObject_LayoutUpdated );
this.AssociatedObject.Loaded += new RoutedEventHandler( AssociatedObject_Loaded );
}
protected override void OnDetaching() {
base.OnDetaching();
this.AssociatedObject.LayoutUpdated -= new EventHandler( AssociatedObject_LayoutUpdated );
this.AssociatedObject.Loaded -= new RoutedEventHandler( AssociatedObject_Loaded );
}
private void AssociatedObject_Loaded( object sender, RoutedEventArgs e ) {
if( !FindAndSetFocus() ) {
_setFocusOnLayoutUpdated = true;
}
}
private void AssociatedObject_LayoutUpdated( object sender, EventArgs e ) {
if( _setFocusOnLayoutUpdated ) {
_setFocusOnLayoutUpdated = false;
FindAndSetFocus();
}
}
private bool FindAndSetFocus() {
var found = Find( this.AssociatedObject ) as Control;
if( found != null ) {
found.Focus();
return true;
}
return false;
}
private DependencyObject Find( DependencyObject root ) {
if( root != null ) {
if( (string) root.GetValue( FrameworkElement.NameProperty ) == TargetName ) {
return root;
}
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( root ); i++ ) {
var result = Find( VisualTreeHelper.GetChild( root, i ) );
if( result != null )
return result;
}
}
return null;
}
}
}
And the XAML:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:tk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
xmlns:local="clr-namespace:SilverlightApplication1">
<Grid x:Name="LayoutRoot">
<sdk:TabControl>
<sdk:TabItem Header="Tab 1">
<Grid>
<i:Interaction.Behaviors>
<local:SetFocusBehavior TargetName="tb1"></local:SetFocusBehavior>
</i:Interaction.Behaviors>
<TextBox Width="200" Height="30" x:Name="tb1"></TextBox>
</Grid>
</sdk:TabItem>
<sdk:TabItem Header="Tab 2">
<Grid>
<i:Interaction.Behaviors>
<local:SetFocusBehavior TargetName="tb2"></local:SetFocusBehavior>
</i:Interaction.Behaviors>
<tk:DataForm CurrentItem="sometext">
<tk:DataForm.EditTemplate>
<DataTemplate>
<TextBox Width="200" Height="30" x:Name="tb2"></TextBox>
</DataTemplate>
</tk:DataForm.EditTemplate>
</tk:DataForm>
</Grid>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
</UserControl>
精彩评论