diff --git a/src/LiveChartsCore/Drawing/Layouts/CoreStackLayout.cs b/src/LiveChartsCore/Drawing/Layouts/CoreStackLayout.cs
new file mode 100644
index 000000000..dd943153d
--- /dev/null
+++ b/src/LiveChartsCore/Drawing/Layouts/CoreStackLayout.cs
@@ -0,0 +1,211 @@
+// The MIT License(MIT)
+//
+// Copyright(c) 2021 Alberto Rodriguez Orozco & LiveCharts Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using System.Collections.Generic;
+using System;
+
+namespace LiveChartsCore.Drawing.Layouts;
+
+///
+/// Defines a the stack panel geometry.
+///
+/// The type of the drawing context.
+/// The type of the background geometry.
+public abstract class CoreStackLayout
+ : CoreGeometry, IDrawable
+ where TDrawingContext : DrawingContext
+ where TBackgroundGeometry : CoreSizedGeometry, IDrawable, new()
+{
+ private readonly TBackgroundGeometry _backgroundGeometry = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected CoreStackLayout()
+ { }
+
+ ///
+ /// Initializes a new instance of the class,
+ /// and uses the specified geometry as background.
+ ///
+ /// The background gemetry.
+ protected CoreStackLayout(TBackgroundGeometry backgroundGeometry)
+ {
+ _backgroundGeometry = backgroundGeometry;
+ }
+
+ ///
+ /// Gets or sets the panel orientation.
+ ///
+ public ContainerOrientation Orientation { get; set; }
+
+ ///
+ /// Gets or sets the vertical alignment.
+ ///
+ public Align VerticalAlignment { get; set; }
+
+ ///
+ /// Gets or sets the horizontal alignment.
+ ///
+ public Align HorizontalAlignment { get; set; }
+
+ ///
+ /// Gets or sets the padding.
+ ///
+ public Padding Padding { get; set; } = new();
+
+ ///
+ /// Gets or sets the maximum width. When the maximum with is reached, a new row is created.
+ ///
+ public double MaxWidth { get; set; }
+
+ ///
+ /// Gets or sets the maximum height. When the maximum height is reached, a new column is created.
+ ///
+ public double MaxHeight { get; set; }
+
+ ///
+ public IEnumerable> Children { get; set; } = [];
+
+ ///
+ public void Draw(TDrawingContext context)
+ {
+ var size = Measure();
+
+ _backgroundGeometry.X = X;
+ _backgroundGeometry.Y = Y;
+ _backgroundGeometry.Height = size.Height;
+ _backgroundGeometry.Width = size.Width;
+ _backgroundGeometry.Fill = Fill;
+ _backgroundGeometry.Stroke = Stroke;
+
+ _backgroundGeometry.Draw(context);
+ }
+
+ ///
+ public override LvcSize Measure()
+ {
+ var xl = Padding.Left;
+ var yl = Padding.Top;
+ var rowHeight = -1f;
+ var columnWidth = -1f;
+ var mx = 0f;
+ var my = 0f;
+
+ List line = [];
+
+ LvcSize alignCurrentLine()
+ {
+ var mx = -1f;
+ var my = -1f;
+
+ foreach (var child in line)
+ {
+ if (Orientation == ContainerOrientation.Horizontal)
+ child.Drawable.Y = VerticalAlignment switch
+ {
+ Align.Start => yl,
+ Align.Middle => yl + (rowHeight - child.Size.Height) / 2f,
+ Align.End => yl + rowHeight - child.Size.Height,
+ _ => throw new NotImplementedException()
+ };
+ else
+ child.Drawable.X = HorizontalAlignment switch
+ {
+ Align.Start => xl,
+ Align.Middle => xl + (columnWidth - child.Size.Width) / 2f,
+ Align.End => xl + columnWidth - child.Size.Width,
+ _ => throw new NotImplementedException()
+ };
+
+ child.Drawable.Parent = this;
+
+ if (child.Size.Width > mx) mx = child.Size.Width;
+ if (child.Size.Height > my) my = child.Size.Height;
+ }
+
+ line = [];
+ return new LvcSize(mx, my);
+ }
+
+ foreach (var child in Children)
+ {
+ var childSize = child.Measure();
+
+ if (Orientation == ContainerOrientation.Horizontal)
+ {
+ if (xl + childSize.Width > MaxWidth)
+ {
+ var lineSize = alignCurrentLine();
+ xl = Padding.Left;
+ yl += lineSize.Height;
+ rowHeight = -1f;
+ }
+
+ if (rowHeight < childSize.Height) rowHeight = childSize.Height;
+ child.X = xl;
+
+ xl += childSize.Width;
+ }
+ else
+ {
+ if (yl + childSize.Height > MaxHeight)
+ {
+ var lineSize = alignCurrentLine();
+ yl = Padding.Top;
+ xl += lineSize.Width;
+ columnWidth = -1f;
+ }
+
+ if (columnWidth < childSize.Width) columnWidth = childSize.Width;
+ child.Y = yl;
+
+ yl += childSize.Height;
+ }
+
+ if (xl > mx) mx = xl;
+ if (yl > my) my = yl;
+ line.Add(new MeasureResult(child, childSize));
+ }
+
+ if (line.Count > 0)
+ {
+ var lineSize = alignCurrentLine();
+
+ if (Orientation == ContainerOrientation.Horizontal)
+ yl += lineSize.Height;
+ else
+ xl += lineSize.Width;
+
+ if (xl > mx) mx = xl;
+ if (yl > my) my = yl;
+ }
+
+ return new LvcSize(mx + Padding.Right, my + Padding.Bottom);
+ }
+
+ private class MeasureResult(IDrawable visual, LvcSize size)
+ {
+ public IDrawable Drawable { get; set; } = visual;
+ public LvcSize Size { get; set; } = size;
+ }
+}
diff --git a/src/LiveChartsCore/Drawing/Layouts/CoreTableLayout.cs b/src/LiveChartsCore/Drawing/Layouts/CoreTableLayout.cs
new file mode 100644
index 000000000..d6dc4fb11
--- /dev/null
+++ b/src/LiveChartsCore/Drawing/Layouts/CoreTableLayout.cs
@@ -0,0 +1,265 @@
+// The MIT License(MIT)
+//
+// Copyright(c) 2021 Alberto Rodriguez Orozco & LiveCharts Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace LiveChartsCore.Drawing.Layouts;
+
+///
+/// Defines a the stack panel geometry.
+///
+/// The type of the drawing context.
+/// The type of the background geometry.
+public abstract class CoreTableLayout
+ : CoreGeometry, IDrawable
+ where TDrawingContext : DrawingContext
+ where TBackgroundGeometry : CoreSizedGeometry, IDrawable, new()
+{
+ private LvcSize[,] _measuredSizes = new LvcSize[0, 0];
+ private readonly Dictionary> _positions = [];
+ private int _maxRow = 0;
+ private int _maxColumn = 0;
+ private readonly TBackgroundGeometry _backgroundGeometry = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected CoreTableLayout()
+ { }
+
+ ///
+ /// Initializes a new instance of the class,
+ /// and uses the specified geometry as background.
+ ///
+ /// The background gemetry.
+ protected CoreTableLayout(TBackgroundGeometry backgroundGeometry)
+ {
+ _backgroundGeometry = backgroundGeometry;
+ }
+
+ ///
+ /// Gets or sets the panel orientation.
+ ///
+ public ContainerOrientation Orientation { get; set; }
+
+ ///
+ /// Gets or sets the vertical alignment.
+ ///
+ public Align VerticalAlignment { get; set; }
+
+ ///
+ /// Gets or sets the horizontal alignment.
+ ///
+ public Align HorizontalAlignment { get; set; }
+
+ ///
+ /// Gets or sets the padding.
+ ///
+ public Padding Padding { get; set; } = new();
+
+ ///
+ /// Gets or sets the maximum width. When the maximum with is reached, a new row is created.
+ ///
+ public double MaxWidth { get; set; }
+
+ ///
+ /// Gets or sets the maximum height. When the maximum height is reached, a new column is created.
+ ///
+ public double MaxHeight { get; set; }
+
+ ///
+ IEnumerable> IDrawable.Children
+ {
+ get => _positions.Values.SelectMany(row => row.Values.Select(col => col.Drawable));
+ set => throw new Exception(
+ $"Unable to set the Children prroperty of a table, instead please use the " +
+ $"{nameof(AddChild)} method.");
+ }
+
+ ///
+ /// Adds a child to the layout.
+ ///
+ /// The row index.
+ /// The column index.
+ /// The visual to add.
+ /// The cell horizontal alignment, if null the alignment will be defined by the layout.
+ /// The cell vertical alignment, if null the alignment will be defined by the layout.
+ public void AddChild(
+ IDrawable child,
+ int row, int column,
+ Align? horizontalAlign = null,
+ Align? verticalAlign = null)
+ {
+ if (!_positions.TryGetValue(row, out var r))
+ _positions.Add(row, r = []);
+
+ r[column] = new(row, column, child, verticalAlign, horizontalAlign);
+
+ if (row > _maxRow) _maxRow = row;
+ if (column > _maxColumn) _maxColumn = column;
+ }
+
+ ///
+ /// Removes a child from the layout.
+ ///
+ /// The row.
+ /// The column.
+ public void RemoveChildAt(int row, int column)
+ {
+ var r = _positions[row];
+ _ = r.Remove(column);
+ if (r.Count == 0) _ = _positions.Remove(row);
+ }
+
+ ///
+ public void Draw(TDrawingContext context)
+ {
+ var controlSize = Measure();
+
+ float w, h = Padding.Top;
+
+ for (var r = 0; r <= _maxRow; r++)
+ {
+ if (!_positions.TryGetValue(r, out var row)) continue;
+
+ var rowHeight = _measuredSizes[r, _maxColumn + 1].Height;
+ w = Padding.Left;
+
+ for (var c = 0; c <= _maxColumn; c++)
+ {
+ var columnWidth = _measuredSizes[_maxRow + 1, c].Width;
+ if (!row.TryGetValue(c, out var cell))
+ {
+ w += columnWidth;
+ continue;
+ }
+
+ cell.Drawable.X = (cell.HorizontalAlign ?? HorizontalAlignment) switch
+ {
+ Align.Start => w,
+ Align.Middle => w + (columnWidth - _measuredSizes[r, c].Width) * 0.5f,
+ Align.End => w + columnWidth - _measuredSizes[r, c].Width,
+ _ => throw new NotImplementedException(),
+ };
+ cell.Drawable.Y = (cell.VerticalAlign ?? VerticalAlignment) switch
+ {
+ Align.Start => h,
+ Align.Middle => h + (rowHeight - _measuredSizes[r, c].Height) * 0.5f,
+ Align.End => h + rowHeight - _measuredSizes[r, c].Height,
+ _ => throw new NotImplementedException(),
+ };
+
+ cell.Drawable.Parent = this;
+ w += columnWidth;
+ }
+
+ h += rowHeight;
+ }
+
+ _backgroundGeometry.X = X;
+ _backgroundGeometry.Y = Y;
+ _backgroundGeometry.Height = controlSize.Height;
+ _backgroundGeometry.Width = controlSize.Width;
+ _backgroundGeometry.Fill = Fill;
+ _backgroundGeometry.Stroke = Stroke;
+
+ _backgroundGeometry.Draw(context);
+ }
+
+ ///
+ public override LvcSize Measure()
+ {
+ var maxH = Padding.Top;
+ _measuredSizes = new LvcSize[_maxRow + 2, _maxColumn + 2];
+
+ var mr = _maxRow + 1;
+ var mc = _maxColumn + 1;
+
+ for (var r = 0; r <= _maxRow; r++)
+ {
+ if (!_positions.TryGetValue(r, out var row)) continue;
+
+ for (var c = 0; c <= _maxColumn; c++)
+ {
+ var cellSize = _measuredSizes[r, c];
+ var rowSize = _measuredSizes[r, mc];
+ var columnSize = _measuredSizes[mr, c];
+
+ if (!row.TryGetValue(c, out var cell)) continue;
+
+ var childSize = cell.Drawable.Measure();
+
+ if (cellSize.Width < childSize.Width)
+ _measuredSizes[r, c] = new(childSize.Width, _measuredSizes[r, c].Height);
+ if (cellSize.Height < childSize.Height)
+ _measuredSizes[r, c] = new(_measuredSizes[r, c].Width, childSize.Height);
+ if (rowSize.Height < childSize.Height)
+ _measuredSizes[r, mc] = new(0, childSize.Height);
+ if (columnSize.Width < childSize.Width)
+ _measuredSizes[mr, c] = new(childSize.Width, 0);
+ }
+
+ maxH += _measuredSizes[r, _maxColumn + 1].Height;
+ }
+
+ var maxW = Padding.Left;
+ for (var c = 0; c <= _maxColumn; c++)
+ maxW += _measuredSizes[_maxRow + 1, c].Width;
+
+ return new(maxW + Padding.Right, maxH + Padding.Bottom);
+ }
+
+ private class TableCell(
+ int row,
+ int column,
+ IDrawable visualElement,
+ Align? verticalAlign = null,
+ Align? horizontalAlign = null)
+ {
+ ///
+ /// Gets the row.
+ ///
+ public int Row { get; } = row;
+
+ ///
+ /// Gets the column.
+ ///
+ public int Column { get; } = column;
+
+ ///
+ /// Gets or sets the vertical alignment.
+ ///
+ public Align? VerticalAlign { get; } = verticalAlign;
+
+ ///
+ /// Gets or sets the horizontal alignment.
+ ///
+ public Align? HorizontalAlign { get; } = horizontalAlign;
+
+ ///
+ /// Gets the visual element.
+ ///
+ public IDrawable Drawable { get; } = visualElement;
+ }
+}
diff --git a/src/LiveChartsCore/VisualElements/TableLayout.cs b/src/LiveChartsCore/VisualElements/TableLayout.cs
index e650ddb86..d05030930 100644
--- a/src/LiveChartsCore/VisualElements/TableLayout.cs
+++ b/src/LiveChartsCore/VisualElements/TableLayout.cs
@@ -305,7 +305,6 @@ public class TableCell(
Align? verticalAlign = null,
Align? horizontalAlign = null)
{
-
///
/// Gets the row.
///
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Geometries/StackLayout.cs b/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Layouts/StackLayout.cs
similarity index 95%
rename from src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Geometries/StackLayout.cs
rename to src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Layouts/StackLayout.cs
index 2d0968d67..8c2ea1272 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Geometries/StackLayout.cs
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Layouts/StackLayout.cs
@@ -22,8 +22,9 @@
using LiveChartsCore.Drawing;
using LiveChartsCore.Drawing.Layouts;
+using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
-namespace LiveChartsCore.SkiaSharpView.Drawing.Geometries;
+namespace LiveChartsCore.SkiaSharpView.Drawing.Layouts;
///
public class StackLayout : CoreStackLayout
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Geometries/TableLayout.cs b/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Layouts/TableLayout.cs
similarity index 95%
rename from src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Geometries/TableLayout.cs
rename to src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Layouts/TableLayout.cs
index a97de9e3c..8dcca0012 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Geometries/TableLayout.cs
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp/Drawing/Layouts/TableLayout.cs
@@ -22,8 +22,9 @@
using LiveChartsCore.Drawing;
using LiveChartsCore.Drawing.Layouts;
+using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
-namespace LiveChartsCore.SkiaSharpView.Drawing.Geometries;
+namespace LiveChartsCore.SkiaSharpView.Drawing.Layouts;
///
public class TableLayout : CoreTableLayout