I have two classes [applicable to this question]. The first, XYAxes2
, extends FrameworkElement
. It overrides MeasureOverride
and ArrangeOverride
in an attempt to place it's sole child, XAxis
(also extends FrameworkElement
), in the desired position.
XYAxes2
(Parent):
Scale XAxis =//...
protected override Size MeasureOverride( Size availableSize ) {
XAxis.Measure( availableSize );
return (Size)availableSize;
}
protected override Size ArrangeOverride( Size finalSize ) {
XAxis.Arrange( new Rect( 50, 50, 100, 200 ) );
return finalSize;
}
Scale
is a custom drawn component.
Scale
(Child):
protected override void OnRender( DrawingContext cx ) {
ValidateTicks();
if (Orientation == Orientation.Horizontal) {
cx.DrawLine( Pen, new Point( -.5, .5 ), new Point( ActualWidth - .5, .5 ) );
foreach (double tick in _ticks.Keys) {
double val = ScaleTransformCallback( tick );
double x = -0.5 + (int)((val - MinValue) * ActualWidth / (MaxValue - MinValue));
cx.DrawLine( Pen, new Point( x, .5 ), new Point( x, TickLength - .5 ) );
FormattedText txt = _ticks[tick];
cx.DrawText( txt, new Point( x - txt.Width / 2, TickLength + TextMargin ) );
}
} else {
double Left = maxTextWidth + 2 * TextMargin;
double Right = this.TickLength + Left;
cx.DrawLine( Pen, new Point( Right - .5, +.5 ), new Point( Right - .5, ActualHeight + .5 ) );
foreach (double tick in _ticks.Keys) {
double val = ScaleTransformCallback( tick );
double y = -0.5 + ActualHeight - (int)((val - MinValue) * ActualHeight / (MaxValue - MinValue));
cx.DrawLine( Pen, new Point( Right - .5, y ), new Point( Left - .5, y ) );
FormattedText txt = _ticks[tick];
cx.DrawText( txt, new Point( Left - txt.Width - TextMargin, y - txt.Height / 2 ) );
}
}
}
When debugging, ActualWidth
in this function is always the width of the parent rather than the width set by ArrangeOverride
, 100. The child is, however, clipped to the region specified by ArrangeOverride
.
Am I do something wrong in one of these functions?
Edit: By inserting XAxis.Measure(100,200)
into ArrangeOverride
, the problem is apparently fixed. Surely, it shouldn't be necessary to call Measure
everytime the arrangement is invalidated (this would mean InvalidateArrange
would need to implicitly call InvalidateMeasure
). Can anyone shed any light on the causes behind this?
Does anyone know where DesiredSize
and ActualWidth/Height
are actually set (obviously they are not set by my code)?
The solution I have used for this is to use multiple passes in the MeasureOverride
function. I keep measuring with different sizes until a suitable overall size is determined.
By reading through the .Net 4.0 source code, it appears that :-
DesiredSize
is set by the UIElement.Measure
function to be the
return value of MeasureOverride
.
ActualWidth
/ActualHeight
(RenderSize
) are set by
FrameworkElement.ArrangeCore
to be whatever value is returned from
ArrangeOverride
There doesn't appear to be any error checking or
rounding applied.
I hope this can help other people.