1

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 ?

Corabox
  • 157
  • 10
  • Messing with the container generator is always a tough task. There are some workarounds but for very limited cases. If you have a small number of items, putting the ItemsControl in a non-virtualizing panel will guarantee your item containers are always instantiated. Another trick could be to first scroll to top so the containers get generated and then select the first one. Also there is a known bug (google it) with ContainerGenerator where you need to access the Children property before being able to obtain anything... – shadow32 Jun 21 '18 at 08:29
  • Thanks for your comment @shadow32. I read some posts about virtualized and not virtualized panel but I didn't found a lot a informations about it. I will really never have a lot of spaces so I don't need a virtualized panel. From what I understand, I should set my wrapPanel not virtualized but I don't see any property related to that. **How could I do this ?** – Corabox Jun 21 '18 at 09:08
  • WrapPanel is not virtualizing, I was listing solutions in general. You may find this similar question helpful in your case: https://stackoverflow.com/questions/3289116/why-does-itemcontainergenerator-return-null – shadow32 Jun 21 '18 at 09:44
  • Unfortunately, it doesn't help because in my case, `spacesItems.ItemContainerGenerator.ContainerFromIndex(index: 0)` is not null. This is `VisualTreeHelper.GetChildrenCount(container)` which return zero. I really think that it's because I call it too soon. Like I'said in the post, if I call the `SetFocusOnSearchBox()` in a test button click, it works well. – Corabox Jun 21 '18 at 10:08
  • Nobody has an answer for me ? I still have the problem... – Corabox Jul 10 '18 at 06:08

0 Answers0