中的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的版本。 –