diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs
new file mode 100644
index 0000000000..304a760216
--- /dev/null
+++ b/src/Avalonia.Controls/GridSplitter.cs
@@ -0,0 +1,209 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Represents the control that redistributes space between columns or rows of a Grid control.
+ ///
+ ///
+ /// Unlike WPF GridSplitter, Avalonia GridSplitter has only one Behavior, GridResizeBehavior.PreviousAndNext.
+ ///
+ public class GridSplitter : Thumb
+ {
+ private List _definitions;
+
+ private Grid _grid;
+
+ private DefinitionBase _nextDefinition;
+
+ private Orientation _orientation;
+
+ private DefinitionBase _prevDefinition;
+
+ private void GetDeltaConstraints(out double min, out double max)
+ {
+ var prevDefinitionLen = GetActualLength(_prevDefinition);
+ var prevDefinitionMin = GetMinLength(_prevDefinition);
+ var prevDefinitionMax = GetMaxLength(_prevDefinition);
+
+ var nextDefinitionLen = GetActualLength(_nextDefinition);
+ var nextDefinitionMin = GetMinLength(_nextDefinition);
+ var nextDefinitionMax = GetMaxLength(_nextDefinition);
+ // Determine the minimum and maximum the columns can be resized
+ min = -Math.Min(prevDefinitionLen - prevDefinitionMin, nextDefinitionMax - nextDefinitionLen);
+ max = Math.Min(prevDefinitionMax - prevDefinitionLen, nextDefinitionLen - nextDefinitionMin);
+ }
+
+ protected override void OnDragDelta(VectorEventArgs e)
+ {
+ // WPF doesn't change anything when spliter is in the last row/column
+ // but resizes the splitter row/column when it's the first one.
+ // this is different, but more internally consistent.
+ if (_prevDefinition == null || _nextDefinition == null)
+ return;
+
+ var delta = _orientation == Orientation.Vertical ? e.Vector.X : e.Vector.Y;
+ double max;
+ double min;
+ GetDeltaConstraints(out min, out max);
+ delta = Math.Min(Math.Max(delta, min), max);
+
+ var prevIsStar = IsStar(_prevDefinition);
+ var nextIsStar = IsStar(_nextDefinition);
+
+ if (prevIsStar && nextIsStar)
+ {
+ foreach (var definition in _definitions)
+ {
+ if (definition == _prevDefinition)
+ {
+ SetLengthInStars(_prevDefinition, GetActualLength(_prevDefinition) + delta);
+ }
+ else if (definition == _nextDefinition)
+ {
+ SetLengthInStars(_nextDefinition, GetActualLength(_nextDefinition) - delta);
+ }
+ else if (IsStar(definition))
+ {
+ SetLengthInStars(definition, GetActualLength(definition)); // same size but in stars.
+ }
+ }
+ }
+ else if (prevIsStar)
+ {
+ SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta);
+ }
+ else if (nextIsStar)
+ {
+ SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta);
+ }
+ else
+ {
+ SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta);
+ SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta);
+ }
+ }
+
+ private double GetActualLength(DefinitionBase definition)
+ {
+ if (definition == null)
+ return 0;
+ var columnDefinition = definition as ColumnDefinition;
+ return columnDefinition?.ActualWidth ?? ((RowDefinition)definition).ActualHeight;
+ }
+
+ private double GetMinLength(DefinitionBase definition)
+ {
+ if (definition == null)
+ return 0;
+ var columnDefinition = definition as ColumnDefinition;
+ return columnDefinition?.MinWidth ?? ((RowDefinition)definition).MinHeight;
+ }
+
+ private double GetMaxLength(DefinitionBase definition)
+ {
+ if (definition == null)
+ return 0;
+ var columnDefinition = definition as ColumnDefinition;
+ return columnDefinition?.MaxWidth ?? ((RowDefinition)definition).MaxHeight;
+ }
+
+ private bool IsStar(DefinitionBase definition)
+ {
+ var columnDefinition = definition as ColumnDefinition;
+ return columnDefinition?.Width.IsStar ?? ((RowDefinition)definition).Height.IsStar;
+ }
+
+ private void SetLengthInStars(DefinitionBase definition, double value)
+ {
+ var columnDefinition = definition as ColumnDefinition;
+ if (columnDefinition != null)
+ {
+ columnDefinition.Width = new GridLength(value, GridUnitType.Star);
+ }
+ else
+ {
+ ((RowDefinition)definition).Height = new GridLength(value, GridUnitType.Star);
+ }
+ }
+
+ private void SetLength(DefinitionBase definition, double value)
+ {
+ var columnDefinition = definition as ColumnDefinition;
+ if (columnDefinition != null)
+ {
+ columnDefinition.Width = new GridLength(value);
+ }
+ else
+ {
+ ((RowDefinition)definition).Height = new GridLength(value);
+ }
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ _grid = this.GetVisualParent();
+
+ _orientation = DetectOrientation();
+
+ int definitionIndex; //row or col
+ if (_orientation == Orientation.Vertical)
+ {
+ Cursor = new Cursor(StandardCursorType.SizeWestEast);
+ _definitions = _grid.ColumnDefinitions.Cast().ToList();
+ definitionIndex = GetValue(Grid.ColumnProperty);
+ PseudoClasses.Add(":vertical");
+ }
+ else
+ {
+ Cursor = new Cursor(StandardCursorType.SizeNorthSouth);
+ definitionIndex = GetValue(Grid.RowProperty);
+ _definitions = _grid.RowDefinitions.Cast().ToList();
+ PseudoClasses.Add(":horizontal");
+ }
+
+ if (definitionIndex > 0)
+ _prevDefinition = _definitions[definitionIndex - 1];
+
+ if (definitionIndex < _definitions.Count - 1)
+ _nextDefinition = _definitions[definitionIndex + 1];
+ }
+
+ private Orientation DetectOrientation()
+ {
+ if (!_grid.ColumnDefinitions.Any())
+ return Orientation.Horizontal;
+ if (!_grid.RowDefinitions.Any())
+ return Orientation.Vertical;
+
+ var col = GetValue(Grid.ColumnProperty);
+ var row = GetValue(Grid.RowProperty);
+ var width = _grid.ColumnDefinitions[col].Width;
+ var height = _grid.RowDefinitions[row].Height;
+ if (width.IsAuto && !height.IsAuto)
+ {
+ return Orientation.Vertical;
+ }
+ if (!width.IsAuto && height.IsAuto)
+ {
+ return Orientation.Horizontal;
+ }
+ if (_grid.Children.OfType() // Decision based on other controls in the same column
+ .Where(c => Grid.GetColumn(c) == col)
+ .Any(c => c.GetType() != typeof(GridSplitter)))
+ {
+ return Orientation.Horizontal;
+ }
+ return Orientation.Vertical;
+ }
+ }
+}