views:

4437

answers:

4

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

+3  A: 

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

Just wondering how you go about deselecting the ToggleButton on the row that is selected when you click a new row's button?
Stephen Price
A: 

I suggest you take a look at the RowVisibilityChanged event on the datagrid, basically when the row visibility changes to "Visible", then load the info for the row.

Neil
+1  A: 

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.

mknopf
A: 

In addition to the answer uxrx provided here is the code for finding an ancestor

public static partial class Extensions 
{ 
    public static T FindAncestor<T>(DependencyObject obj) where T : DependencyObject 
    { 
        while (obj != null) 
        { 
            T o = obj as T; 
            if (o != null)            
                return o; 

            obj = VisualTreeHelper.GetParent(obj); 
        } 
        return null; 
    } 

    public static T FindAncestor<T>(this UIElement obj) where T : UIElement 
    { 
        return FindAncestor<T>((DependencyObject)obj); 
    } 
}
Nick