diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs
index 53c6b8e66d..f559fd0f6e 100644
--- a/src/Perspex.SceneGraph/Visual.cs
+++ b/src/Perspex.SceneGraph/Visual.cs
@@ -307,8 +307,9 @@ namespace Perspex
/// A containing the transform.
public Matrix TransformToVisual(IVisual visual)
{
- var thisOffset = GetOffsetFromRoot(this).Item2;
- var thatOffset = GetOffsetFromRoot(visual).Item2;
+ var common = this.FindCommonVisualAncestor(visual);
+ var thisOffset = GetOffsetFrom(common, this);
+ var thatOffset = GetOffsetFrom(common, visual);
return Matrix.CreateTranslation(-thatOffset) * Matrix.CreateTranslation(thisOffset);
}
@@ -468,6 +469,30 @@ namespace Perspex
}
}
+ ///
+ /// Gets the visual offset from the specified ancestor.
+ ///
+ /// The ancestor visual.
+ /// The visual.
+ /// The visual offset.
+ private static Vector GetOffsetFrom(IVisual ancestor, IVisual visual)
+ {
+ var result = new Vector();
+
+ while (visual != ancestor)
+ {
+ result = new Vector(result.X + visual.Bounds.X, result.Y + visual.Bounds.Y);
+ visual = visual.VisualParent;
+
+ if (visual == null)
+ {
+ throw new ArgumentException("'visual' is not a descendent of 'ancestor'.");
+ }
+ }
+
+ return result;
+ }
+
///
/// Gets the root of the controls visual tree and the distance from the root.
///
diff --git a/src/Perspex.SceneGraph/VisualTree/VisualExtensions.cs b/src/Perspex.SceneGraph/VisualTree/VisualExtensions.cs
index 901bf678f1..d08682c829 100644
--- a/src/Perspex.SceneGraph/VisualTree/VisualExtensions.cs
+++ b/src/Perspex.SceneGraph/VisualTree/VisualExtensions.cs
@@ -8,10 +8,22 @@ using System.Linq;
namespace Perspex.VisualTree
{
///
- /// Provides extension methods for working with visual tree.
+ /// Provides extension methods for working with the visual tree.
///
public static class VisualExtensions
{
+ ///
+ /// Tries to get the first common ancestor of two visuals.
+ ///
+ /// The first visual.
+ /// The second visual.
+ /// The common ancestor, or null if not found.
+ public static IVisual FindCommonVisualAncestor(this IVisual visual, IVisual target)
+ {
+ return visual.GetSelfAndVisualAncestors().Intersect(target.GetSelfAndVisualAncestors())
+ .FirstOrDefault();
+ }
+
///
/// Enumerates the ancestors of an in the visual tree.
///
diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
index 38d6ca5e1c..9da287f4b9 100644
--- a/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
@@ -189,6 +189,28 @@ namespace Perspex.Controls.UnitTests.Presenters
Assert.Equal(new Vector(10, 10), target.Offset);
}
+ [Fact]
+ public void BringDescendentIntoView_Should_Work()
+ {
+ var target = new ScrollContentPresenter
+ {
+ Width = 100,
+ Height = 100,
+ Content = new Border
+ {
+ Width = 200,
+ Height = 200,
+ }
+ };
+
+ target.ApplyTemplate();
+ target.Measure(Size.Infinity);
+ target.Arrange(new Rect(0, 0, 100, 100));
+ target.BringDescendentIntoView(target.Child, new Rect(200, 200, 0, 0));
+
+ Assert.Equal(new Vector(100, 100), target.Offset);
+ }
+
private class TestControl : Control
{
protected override Size MeasureOverride(Size availableSize)