WPF FlowDocument表 - AutoFit选项?

时间:2009-09-29 08:34:29

标签: wpf flowdocument

我想要一个表根据内容逻辑地调整列的大小。这在WPF中是否可行?

alt text http://img43.imageshack.us/img43/2640/flowdocument.jpg

以下是我正在使用的代码:

<Window x:Class="FlowDocument.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Style TargetType="{x:Type TableCell}">
            <Setter Property="BorderBrush" Value="Gray" />
            <Setter Property="BorderThickness" Value="3" />


        </Style>
        <Style TargetType="{x:Type Paragraph}">
            <Setter Property="Padding" Value="2, 2, 2, 2" />
        </Style>
    </Window.Resources>
    <Grid>
        <FlowDocumentScrollViewer>
            <FlowDocument>
                <Table>
                    <Table.Columns>
                        <TableColumn Background="LightBlue" />
                        <TableColumn Background="Coral" />
                    </Table.Columns>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>This is a long piece of text</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>This isn't</Paragraph>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>
                                <Paragraph>This is a another long piece of text. The column should be wider than the other one!</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>Ditto</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                </Table>
            </FlowDocument>
        </FlowDocumentScrollViewer>
    </Grid>
</Window>

4 个答案:

答案 0 :(得分:2)

这不是你想要的,但你可以做类似

的事情
<Table.Columns>
    <TableColumn Background="LightBlue" Width="2*"  />
    <TableColumn Background="Coral" Width="*" />
</Table.Columns>

答案 1 :(得分:2)

可以通过确定色谱柱最宽单元格的所需宽度来实现。最宽的单元格可以通过循环遍历确定所需单元格宽度并记住最大值的所有行来确定。

在此示例中,所有列都已优化。值19可能是由于左右单元格填充加上单元格边框厚度。

void autoresizeColumns(Table table)
{
    TableColumnCollection columns = table.Columns;
    TableRowCollection rows = table.RowGroups[0].Rows;
    TableCellCollection cells;
    TableRow row;
    TableCell cell;

    int columnCount = columns.Count;
    int rowCount = rows.Count;
    int cellCount = 0;

    double[] columnWidths = new double[columnCount];
    double columnWidth;

    // loop through all rows
    for (int r = 0; r < rowCount; r++)
    {
        row = rows[r];
        cells = row.Cells;
        cellCount = cells.Count;

        // loop through all cells in the row    
        for (int c = 0; c < columnCount && c < cellCount; c++)
        {
            cell = cells[c];
            columnWidth = getDesiredWidth(new TextRange(cell.ContentStart, cell.ContentEnd)) + 19;

            if (columnWidth > columnWidths[c])
            {
                columnWidths[c] = columnWidth;
            }
        }
    }

    // set the columns width to the widest cell
    for (int c = 0; c < columnCount; c++)
    {
        columns[c].Width = new GridLength(columnWidths[c]);
    }
}


double getDesiredWidth(TextRange textRange)
{
    return new FormattedText(
        textRange.Text,
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        new Typeface(
            textRange.GetPropertyValue(TextElement.FontFamilyProperty) as FontFamily,
            (FontStyle)textRange.GetPropertyValue(TextElement.FontStyleProperty),
            (FontWeight)textRange.GetPropertyValue(TextElement.FontWeightProperty),
            FontStretches.Normal),
            (double)textRange.GetPropertyValue(TextElement.FontSizeProperty),
        Brushes.Black,
        null,
        TextFormattingMode.Display).Width;
}

答案 2 :(得分:1)

实际上,Microsoft建议为此使用网格而不是表格: docs.microsoft.com: Table vs Grid

A,网格不支持开箱即用的简单网格线。微软表示Grid.ShowGridLines仅用于设计目的,并绘制相当难看的虚线。 Micrsoft希望您自己绘制网格线。微软有多懒?

下面是一些示例代码,该如何完成:

simple grid with lines

