I want a table to logically size the columns according to the contents. Is this possible in WPF?
alt text http://img43.imageshack.us/img43/2640/flowdocument.jpg
You can write a simplified "AutoFit" function like below, that roughly compacts the width of columns within a given extent.
If you use Double, not Integer, and try to precisely calculate them, it may take much longer to converge wastefully.
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(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 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 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; }
}
//------------------------------------------------------------------------------------------------
...
}
//----------------------------------------------------------------------------------------------------
}