Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Silverlight Datagrid - Row Details works great, but I want a button!

I'm using a silverlight 3 datagrid, and within it, I'm nesting related records in another control by using the rowdetails (visibilitymode = visiblewhenselected).

I really like how this works, but I'd much rather have the grid display the row details when a "+" button is pressed, much as a tree will expand when you click a node.

I tried programmatically defining the template by using resources like this:

<Grid.Resources>
    <DataTemplate x:Key="EmptyTemplate">
        <StackPanel>
            <!--<TextBlock Text="Empty Template!!!" />-->
        </StackPanel>
    </DataTemplate>
    <DataTemplate x:Key="SongTemplate">
        <StackPanel>
            <AdminControls:ArtistSongControl x:Name="ArtistSongControl" />
        </Stack>
    </DataTemplate>
</Grid.Resources>

And in the grid's LoadingRowDetails event, I'd choose which template to set by:

e.Row.DetailsTemplate = (DataTemplate)LayoutRoot.Resources["SongTemplate"];

This sortof worked, but I found that I had problems with collapsing previous rows details template, and even crashed ie8 (not sure if that's related).

Basically, I really like how the silverlight 3 datagrid works, and even how the rowdetailstemplate stuff is implemented. I simply would like to defer loading any details until a row is expanded purposely (as a tree would be). All of the 3rd party grids seem to do this, and microsoft's is soooo close. Does anyone have any idea how to solve this one?

Thanks, Dennis

like image 237
Dennis Ward Avatar asked Aug 07 '09 01:08

Dennis Ward


2 Answers

Dennis,

In case you haven't already found an answer to this, I wanted the same behavior and solved it by customizing the RowHeaderTemplate, which lets you throw a button in the header for each row. Then I implemented a handler for the button like so:

private void ToggleButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
    ToggleButton button = sender as ToggleButton;
    DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();

    if (button.IsChecked == true)
    {
        row.DetailsVisibility = Visibility.Visible;

        //Hide any already expanded row. We only want one expanded at a time for simplicity and
        //because it masks a virtualization bug in the datagrid.
        if (_expandedRow != null)
            _expandedRow.DetailsVisibility = Visibility.Collapsed;

        _expandedRow = row;
    }
    else
    {
        row.DetailsVisibility = Visibility.Collapsed;
        _expandedRow = null;
    }
}

Note that GetVisualAncestorOfType<> is an extension method I've implemented to dig into the visual tree.

You'll also need to set the datagrid's HeadersVisibility property to Row or All

like image 114
user101626 Avatar answered Sep 19 '22 20:09

user101626


here is another way to achieve what you are trying to do:

In the DataGrid set up a LoadingRow Event like this:

<data:DataGrid LoadingRow="ItemsGrid_LoadingRow" .....

In the DataGrid create a Template Column which will contain a Button such as the following:

<data:DataGridTemplateColumn CellStyle="{StaticResource DataGridCellStyle1}" CanUserReorder="False">
       <data:DataGridTemplateColumn.CellTemplate>
             <DataTemplate>
                    <Button x:Name="ViewButton" Click="ToggleRowDetailsVisibility" Cursor="Hand" Content="View Details" />                                       
             </DataTemplate>
         </data:DataGridTemplateColumn.CellTemplate>
       </data:DataGridTemplateColumn>

In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element

private void ItemsGrid_LoadingRow(object sender, DataGridRowEventArgs e)
    {
        var ViewButton = (Button)ItemsGrid.Columns[0].GetCellContent(e.Row).FindName("ViewButton");
        ViewButton.Tag = e.Row;
    }

In the Buttons EventHandler (in this case ToggleRowDetailsVisibility) we will extract the Row so that we can toggle its DetailsVisibility

In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element

private void ToggleRowDetailsVisibility(object sender, RoutedEventArgs e)
    {
        var Button = sender as Button;
        var Row = Button.Tag as DataGridRow;

        if(Row != null)
        {
            if(Row.DetailsVisibility == Visibility.Collapsed)
            {
                Row.DetailsVisibility = Visibility.Visible;

                //Hide any already expanded row. We only want one expanded at a time for simplicity and
                //because it masks a virtualization bug in the datagrid.
                if (CurrentlyExpandedRow != null)
                {
                    CurrentlyExpandedRow.DetailsVisibility = Visibility.Collapsed;
                }

                CurrentlyExpandedRow = Row;
            }
            else
            {
                Row.DetailsVisibility = Visibility.Collapsed;
                CurrentlyExpandedRow = null;
            }
        }
    }

You will notice that "CurrentlyExpandedRow", this is a Global variable, of type DataGridRow, that we store the currently expanded row in, this allows us to close that Row when a new one is to be opened.

Hope this helps.

like image 23
mknopf Avatar answered Sep 19 '22 20:09

mknopf