【问题标题】:Silverlight: Get first TextBox in DataTemplateSilverlight:在 DataTemplate 中获取第一个 TextBox
【发布时间】:2015-06-18 17:29:35
【问题描述】:

到目前为止,我已经查看了几个关于如何在 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 的所有 UIElement,但我发现 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 什么都没有:

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)

我尝试获取 DataTemplate,如 here 所示,然后加载内容并在子级中搜索 TextBox,如 here 所示,但始终找不到 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

【问题讨论】:

  • 延迟可能确实有效,但不建议这样做。如果你想更正那你应该使用FrameworkElementLoaded事件。
  • @Martin 在 Silverlight 中,不能保证在应用模板后发生 Loaded 事件。 但该链接确实包含有关问题潜在解决方案的其他有用信息。谢谢。

标签: vb.net silverlight datatemplate children


【解决方案1】:

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

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

【讨论】:

  • 不,我不包装IntegerTextBox。另外,您能否在 vb.net 中重写您的答案以使其符合问题?如果您不知道如何,我可能会建议进行编辑,因为我已经将其转换为 vb.net 来编写它。在进一步的测试中,加载后,元素还没有显示在屏幕上,所以它最初没有聚焦。另外,当我现在点击网格中的 anything 时,它会聚焦第一个...
  • 我不确定我是否理解您要执行的操作。您想将焦点设置到第一个文本框吗?但是什么时候应该发生呢?什么时间点?哪个事件会触发焦点变化?
  • 我在我的问题结束时编辑了我想要使用的内容。如果我处理 MyControl.GotFocus,那么只要它获得焦点,它就会选择第一个框,但这包括当我单击网格中的不同框时,这意味着我永远无法选择其他框。如果我尝试使方法 not 成为处理程序,那么由于某种原因,firstNothing。作为旁注,当焦点在加载后发生时,我认为它发生得太快了,并且内容实际上还没有被渲染,所以我需要找到一种方法将调用异步延迟 100 毫秒或其他东西。
  • 我添加了一个后台工作人员等待 100 毫秒,然后在完成后我得到第一个文本框并且它可以工作。 Silverlight 需要一段时间才能在屏幕上呈现控件,我不得不让它先完成。这可能是主要问题。
猜你喜欢
  • 2011-01-21
  • 2014-03-01
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
相关资源
最近更新 更多