DerpyNerd
DerpyNerd

Reputation: 4803

WPF - get row index for Combobox control in DataGridTemplateColumn

I'm wondering on how to do this. I have a DataGridTemplateColumn with a simple ComboBox control inside. The combobox has a SelectionChanged event linked to it.

In the changed event I want to know what the row index of the altered row is deriving it from the altered combobox.

Am I taking the wrong approach? Here's what I have:

<DataGrid AutoGenerateColumns="False" Margin="5,10,5,5"
            x:Name="dgrMatches" ItemsSource="{Binding .}"
            CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserSortColumns="False"
            SelectionMode="Single" SelectionUnit="FullRow" IsReadOnly="False"
            RowStyle="{DynamicResource EditableRows}" CellStyle="{DynamicResource EditableTableCells}">
        <DataGrid.Columns>
            <DataGridTextColumn ... />

            <DataGridTemplateColumn Header="Legs won" Width="Auto">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox Name="cbbLegsWonA"
                                SelectedIndex="{Binding LegsWonA, Mode=TwoWay}"
                                ItemsSource="{Binding NumberOfLegs}"
                                SelectionChanged="cbbLegsWonA_SelectionChanged" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <!-- @Chris Eelmaa -->
            <DataGridTemplateColumn Header="Legs won" Width="Auto">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox Name="cbbLegsWonB"
                                SelectedIndex="{Binding LegsWonB, Mode=TwoWay}"
                                ItemsSource="{Binding NumberOfLegs}"
                                SelectionChanged="cbbLegsWonB_SelectionChanged" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <DataGridTextColumn ... />
        </DataGrid.Columns>
    </DataGrid>

And the event handler:

private void cbbLegsWonA_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ComboBox cbbLegsA = e.Source as ComboBox; // Altered combobox
    int rowIndex = -1;

    if (cbbLegsA.Tag == null)
    {
        DataGridRow row = (DataGridRow)dgrMatches.ContainerFromElement(cbbLegsA);
        rowIndex = row.GetIndex();
        cbbLegsA.Tag = rowIndex;
    }
    else
    {
        Int32.TryParse(cbbLegsA.Tag.ToString(), out rowIndex);
    }

//@ChrisEelmaa: Basically, change the bound list and refresh the items in the datagrid
//The debugger doesn't get to this point, ofcourse
SingleMatch match = matches.ElementAt(rowIndex); // Get the current match out of the bound list
match.LegsWonA = cbbLegsA.SelectedIndex; // Manually change second combobox item
dgrMatches.Items.Refresh();

...
}

This doesn't work: (DataGridRow)dgrMatches.ContainerFromElement(cbbLegsA) == null

Upvotes: 1

Views: 1523

Answers (1)

Dave M
Dave M

Reputation: 3043

(DataGridRow)dgrMatches.ContainerFromElement(cbbLegsA) == null doesn't work because the DataGrid's ItemContainer for the particular row is not the ComboBox from your DataTemplate, it is a DataGridRow that contains a 'templated' version of your ComboBox. Instead you need to use VisualTreeHelper.FindParent() to find the DataGridRow from the ComboBox (since the your ComboBox is in the visual tree of the DataGridRow, but not the logical tree). You can find the row index easily from the DataGridRow reference. However...

A much better approach as suggested in the comments is to use MVVM pattern. Your ComboBox would be bound to properties in the ViewModel. When one property changes in the ViewModel, you can easily update the other one to achieve the behavior you want without any ugly searching through the visual tree, or having a bunch of UI code behind. Instead of the logic of one ComboBox automatically updating another one being in your UI, it's in a much easier to control object model of your view (aka 'ViewModel') where it should be.

Upvotes: 1

Related Questions