web-dev-qa-db-ja.com

コンテンツに合わせてWPF DataGridのサイズを変更する方法は?

狙い

すべてのセル(テキスト)が完全に表示されるように、DataGrid(標準、WPFから)にこのようなサイズを設定したいと思います。 DataGridを含むDockPanelのウィンドウがあるため、ウィンドウのサイズを変更すると、それに応じてすべてのネストされたウィジェット(DockPanelおよびDataGrid)のサイズが変更されます。

例(編集-1)

100ピクセル幅のウィンドウがあり、1列のDataGridがあるとします。このセルは「速い茶色のキツネ...」(400ピクセル幅)です。したがって、DataGridのサイズは400ピクセルに変更する必要があり(おそらく、パディングのため)、Windowのサイズも400ピクセルに変更する必要があります(また、パディングのために)。

私はそれを行うための標準的な方法を見つけませんでした(AFAIK WPFはコンテンツを希望の幅にクリップする方法を提供します、私の問題は正反対です)、私はそのようない回避策を考え出しますが、それはあまりうまくいきません。

回避策

  1. dataGridヘッダーを反復処理し(単なる文字列であると仮定)、テキストに必要な幅を計算します
  2. 各列ごとにDataGrid行を反復し(TextBlockまたはTextBoxであると仮定)、テキストに必要な最大幅を計算します-TextBlock/TextBoxの水平方向の余白とDataGridセルの水平方向の余白を追加します
  3. 列のDataGrid ActualWidthと(2)で計算された最大幅の差をすべて合計する
  4. (3)で計算された差だけウィンドウ幅を広げる

問題

いくつかのテストを行いましたが、場合によっては計算された幅が大きすぎます(これは小さな問題です)。この問題は、コアプロシージャから始まります。TextBox/ TextBlockに必要な幅を計算すると、計算された幅は常に1単位小さくなります(幅を計算された幅に設定すると、テキストから1ピクセルが常にクリップされます)。

ここで無視している要素はどれですか?または多分もっと良い-その内容に合わせてDataGridのサイズを変更する方法は既にありますか?

コード

テキストに必要な幅の計算(ここではTextBlockの場合):

    public static double TextWidth(this TextBlock widget, string text)
    {
        var formattedText = new FormattedText(text, // can use arbitrary text
                                              System.Globalization.CultureInfo.CurrentCulture,
                                              widget.FlowDirection,
                                              widget.FontFamily.GetTypefaces().FirstOrDefault(),
                                              widget.FontSize, 
                                              widget.Foreground);

        return formattedText.Width+widget.Padding.Left+widget.Padding.Right;
    }

DataGridコンテンツに合わせてウィンドウサイズを調整します(ugly_factorは見苦しい回避策です;-)適切に修正する方法がわからなかったため、1.3に設定し、この方法でウィンドウが小さすぎないようにしました):

    public static void AdjustWidthToDataGrid(this Window window, DataGrid dataGrid, double ugly_factor)
    {
        var max_widths = dataGrid.Columns.Select(it => window.TextWidth(it.Header as string) 
                                                                        * ugly_factor).ToArray();

        foreach (var row in Enumerable.Range(0, dataGrid.Items.Count))
            foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
            {
                var cell = dataGrid.GetCell(row, col);
                double width = 0;
                if (cell.Content is TextBlock)
                    width = (cell.Content as TextBlock).TextWidth();
                else if (cell.Content is TextBox)
                    width = (cell.Content as TextBox).TextWidth();

                if (cell.Content is FrameworkElement)
                {
                    var widget = cell.Content as FrameworkElement;
                    width = width + widget.Margin.Left + widget.Margin.Right;
                }

                max_widths[col] = Math.Max(max_widths[col], 
                                           width*ugly_factor+cell.Padding.Left+cell.Padding.Right);
            }

        double width_diff = 0;

        foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
            width_diff += Math.Max(0,max_widths[col] - dataGrid.Columns[col].ActualWidth);

        if (width_diff > 0)
            window.Width = window.ActualWidth+ width_diff;
    }
21
greenoldman

Markoのコメント が答えです:

私はあなたのサイズ変更ロジックに少し困惑しています。まず、ウィンドウのSizeToContent="WidthAndHeight"、ウィンドウはデータグリッドのフルサイズで表示されます。一方、事前に定義されたサイズでウィンドウを表示し、ウィンドウが表示されたらウィンドウのサイズを変更する場合、その時点でデータグリッドの希望するサイズにサイズ変更することは非常に直感的ではありません。サイズ変更は、たとえば100pxから400pxにジャンプしますが、250pxだけにサイズ変更したい場合はどうなるでしょうか...(ウィンドウの隅をドラッグしてサイズ変更を確認します)

2
greenoldman

ヘッダーとセルの両方のコンテンツに応じて幅に合わせてデータグリッドの列にオプションを指定する必要があるという同じ問題から出てきたばかりです。次のコードを使用しました。

_private void FitToContent()
    {
        // where dg is my data grid's name...
        foreach (DataGridColumn column in dg.Columns)
        {
            //if you want to size ur column as per the cell content
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToCells);
            //if you want to size ur column as per the column header
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToHeader);
            //if you want to size ur column as per both header and cell content
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);
        }
    }
_

また、表示(データグリッドの幅)に合わせて列にオプションを提供しました。そのために、上記と同じコードを使用し、次の小さな変更を加えました。

column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Star);

すべての列について... :) Horizo​​ntalScrollVisibilityをAutoのままにしてください。

21

私があなたの質問を正しく理解し、あなたがしたい場合:

  1. 列の幅がコンテンツの幅と同じであるDataGrid。
  2. DataGridはそのコンテンツに適合します。

これはデータバインディングで実現できます:

<DataGrid AutoGenerateColumns="False"
          EnableRowVirtualization="True"
          Height="111"
          HorizontalAlignment="Left"
          ItemsSource="{Binding}"
          Margin="72,203,0,0"
          Name="dataGrid"
          RowDetailsVisibilityMode="VisibleWhenSelected"
          VerticalAlignment="Top"
          Width="{Binding Path=ActualWidth, ElementName=grid}">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Column1"
                            Binding="{Binding Path=Something1}"
                            Header="Column1" 
                            Width="Auto"  />
        <DataGridTextColumn x:Name="Column2"
                            Binding="{Binding Path=Something2}"
                            Header="Column2"
                            Width="*" />
    </DataGrid.Columns>
</DataGrid>

ここで、最初の列は必要な幅で、2番目の列は広げられたスペースです。ただし、DataGridの幅は、その周りにあるGridの幅と同じであるため、目的の結果が達成されます。

8
GC87

SizeToCellsは私のために働いたものです。 XAMLであり、簡潔だからです。

<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}" Header="Name" Width="SizeToCells"/>
1
Pale Ale

私の場合、DataGridはHorizontalAlignment="Stretch" VerticalAlignment="Stretch"デフォルトでは、設定します

<DataGrid ItemsSource="{Binding Source}" Width="Auto" Height="Auto" 
          HorizontalAlignment="Center" VerticalAlignment="Center"/>

その後動作します。

1
yu yang Jian