/// <summary>
/// Measure the children
/// </summary>
/// <param name="availableSize">Size available</param>
/// <returns>Size desired</returns>
protected override Size MeasureOverride(Size availableSize)
{
if (ScrollOwner != null)
{
if (ScrollOwner.ScrollableWidth < HorizontalOffset)
{
SetHorizontalOffset(ScrollOwner.ScrollableWidth);
}
if (ScrollOwner.ScrollableHeight < VerticalOffset)
{
SetVerticalOffset(ScrollOwner.ScrollableHeight);
}
}
// We need to access InternalChildren before the generator to work around a bug
UIElementCollection children = InternalChildren;
IItemContainerGenerator generator = ItemContainerGenerator;
ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);
TreeViewExItem treeViewItem = itemsControl as TreeViewExItem;
TreeViewEx treeView = itemsControl as TreeViewEx ?? treeViewItem.ParentTreeView;
Debug(treeViewItem, "Measuring");
double maxWidth = 0;
double currentYinItemSystem = 0;
if (treeView.IsVirtualizing)
{
// never forget: virtualization of a tree is an approximation. there are some use cases which theoretically work and others
// we try to get it working by estimations. See GetCachedOrEstimatedHeight for more infos.
int itemCount = itemsControl.Items.Count;
int firstVisibleItemIndex = 0;
int lastVisibleItemIndex = itemCount;
double itemTop;
if (treeViewItem != null)
{
itemTop = treeViewItem.itemTopInTreeSystem + GetHeightOfHeader(itemsControl);
}
else
{
// get the area where items have to be visualized. This is from top to bottom of the visible space in tree system.
// We add a little of offset. It seems like it improves estimation of heights.
double predictionOffset = 50;
double top = VerticalOffset - predictionOffset;
if (top < 0)
{
top = 0;
}
treeView.realizationSpace.Top = top;
treeView.realizationSpace.Bottom = VerticalOffset + availableSize.Height + predictionOffset;
itemTop = GetHeightOfHeader(itemsControl);
}
int itemGeneratorIndex = 0;
bool isPreviousItemVisible = false;
IDisposable generatorRun = null;
currentYinItemSystem = 0;
int childHierarchyLevel = 0;
if (treeViewItem != null)
{
childHierarchyLevel = treeViewItem.hierachyLevel + 1;
}
try
{
// iterate child items
for (int i = 0; i < itemCount; i++)
{
double estimatedHeight = GetCachedOrEstimatedHeight(treeView, childHierarchyLevel);
VerticalArea childSpace = new VerticalArea();
childSpace.Top = itemTop + currentYinItemSystem;
childSpace.Bottom = childSpace.Top + estimatedHeight;
// check if item is possibly visible or could become visible if someone changes expanding of siblings
bool isVisibleItem = treeView.realizationSpace.IsWithin(childSpace);
if (isVisibleItem)
{
// we have found a visible item, lets check if its the first visible item.
if (!isPreviousItemVisible)
{
// we found a visible item, lets initialize the visible item section of the loop
isPreviousItemVisible = true;
firstVisibleItemIndex = i;
GeneratorPosition startPos = generator.GeneratorPositionFromIndex(i);
itemGeneratorIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1;
generatorRun = generator.StartAt(startPos, GeneratorDirection.Forward, true);
}
else
{
itemGeneratorIndex++;
}
// Get or create the child
bool newlyRealized;
TreeViewExItem child = generator.GenerateNext(out newlyRealized) as TreeViewExItem;
Debug(treeViewItem, "Found visible child: " + child.DataContext);
if (newlyRealized)
{
// Figure out if we need to insert the child at the end or somewhere in the middle
AddOrInsertItemToInternalChildren(itemGeneratorIndex, child);
child.ParentTreeView = treeView;
generator.PrepareItemContainer(child);
}
else
{
// The child has already been created, let's be sure it's in the right spot
if (child != children[itemGeneratorIndex])
{
throw new InvalidOperationException("Wrong child was generated");
}
}
if (treeViewItem != null)
{
child.itemTopInTreeSystem = currentYinItemSystem + itemTop;
child.hierachyLevel = treeViewItem.hierachyLevel + 1;
}
else
{
child.itemTopInTreeSystem = currentYinItemSystem;
child.hierachyLevel = 1;
}
InvalidateMeasure(child);
child.Measure(new Size(double.MaxValue, double.MaxValue));
// add real height to cache
double heightOfChild = child.DesiredSize.Height;
RegisterHeight(treeView, childHierarchyLevel, heightOfChild);
currentYinItemSystem += child.DesiredSize.Height;
// save the maximum needed width
maxWidth = Math.Max(maxWidth, child.DesiredSize.Width);
}
else
{
//Debug(treeViewItem, "Found invisible child: " + i);
if (isPreviousItemVisible)
{
// set last visible index. this is important, to cleanup not anymore used items
lastVisibleItemIndex = i;
isPreviousItemVisible = false;
}
// dispose generator run. if we do it after the whole loop, we run in multithreading issues
if (generatorRun != null)
{
generatorRun.Dispose();
generatorRun = null;
}
currentYinItemSystem += GetCachedOrEstimatedHeight(treeView, childHierarchyLevel);
}
//Debug(treeViewItem, "Current y for " + i + ": " + currentYinItemSystem);
}
}
finally
{
//just for safety
if (generatorRun != null)
{
generatorRun.Dispose();
generatorRun = null;
}
}
//Debug("Cleaning all items but " + firstVisibleItemIndex + " to " + lastVisibleItemIndex + " for element " + itemsControl.DataContext);
CleanUpItems(firstVisibleItemIndex, lastVisibleItemIndex);
}
else
{
//Debug("Virtualization is OFF.");
GeneratorPosition startPos = generator.GeneratorPositionFromIndex(0);
using (generator.StartAt(startPos, GeneratorDirection.Forward, true))
{
for (int i = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1; i < itemsControl.Items.Count; i++)
{
// Get or create the child
bool newlyRealized;
TreeViewExItem child = generator.GenerateNext(out newlyRealized) as TreeViewExItem;
if (newlyRealized)
{
// Figure out if we need to insert the child at the end or somewhere in the middle
AddOrInsertItemToInternalChildren(i, child);
child.ParentTreeView = treeView ?? treeViewItem.ParentTreeView;
generator.PrepareItemContainer(child);
}
child.Measure(new Size(double.MaxValue, double.MaxValue));
// now get the real height
double height = child.DesiredSize.Height;
// add real height to current position
currentYinItemSystem += height;
// save the maximum needed width
maxWidth = Math.Max(maxWidth, child.DesiredSize.Width);
}
}
}
if (double.IsPositiveInfinity(maxWidth) || double.IsPositiveInfinity(currentYinItemSystem))
{
throw new InvalidOperationException("???");
}
Extent = new Size(maxWidth, currentYinItemSystem);
Viewport = availableSize;
Debug(treeViewItem, "Desired height: " + Extent.Height);
return(Extent);
}