I'm developing an application in C#/WPF using the MVVM pattern. I have a screen which show some supports. Each support have a collection of some spaces. Users must be able to enter some informations on spaces.
I have a GlobalViewModel as datacontext on my view. It contains an ObservableCollection of SupportViewModels and a current SupportViewModel, these objects are binded to a DataGrid. Below I use an ItemsControl with a dataTemplate to show the spaces. The ItemsControl.ItemsSource is binded to CurrentSupport.Spaces (=an ObservableCollection of SpaceViewModels).
If I simplify my view it, would be something like this
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<telerik:RadGridView Grid.Row="0"
x:Name="supportGrid"
CanUserDeleteRows="False"
CanUserInsertRows="False"
ItemsSource="{Binding Supports}"
SelectedItem="{Binding CurrentSupport}"
AutoGenerateColumns="False" >
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Code" DataMemberBinding="{Binding Code}"/>
<telerik:GridViewDataColumn Header="State" DataMemberBinding="{Binding State, Converter={StaticResource EnumValueToTextConverter}}"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
<Grid Grid.Row="1" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<telerik:Label Grid.Column="0" Content="Code" />
<telerik:RadWatermarkTextBox Grid.Column="1" Text="{Binding CurrentSupport.Code, Mode=OneWay}" IsReadOnly="True" />
</Grid>
<ItemsControl Grid.Row="2"
x:Name="spacesItems"
ItemsSource="{Binding CurrentSupport.Spaces}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<telerik:GroupBox Grid.ColumnSpan="2"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Header="{Binding Code, Mode=OneWay}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<telerik:Label Grid.Column="0" Content="Ref." />
<telerik:RadMaskedTextInput x:Name="findSearchBox"
Grid.Column="1"
Mask=""
HorizontalAlignment="Stretch"
SelectionOnFocus="SelectAll">
</telerik:RadMaskedTextInput>
<telerik:RadButton Grid.Column="2"
Command="{Binding FindCommand}"
CommandParameter="{Binding Text, ElementName=findSearchBox}"
Content="Search" />
<!-- *** Some other fields *** -->
</Grid>
</telerik:GroupBox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I want that when the current support is changed (by the user or throuph the code), then the 1st TextBox (which is defined in the spacesItems DataTemplate) of the 1st space is selected.
To select the "findSearchBox" on the 1st space, I found this How to focus a datatemplated TextBox in the first element of an ItemsControl in a Window, when the Window is opened? (C#, WPF) .
private void SetFocusOnSearchBox()
{
if (!spacesItems.HasItems)
return;
DependencyObject cp = spacesItems.ItemContainerGenerator.ContainerFromIndex(index: 0);
RadMaskedTextInput tb = FindVisualChild<RadMaskedTextInput>(cp);
if (tb != null)
{
FocusManager.SetFocusedElement(spacesItems, tb);
}
}
It works well if I call the method on a button click but when I call the method on the SelectionChanged event of my DataGrid it doesn't work because spacesItems.ItemContainerGenerator.ContainerFromIndex(0)
do not have any children yet at this time. I also try to call it on CurrentSupport propertyChanged event but this is the same.
When can I call this ? Or should I do in another way ?