Thursday, August 20, 2009

Getting selected row in a Silverlight DataGrid when a button in a CellTemplate is clicked

Wow, what a long title. :) Here is a better description of what the scenario is. You have a Silverlight application that uses a DataGrid. The DataGrid is bound to a list of Person objects for example. You add a Button to the DataGrid by creating a DataGridTemplateColumn.CellTemplate and putting the Button in it. You want the button to select the row that contains that button, and you want access to the data in that row.

There are really two questions, but the very closely related:

  1. How do I select the row that contains the button that is clicked?
  2. How do I get data for the selected row?

The scenario is not that difficult or uncommon, but the solution was not obvious to me. This may be because I am new to Silverlight, and have most my experience in ASP.NET. The good news is that it is really easy.

Here is what I figured out.

First let’s show the DataGrid in the .xaml file to see how the Button was added in the first place. This example has an Edit button in the first column, and two other columns just to show that there would normally be other columns.

<data:DataGrid x:Name-"MyDataGrid" IsReadOnly="True">
<data:DataGrid.Columns>
<data:DataGridTemplateColumn Header="Edit" CanUserSort="False">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnEditQuote" Click="btnEditQuote_Click" Content="Edit"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<data:DataGridTextColumn Header="Other Column1" Binding="{Binding Column1}" Width="110" />
<data:DataGridTextColumn Header="Other Column2" Binding="{Binding Column2}" Width="110" />
</data:DataGrid.Columns>
</data:DataGrid>

The answer to both questions is really quite simple once we are armed with the knowledge that the Button has a property named DataContext. The DataContext property contains the entity related to row that is clicked. 

Here is all we need in our Edit button Click event handler. In this example, I the sender is the Button that was clicked so we can cast it to a Button. The button has a DataContext so we can get the entity related to that row. If we want to access the data in the entity we will need to cast it to the appropriate type since it is of type object when returned from btn.DataContext. When we set the MyDataGrid.SelectedItem = entity this changes the MyDataGrid.SelectedIndex property and the UI is updated with the new selected row selected (highlighted). In this case, I use a MessageBox to show that we are getting the selected row index and the correct data from the other columns.

void btnEditQuote_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
var entity = btn.DataContext as MyApp.Entities.MyEntity;
MyDataGrid.SelectedItem = entity;

MessageBox.Show("Selected row: " + MyDataGrid.SelectedIndex + " - " + entity.Column1 + ", " + entity.Column2);
}

6 comments:

yamroll said...

I have been having a similar problem and ended up doing something similar to your solution [although I like your explanation, it helps it make sense better].

The problem I'm having now though, is that my scrollbar is not being updated to scroll to show the selected item

Did you face the same problem, and did you find a solution for it?

Thanks!
-Shayla

Brent V said...

Hi Yamroll / Shavla,

Thank you for the kind feedback.

I did not have the same problem since they row was already in view. I believe there is a method call that will scroll the DataGrid to the selected row. Sorry, I only remember seeing it in a demo, so I don't know what it was called. It may have been in SilverLight 3 only, not sure.

Brent

Anonymous said...

Hi
Can you provide complete solution for this issue? :)

Anonymous said...

I understand everything but MyApp.Entities.MyEntity;

What is that? MyApp is not mentioned anywhere else on the page.

Anonymous said...

Replace MyApp.Entities.MyEntity;

to

var entity = (EntityType)btn.DataContext;

Anonymous said...

Thank u very very much. It works!