So far I have looked at several questions and answers for how to get a TextBox within a DataTemplate, but none of it is working for me.
I have xaml like so (minimal example). The data template is in my section for static resources, and the ItemsControl is in the content section:
<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}" />
I need to be able set focus to the first IntegerTextBox in the Grid in the code behind. IntegerTextBox inherits the TextBox class.
I first tried writing my own method to recursively search through all UIElements for the first TextBox, but I found that the content within the DataTemplate isn't searchable in this way. The ItemsControl's children always return 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
I have tried this, similar to here and here, but the ContentPresenter is 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)
I tried getting the DataTemplate as shown here, then loading the content and searching the children for a TextBox as shown here, but it never finds a TextBox.
I have worked a couple days on this, but I cannot see what I am doing wrong. Is it some obvious mistake, or am I approaching the problem incorrectly? Thanks.
EDIT - This is how I got it to work by adding 100 ms delay:
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
You are approaching the problem incorrectly. Accessing the
DataTemplate
and searching for aTextBox
will get you nowhere. The template is only a blueprint, only when it is used somewhere (like for each item in your ItemsControl) its content is instantiated (once for each item). One of several possible solutions to set focus to the first item's textbox: In your code-behind add an eventhandler to the itemsControl:some helper code:
And btw: why do you wrap the
IntergerTextBox
in a separateGrid
for each item?