Silverlight:在DataTemplate中获取第一个TextBox

时间:2015-06-18 17:29:36

标签: vb.net silverlight datatemplate children

到目前为止,我已经查看了有关如何在DataTemplate中获取TextBox的几个问题和答案,但它们都不适用于我。

我有像这样的xaml(最小的例子)。数据模板在我的静态资源部分中,ItemsControl在内容部分中:

<DataTemplate x:Key="GridTemplate">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="140" />
        </Grid.ColumnDefinitions>
        <sdk:IntegerTextBox DataField="Model.DataField" Width="90" SelectAllOnFocus="True" />
    </Grid>
</DataTemplate>

<ItemsControl x:Name="MyControl" ItemsSource="{Binding MyList}" ItemTemplate="{StaticResource GridTemplate}" />

我需要能够将焦点设置为后面代码中Grid中的第一个IntegerTextBox。 IntegerTextBox继承TextBox类。

我首先尝试编写自己的方法来递归搜索第一个TextBox的所有UIElements,但我发现DataTemplate中的内容不能以这种方式搜索。 ItemsControl的子节点总是返回Nothing:

Private Function FirstTextBox(ByVal uiElement As UIElement) As TextBox
    Dim textBox As TextBox = TryCast(uiElement, TextBox)
    If textBox IsNot Nothing Then Return textBox
    Dim panel As Panel = TryCast(uiElement, Panel)
    If panel IsNot Nothing Then
        For Each child As UIElement In panel.Children
            textBox = FirstTextBox(child)
            If textBox IsNot Nothing Then Return textBox
        Next
    End If
    Dim itemsControl As ItemsControl = TryCast(uiElement, ItemsControl)
    If itemsControl IsNot Nothing Then
        For i As Integer = 0 To itemsControl.Items.Count
            textBox = FirstTextBox(CType(itemsControl.ItemContainerGenerator.ContainerFromIndex(i), UIElement))
            If textBox IsNot Nothing Then Return textBox
        Next
    End If
    Return textBox
End Function

我尝试过此操作,类似于herehere,但ContentPresenter是Nothing:

Dim contentPresenter = CType(MyControl.ItemContainerGenerator.ContainerFromIndex(0), ContentPresenter)
Dim textbox As TextBox = CType(CType(contentPresenter.ContentTemplate.LoadContent(), Panel).Children.First(Function(c) TypeOf c Is TextBox), TextBox)

我尝试按照here显示DataTemplate,然后加载内容并在子框架中搜索显示here的TextBox,但它永远找不到TextBox。

我已经工作了几天,但我看不出我做错了什么。这是一个明显的错误,还是我错误地解决了这个问题?感谢。

编辑 - 这是我通过添加100毫秒延迟让它工作的方式:

Private Function FindDescendant(Of TDescendant As DependencyObject)(ByVal obj As DependencyObject) As TDescendant
    Dim all = VisualTreeExtensions.GetVisualDescendants(obj)
    Dim first = all.OfType(Of TDescendant)().FirstOrDefault()
    Return first
End Function

Private Sub bw_DoWork(ByVal sender As Object, ByVal e As ComponentModel.DoWorkEventArgs)
    System.Threading.Thread.Sleep(100)
End Sub

Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As ComponentModel.RunWorkerCompletedEventArgs)
    Dim firstTextBox = FindDescendant(Of IntegerTextBox)(MyControl)
    If firstTextBox IsNot Nothing Then firstTextBox.Focus()
End Sub

Private Sub SetFocus()
    Dim bw As New ComponentModel.BackgroundWorker
    bw.WorkerReportsProgress = True
    bw.WorkerSupportsCancellation = True
    AddHandler bw.DoWork, AddressOf bw_DoWork
    AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
    bw.RunWorkerAsync()
End Sub

1 个答案:

答案 0 :(得分:1)

您正在接近问题。访问DataTemplate并搜索TextBox将无处可寻。 模板只是一个蓝图,只有当它在某个地方使用时(比如ItemsControl中的每个项目),其内容才会被实例化(每个项目一次)。 将焦点设置到第一个项目文本框的几种可能解决方案之一: 在您的代码隐藏中,向itemsControl添加一个eventhandler:

MyControl.GotFocus += (sender, args) =>
{
    var firstItemTextBox = MyControl.FindDescendant<IntegerTextBox>();
    if ( firstItemTextBox != null ) firstItemTextBox.Focus();
};

一些帮助代码:

//you need System.Windows.Controls.Toolkit.dll from the SilverlightToolkit for the class VisualTreeExtensions
using System.Windows.Controls.Primitives;
public static class ControlExtensions
{
    public static TDescendant FindDescendant<TDescendant>(this DependencyObject element)
    {
        return element.GetVisualDescendants().OfType<TDescendant>().FirstOrDefault();
    }
}

顺便说一下:为什么要将IntergerTextBox包装在每个项目的单独Grid中?