DataGrid Row Auto-Resize only grows row height but won't shrink

Jan Holan       12. 8. 2012       Silverlight, Komponenty       6263 zobrazení

Pokud dynamicky měníte velikost buňky v Silverlight DataGrid kontrolu, narazíte na problém, že výška řádku data gridu se podle potřeby správně zvětšuje, ale už se zpátky nezmenšuje.

Vyvolání chyby

Pro vyvolání tohoto stavu použijeme následující příklad. DataGrid obsahuje jeden sloupec s wrap textem, který dynamicky podle šířky sloupce mění počet řádků ve kterých je text zobrazen, a jeden sloupec s tlačítkem pro změnu velikosti buňky.

<sdk:DataGrid x:Name="grdTest" Margin="30" AutoGenerateColumns="False" CanUserReorderColumns="False" CanUserResizeColumns="True" CanUserSortColumns="False" >
    <sdk:DataGrid.Columns>
        <sdk:DataGridTemplateColumn Header="Text">
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="TextBlock text" Margin="4" VerticalAlignment="Center" HorizontalAlignment="Left"/>
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        <sdk:DataGridTemplateColumn Header="Wrap text" Width="150">
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="Wrap TextBlock text" Margin="4" VerticalAlignment="Center" HorizontalAlignment="Left" TextWrapping="Wrap"/>
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        <sdk:DataGridTemplateColumn Header="Resize button">
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid VerticalAlignment="Center">
                        <StackPanel Margin="4" Orientation="Horizontal" VerticalAlignment="Center" Height="20">
                            <ToggleButton Margin="20,0,20,0" Padding="10,0,10,0" Content="Resize" Click="ResizeButton_Click" />
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
    </sdk:DataGrid.Columns>
</sdk:DataGrid>
private void ResizeButton_Click(object sender, RoutedEventArgs e)
{
    var button = (ToggleButton)sender;
    var panel = (StackPanel)VisualTreeHelper.GetParent(button);
    panel.Height = button.IsChecked.Value ? 50 : 20;
}

Výsledek po dvojím stisknutí tlačítka Resize tj. 3 řádek je stejně roztažený jako řádek 2:

TestDataGrid

Příčina

Ve třídě DataGridCellsPresenter je v metodě MeasureOverride vypočítávaná velikost buněk DataGridu, používá se zde pomocná vlastnost DesiredHeight. Problém spočívá v tom, že tato vlastnost DesiredHeight není pro nás případ, kdy má DataGrid RowHeight hodnotu Auto (if (double.IsNaN(this.OwningGrid.RowHeight))), nulována. Problém je také popsán zde (ale pro Silverlight Toolkit DataGrid), nicméně uvedená řešení jsou buď nefunkční nebo nevyhovující pro obecný případ.

Řešení

Řešení spočívá v úpravě DataGrid controlu jeho poděděním. V něm budeme řešit následující:

  • Odchycení události SizeChanged na elementech (CellContent) jednotlivých buněk (odchytávat SizeChanged celého řádku nám nestačí).
  • Vyvolání přepočtu velikosti řádku DataGridu pomoci metod InvalidateMeasure a Measure při nastavení dataGrid.RowHeight = 0 (způsobí v DataGridCellsPresenter shození vlastnosti DesiredHeight).

Kód třídy FixedDataGrid vypadá následovně:

/// <summary>
/// Fixed DataGrid control
/// </summary>
/// <remarks>
/// Fixing issue: DataGrid Row Auto-Resize only grows row height but won't shrink
/// </remarks>
public class FixedDataGrid : DataGrid
{
    /// <summary>
    /// Overrides OnLoadingRow
    /// </summary>
    protected override void OnLoadingRow(DataGridRowEventArgs e)
    {
        if (double.IsNaN(this.RowHeight))
        {
            e.Row.Loaded += Row_Loaded;
        }

        base.OnLoadingRow(e);
    }

    private void Row_Loaded(object sender, RoutedEventArgs e)
    {
        var row = (DataGridRow)sender;
        row.Loaded -= Row_Loaded;

        for (int col = 0; col < this.Columns.Count; col++)
        {
            var cellElement = this.Columns[col].GetCellContent(row);
            if (cellElement != null)
            {
                cellElement.SizeChanged += CellElement_SizeChanged;
            }
        }            
    }

    private static void CellElement_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        //Fix issue: DataGrid Row Auto-Resize only grows row height but won't shrink
        var dataGrid = GetParentOf<DataGrid>((FrameworkElement)sender);
        if (dataGrid != null && double.IsNaN(dataGrid.RowHeight))
        {
            var row = DataGridRow.GetRowContainingElement((FrameworkElement)sender);

            //Fore recalculating row height
            try
            {
                dataGrid.RowHeight = 0;
                row.InvalidateMeasure();
                row.Measure(row.RenderSize);
            }
            finally
            {
                //Restore RowHeight
                dataGrid.RowHeight = double.NaN;
            }
        }
    }

    private static T GetParentOf<T>(FrameworkElement element) where T : FrameworkElement
    {
        while (element != null)
        {
            T item = element as T;
            if (item != null)
            {
                return item;
            }

            element = (FrameworkElement)VisualTreeHelper.GetParent(element);
        }

        return null;
    }
}

Třída je ke stažení zde.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback