2011-07-25 77 views
2

我有一个Silverlight应用程序,它有一个带有多个TabItem的TabControl。当用户选择一个选项卡项目时,我想将焦点设置为该TabItem中的特定控件。我该怎么做呢?当TabItem被选中时,将焦点设置为TabItem中的TextBox

我试图创造的的TabControl的SelectionChanged事件的事件处理程序,并添加以下代码:

private void tcTabs_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    if (tcTabs != null) 
    { 
     switch (tcTabs.SelectedIndex) 
     { 
      case 0: 
       txtTextBox1.Focus(); 
       break; 

      case 1: 
       txtTextBox2.Focus(); 
       break; 

      ... 
     } 
    } 
} 

txtTextBox1txtTextBox2是有问题的选项卡TextBox控件。

如果我在Focus方法调用上设置断点,我发现它们在从一个选项卡切换到另一个选项卡时被调用,但是在显示选项卡时控件未聚焦。我的推测是,我需要在稍后的时间致电Focus,但我不知道该怎么称呼它。

任何帮助非常感谢。

感谢

回答

6

中的TabControl的有趣的事实:你每次切换标签时,的TabItem的子控件再次加载。也就是说,它的Loaded事件被提出。所以你可以附加一个事件。

这就是说,我的首选是使用表达式SDK中提供的触发器/动作行为,这样我就可以通过XAML将所有这些都绑定起来(对我来说,感觉比每次需要时都附加事件更具可重用性) 。

如果您还没有System.Windows.Interactivity.dll,请添加一个引用。

使用此触发动作子类:

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(); 
      } 
     } 
    } 
} 

而且把它挂像这样:

<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> 

注意,当你第一次运行它与该应用程序Silverlight的对象本身可能没有焦点,所以你必须挂钩的JavaScript来专注于手动。但是,一旦用户点击Silverlight应用程序(或您的JavaScript设置焦点),此操作将完成其工作。

为了好玩,这里有一个完整的行为子类,它将与DataForm一起工作,并且应该在其他模板化控件上工作。

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; 
     } 
    } 
} 

而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> 
+0

感谢您的见解的答复和代码段。这适用于TabItem中的“直接”控件,但在一些TabItems中,我需要将焦点设置为DataForm中的TextBox。我可以使用'DataFormID.FindNameInContent'以编程方式执行此操作,但有没有办法将这种功能烘焙到触发器中? –

+0

所有模板化控件(如DataForm)都会给你带来问题。您可以更改操作以使用VisualTreeHelper递归搜索给定的名称,但模板不一定会通过Loaded准备好,因此您必须连接到LayoutUpdated(并确保您只做一次每个加载的事件调用)。 –

+0

添加了一个适用于DataForm的版本。 –