diff --git a/Perspex.Controls/Presenters/ScrollContentPresenter.cs b/Perspex.Controls/Presenters/ScrollContentPresenter.cs
index 4e4cb776bb..5b93e5d41c 100644
--- a/Perspex.Controls/Presenters/ScrollContentPresenter.cs
+++ b/Perspex.Controls/Presenters/ScrollContentPresenter.cs
@@ -31,6 +31,7 @@ namespace Perspex.Controls.Presenters
static ScrollContentPresenter()
{
+ ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true);
Control.AffectsArrange(OffsetProperty);
}
diff --git a/Perspex.Controls/Window.cs b/Perspex.Controls/Window.cs
index 1c8b4eb33c..0f6494fc82 100644
--- a/Perspex.Controls/Window.cs
+++ b/Perspex.Controls/Window.cs
@@ -164,7 +164,9 @@ namespace Perspex.Controls
private void HandleRenderNeeded()
{
- this.dispatcher.InvokeAsync(this.RenderVisualTree, DispatcherPriority.Render);
+ this.dispatcher.InvokeAsync(
+ () => this.impl.Invalidate(new Rect(this.ClientSize)),
+ DispatcherPriority.Render);
}
private void HandlePaint(Rect rect, IPlatformHandle handle)
@@ -186,13 +188,5 @@ namespace Perspex.Controls
this.LayoutManager.ExecuteLayoutPass();
this.impl.Invalidate(new Rect(this.ClientSize));
}
-
- private void RenderVisualTree()
- {
- if (!this.LayoutManager.LayoutQueued)
- {
- this.impl.Invalidate(new Rect(this.ClientSize));
- }
- }
}
}
diff --git a/Perspex.SceneGraph/IVisual.cs b/Perspex.SceneGraph/IVisual.cs
index 5688c796bb..1f1aa8775a 100644
--- a/Perspex.SceneGraph/IVisual.cs
+++ b/Perspex.SceneGraph/IVisual.cs
@@ -28,6 +28,11 @@ namespace Perspex
///
Rect Bounds { get; }
+ ///
+ /// Gets a value indicating whether the scene graph node should be clipped to its bounds.
+ ///
+ bool ClipToBounds { get; }
+
///
/// Gets a value indicating whether this scene graph node is visible.
///
diff --git a/Perspex.SceneGraph/Media/ITransform.cs b/Perspex.SceneGraph/Media/ITransform.cs
index 198c0c1392..248063d6ec 100644
--- a/Perspex.SceneGraph/Media/ITransform.cs
+++ b/Perspex.SceneGraph/Media/ITransform.cs
@@ -6,8 +6,12 @@
namespace Perspex.Media
{
+ using System;
+
public interface ITransform
{
+ event EventHandler Changed;
+
Matrix Value { get; }
}
}
diff --git a/Perspex.SceneGraph/Media/MatrixTransform.cs b/Perspex.SceneGraph/Media/MatrixTransform.cs
index 4db9b26ec0..af9373ab4a 100644
--- a/Perspex.SceneGraph/Media/MatrixTransform.cs
+++ b/Perspex.SceneGraph/Media/MatrixTransform.cs
@@ -6,6 +6,8 @@
namespace Perspex.Media
{
+ using System;
+
public class MatrixTransform : Transform
{
public static readonly PerspexProperty MatrixProperty =
@@ -13,9 +15,11 @@ namespace Perspex.Media
public MatrixTransform()
{
+ this.GetObservable(MatrixProperty).Subscribe(_ => this.RaiseChanged());
}
public MatrixTransform(Matrix matrix)
+ : this()
{
this.Matrix = matrix;
}
diff --git a/Perspex.SceneGraph/Media/RotateTransform.cs b/Perspex.SceneGraph/Media/RotateTransform.cs
index 67fe38b7b8..8759df0bdb 100644
--- a/Perspex.SceneGraph/Media/RotateTransform.cs
+++ b/Perspex.SceneGraph/Media/RotateTransform.cs
@@ -6,6 +6,8 @@
namespace Perspex.Media
{
+ using System;
+
public class RotateTransform : Transform
{
public static readonly PerspexProperty AngleProperty =
@@ -13,9 +15,11 @@ namespace Perspex.Media
public RotateTransform()
{
+ this.GetObservable(AngleProperty).Subscribe(_ => this.RaiseChanged());
}
public RotateTransform(double angle)
+ : this()
{
this.Angle = angle;
}
diff --git a/Perspex.SceneGraph/Media/Transform.cs b/Perspex.SceneGraph/Media/Transform.cs
index 437e018b65..0f8ba46c5c 100644
--- a/Perspex.SceneGraph/Media/Transform.cs
+++ b/Perspex.SceneGraph/Media/Transform.cs
@@ -6,8 +6,20 @@
namespace Perspex.Media
{
+ using System;
+
public abstract class Transform : PerspexObject, ITransform
{
+ public event EventHandler Changed;
+
public abstract Matrix Value { get; }
+
+ protected void RaiseChanged()
+ {
+ if (this.Changed != null)
+ {
+ this.Changed(this, EventArgs.Empty);
+ }
+ }
}
}
diff --git a/Perspex.SceneGraph/Visual.cs b/Perspex.SceneGraph/Visual.cs
index 2e5238dcf4..d4a0d67228 100644
--- a/Perspex.SceneGraph/Visual.cs
+++ b/Perspex.SceneGraph/Visual.cs
@@ -10,12 +10,16 @@ namespace Perspex
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
+ using System.Reactive.Linq;
using Perspex.Media;
using Perspex.Rendering;
using Splat;
public class Visual : PerspexObject, IVisual
{
+ public static readonly PerspexProperty ClipToBoundsProperty =
+ PerspexProperty.Register("ClipToBounds");
+
public static readonly PerspexProperty IsVisibleProperty =
PerspexProperty.Register("IsVisible", true);
@@ -37,6 +41,7 @@ namespace Perspex
static Visual()
{
AffectsRender(IsVisibleProperty);
+ RenderTransformProperty.Changed.Subscribe(RenderTransformChanged);
}
public Visual()
@@ -45,6 +50,12 @@ namespace Perspex
this.visualChildren.CollectionChanged += this.VisualChildrenChanged;
}
+ public bool ClipToBounds
+ {
+ get { return this.GetValue(ClipToBoundsProperty); }
+ set { this.SetValue(ClipToBoundsProperty, value); }
+ }
+
public bool IsVisible
{
get { return this.GetValue(IsVisibleProperty); }
@@ -197,6 +208,34 @@ namespace Perspex
return result;
}
+ private static void RenderTransformChanged(PerspexPropertyChangedEventArgs e)
+ {
+ var sender = e.Sender as Visual;
+
+ if (sender != null)
+ {
+ var oldValue = e.OldValue as ITransform;
+ var newValue = e.NewValue as ITransform;
+
+ if (oldValue != null)
+ {
+ oldValue.Changed -= sender.RenderTransformChanged;
+ }
+
+ if (newValue != null)
+ {
+ newValue.Changed += sender.RenderTransformChanged;
+ }
+
+ sender.InvalidateVisual();
+ }
+ }
+
+ private void RenderTransformChanged(object sender, EventArgs e)
+ {
+ this.InvalidateVisual();
+ }
+
private void SetVisualParent(Visual value)
{
if (this.visualParent != value)
diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs
index a5f402aa37..a70d2ab273 100644
--- a/TestApplication/Program.cs
+++ b/TestApplication/Program.cs
@@ -1,7 +1,9 @@
-using System.Reactive.Linq;
+using System;
+using System.Reactive.Linq;
using Perspex;
using Perspex.Controls;
using Perspex.Controls.Primitives;
+using Perspex.Controls.Shapes;
using Perspex.Diagnostics;
using Perspex.Layout;
using Perspex.Media;
@@ -132,6 +134,7 @@ namespace TestApplication
ListsTab(),
SlidersTab(),
LayoutTab(),
+ AnimationsTab(),
}
},
new TextBlock
@@ -430,5 +433,43 @@ namespace TestApplication
}
};
}
+
+ private static TabItem AnimationsTab()
+ {
+ Rectangle rect1;
+
+ var result = new TabItem
+ {
+ Header = "Animations",
+ Content = new Grid
+ {
+ ColumnDefinitions = new ColumnDefinitions
+ {
+ new ColumnDefinition(1, GridUnitType.Star),
+ },
+ Children = new Controls
+ {
+ (rect1 = new Rectangle
+ {
+ Width = 100,
+ Height = 100,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ VerticalAlignment = VerticalAlignment.Center,
+ Fill = Brushes.Crimson,
+ RenderTransform = new RotateTransform(),
+ }),
+ },
+ },
+ };
+
+ Observable.Interval(TimeSpan.FromMilliseconds(10))
+ .Subscribe(x =>
+ {
+ ((RotateTransform)rect1.RenderTransform).Angle = x;
+ });
+
+ return result;
+ }
+
}
}
diff --git a/Windows/Perspex.Direct2D1/Renderer.cs b/Windows/Perspex.Direct2D1/Renderer.cs
index 41b26588a7..fc6e5bbce8 100644
--- a/Windows/Perspex.Direct2D1/Renderer.cs
+++ b/Windows/Perspex.Direct2D1/Renderer.cs
@@ -133,7 +133,7 @@ namespace Perspex.Direct2D1
transform *= Matrix.Translation(visual.Bounds.Position);
- using (context.PushClip(visual.Bounds))
+ using (visual.ClipToBounds ? context.PushClip(visual.Bounds) : null)
using (context.PushTransform(transform))
{
visual.Render(context);
diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs
index bd191b7e4a..97f12c3b27 100644
--- a/Windows/Perspex.Win32/WindowImpl.cs
+++ b/Windows/Perspex.Win32/WindowImpl.cs
@@ -103,8 +103,6 @@ namespace Perspex.Win32
lpszClassName = this.className,
};
- System.Diagnostics.Debug.WriteLine("Registered class " + this.className);
-
ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx);
if (atom == 0)