中的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>
感謝您的見解的答覆和代碼段。這適用於TabItem中的「直接」控件,但在一些TabItems中,我需要將焦點設置爲DataForm中的TextBox。我可以使用'DataFormID.FindNameInContent'以編程方式執行此操作,但有沒有辦法將這種功能烘焙到觸發器中? –
所有模板化控件(如DataForm)都會給你帶來問題。您可以更改操作以使用VisualTreeHelper遞歸搜索給定的名稱,但模板不一定會通過Loaded準備好,因此您必須連接到LayoutUpdated(並確保您只做一次每個加載的事件調用)。 –
添加了一個適用於DataForm的版本。 –