Primary WinUI APIs:
MeasureOverride(Size), ArrangeOverride(Size)UIElement.InvalidateMeasure(), UIElement.InvalidateArrange()FrameworkElement.UpdateLayout()Grid, StackPanel, RelativePanel, ScrollViewer)Primary Avalonia APIs:
MeasureOverride(Size), ArrangeOverride(Size)Layoutable.InvalidateMeasure(), Layoutable.InvalidateArrange()Layoutable.UpdateLayout()Grid, StackPanel, RelativePanel, ScrollViewerWinUI layout runs as queued measure/arrange passes. Property changes invalidate measure or arrange and the framework resolves layout in a pass before rendering.
Avalonia uses a similar invalidation-based pass model through LayoutManager. The key migration rule is to keep layout-affecting state separated from rendering-only state.
WinUI XAML:
<local:TilePanel>
<Button Content="One" />
<Button Content="Two" />
<Button Content="Three" />
</local:TilePanel>
WinUI C#:
public sealed class TilePanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in Children)
{
child.Measure(new Size(200, 120));
}
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var x = 0.0;
foreach (var child in Children)
{
child.Arrange(new Rect(x, 0, 200, 120));
x += 208;
}
return finalSize;
}
}
Avalonia XAML:
<local:TilePanel xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.Controls">
<Button Content="One" />
<Button Content="Two" />
<Button Content="Three" />
</local:TilePanel>
Avalonia C#:
public sealed class TilePanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in Children)
{
child.Measure(new Size(200, 120));
}
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var x = 0.0;
foreach (var child in Children)
{
child.Arrange(new Rect(x, 0, 200, 120));
x += 208;
}
return finalSize;
}
}
UpdateLayout() in normal command paths; prefer invalidation.LayoutUpdated handlers side-effect free.