Tuesday, August 17, 2010

Silverlight DataGrid Column Width Behavior

I was having a problem with the content in my DataGrid.RowDetailsTemplate being truncated based off of the width of my columns instead of the width of the DataGrid.
clip_image001
Here is some xaml to produce this behavior.


<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<ScrollViewer x:Name="scv" HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" >
<sdk:DataGrid x:Name="dgItems" HorizontalAlignment="Stretch" AutoGenerateColumns="False" ItemsSource="{Binding Collection}">
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="2" MinHeight="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Left" Grid.Row="0" Text="Bio" Margin="4"/>
<TextBlock HorizontalAlignment="Left" Grid.Row="0" Grid.Column="1" Margin="4" Text="{Binding Bio}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</sdk:DataGrid.RowDetailsTemplate>
<sdk:DataGrid.Columns>                
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>

<sdk:DataGridTextColumn Binding="{Binding Name}" Header="Name" />
<!--
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>                           
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
-->
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</ScrollViewer>
</Grid>



If I add a dummy template column, and the Datagrid width behavior, it now looks like:

image


And here is the xaml with the extra column and the Datagrid Column Width Trigger.
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource SampleDataSource}}">
        <ScrollViewer x:Name="scv" HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" >
            <sdk:DataGrid x:Name="dgItems" HorizontalAlignment="Stretch" AutoGenerateColumns="False" ItemsSource="{Binding Collection}">
            <sdk:DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <Grid Margin="2" MinHeight="40" Width="Auto" HorizontalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock HorizontalAlignment="Left" Grid.Row="0" Text="Bio" Margin="4"/>
                        <TextBlock HorizontalAlignment="Stretch" Width="Auto" Grid.Row="0" Grid.Column="1" Margin="4" Text="{Binding Bio}" TextWrapping="Wrap" />
                    </Grid>
                </DataTemplate>
            </sdk:DataGrid.RowDetailsTemplate>
            <sdk:DataGrid.Columns>                
                <sdk:DataGridTemplateColumn>
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                </sdk:DataGridTemplateColumn>
                
                <sdk:DataGridTextColumn Binding="{Binding Name}" Header="Name" />
                
                <sdk:DataGridTemplateColumn>
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>                           
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                </sdk:DataGridTemplateColumn>
                
            </sdk:DataGrid.Columns>
             <i:Interaction.Triggers>
             <i:EventTrigger EventName="Loaded">
             <this:DataGridSpreadColumnsAction/>
             </i:EventTrigger>
             <i:EventTrigger EventName="SizeChanged">
             <this:DataGridSpreadColumnsAction/>
             </i:EventTrigger>
             </i:Interaction.Triggers>
        </sdk:DataGrid>
        </ScrollViewer>
    </Grid>

The code for the DataGridSpreadColumnsAction is.
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;

    public class DataGridSpreadColumnsAction : TriggerAction<DataGrid>
    {
        public static DependencyProperty ColumnSpreadProperty = DependencyProperty.Register("ColumnSpread", typeof(DataGridColumnSpread), typeof(DataGridSpreadColumnsAction), new PropertyMetadata(DataGridColumnSpread.LastColumn));

        // Space available to fill ( -18 Standard vScrollbar)
        private const int ScrollbarWidth = 18;

        public enum DataGridColumnSpread
        {
            LastColumn,
            Evenly
        }

        public DataGridColumnSpread ColumnSpread
        {
            get { return (DataGridColumnSpread)GetValue(DataGridSpreadColumnsAction.ColumnSpreadProperty); }
            set { SetValue(DataGridSpreadColumnsAction.ColumnSpreadProperty, value); }
        }

        protected override void Invoke(object parameter)
        {
            DataGrid associatedDataGrid = this.AssociatedObject as DataGrid;

            if (associatedDataGrid != null
                && associatedDataGrid.RenderSize.Width != 0
                && associatedDataGrid.Columns.Count > 0)
            {
                switch (this.ColumnSpread)
                {
                    case DataGridColumnSpread.LastColumn:
                        DistributeToLastColumn(associatedDataGrid);
                        break;
                    case DataGridColumnSpread.Evenly:
                        DistributeEvenly(associatedDataGrid);
                        break;
                    default:
                        DistributeToLastColumn(associatedDataGrid);
                        break;
                }
            }
        }

        private static void DistributeToLastColumn(DataGrid dataGrid)
        {
            double sumOfColumnWidths = SumColumnWidths(dataGrid);

            double space_available = (dataGrid.RenderSize.Width - ScrollbarWidth) - sumOfColumnWidths;
            if (Math.Abs(space_available) > 1)
            {
                var column = dataGrid.Columns[dataGrid.Columns.Count - 1];
                column.Width = new DataGridLength(column.ActualWidth + space_available);
            }
        }

        private static void DistributeEvenly(DataGrid dataGrid)
        {
            double sumOfColumnWidths = SumColumnWidths(dataGrid);
            double space_available = (dataGrid.RenderSize.Width - ScrollbarWidth) - sumOfColumnWidths;

            if (Math.Abs(space_available / dataGrid.Columns.Count) >= 1)
            {
                foreach (DataGridColumn column in dataGrid.Columns)
                {
                    column.Width = new DataGridLength(column.ActualWidth + (space_available / dataGrid.Columns.Count));
                }
            }
        }

        private static double SumColumnWidths(DataGrid dataGrid)
        {
            double sumColumnWidths = 0.0;

            foreach (DataGridColumn column in dataGrid.Columns)
            {
                sumColumnWidths += column.ActualWidth;
            }

            return sumColumnWidths;
        }
    }

I hope this helps somone who was having issues with their RowDetailsTemplate being truncated.

-Austin

No comments:

Post a Comment