2011-07-25 75 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的版本。 –