<FlowDocumentScrollViewer>
  <FlowDocument>
    <BlockUIContainer>
      <Grid HorizontalAlignment="Left" RenderOptions.EdgeMode="Aliased" UseLayoutRounding="True">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.Resources>
          <Style TargetType="Border">
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="Padding" Value="2,0,3,0"/>
          </Style>

        </Grid.Resources>
        <Border Grid.Row="0" Grid.Column="0" BorderThickness="1,1,1,1">
          <TextBlock Text="AAA"/>
        </Border>
        <Border Grid.Row="0" Grid.Column="1" BorderThickness="0,1,1,1">
          <TextBlock Text="BBB"/>
        </Border>
        <Border Grid.Row="1" Grid.Column="0" BorderThickness="1,0,1,1">
          <TextBlock Text="CCC"/>
        </Border>
        <Border Grid.Row="1" Grid.Column="2" BorderThickness="0,0,1,1">
          <TextBlock Text="QgQ"/>
        </Border>
      </Grid>
    </BlockUIContainer>
  </FlowDocument>
</FlowDocumentScrollViewer>

主要思想是在边框内设置每个TextBox,并为每个Border决定哪一侧需要边界线。

要获得精确的1像素线,必须设置Grid.RenderOptions.EdgeMode="Aliased"Grid.UseLayoutRounding="True"

答案 3 :(得分:0)

您可以编写一个简化的“自动调整”功能,如下所示,该功能可以在给定范围内大致压缩列的宽度。

如果您使用Double而不是Integer并尝试精确地计算它们,则可能会花费更长的时间进行浪费的收敛。

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections.Generic;



namespace Example {

    //----------------------------------------------------------------------------------------------------
    public class MyTable : Table {

        ...

        //------------------------------------------------------------------------------------------------
        public MyTable() {

            ...

            AutoFit();

            ...

        }
        //------------------------------------------------------------------------------------------------

        ...

        //------------------------------------------------------------------------------------------------
        private void AutoFit() {

            int extent = 10;
            int[] lengths = new int[this.Columns.Count];

            // collect content lengths of each column of the first 5 rows
            foreach(TableRow row in this.RowGroups[0].Rows.Take(5)) {
                for(int i = 0; i < row.Cells.Count; i++) {
                    TableCell cell = row.Cells[i];
                    TextRange t = new TextRange(cell.ContentStart, cell.ContentEnd);
                    int length = new List<string>(Regex.Split(t.Text, @"\r\n|[\r\n]")).Select(s => s.Length).Max();
                    lengths[i] = Math.Max(lengths[i], length);
                }
            }

            // keep content lengths with column index before sort
            List<ColumnSize> contentSizes = lengths.Select((length, index) => {
                return new ColumnSize() {Index = index, Size = length};
            }).OrderBy(e => e.Size).ToList();

            // assign compacted ratio to columns by recursion
            int[] compactedSizes = Compact(new int[contentSizes.Count], contentSizes, extent);

            // set width to columns
            for(int i = 0; i < compactedSizes.Length; i++) {
                this.Columns[i].Width = new GridLength(compactedSizes[i], GridUnitType.Star);
            }

        }
        //------------------------------------------------------------------------------------------------
        private int[] Compact(int[] sizes, List<ColumnSize> contentSizes, int extent) {

            int min = contentSizes.Min(e => e.Size);
            int max = extent - contentSizes.Count + 1;

            contentSizes = contentSizes.Select(e => {
                int size = (int)Math.Floor((double)e.Size / (double)min);
                e.Size = size > max ? max : size;
                return e;
            }).OrderBy(e => e.Size).ToList();

            if(contentSizes.Sum(e => e.Size) > extent) {
                if(sizes.All(v => v == 0)) {
                    sizes = sizes.Select(v => 1).ToArray();
                } else {
                    contentSizes.TakeWhile(e => e.Size == 1).ToList().ForEach(e => sizes[e.Index] += 1);
                }
                sizes = Compact(sizes, contentSizes.SkipWhile(e => e.Size <= 1).ToList(), extent);
            } else {
                contentSizes.ForEach(e => sizes[e.Index] = e.Size);
            }

            return sizes;

        }
        //------------------------------------------------------------------------------------------------
        private class ColumnSize {

            public int Index { get; set; }
            public int Size { get; set; }

        }
        //------------------------------------------------------------------------------------------------

        ...

    }
    //----------------------------------------------------------------------------------------------------

}