diff --git a/Perspex.Windows/Window.cs b/Perspex.Windows/Window.cs index 9af8581151..9d6695b777 100644 --- a/Perspex.Windows/Window.cs +++ b/Perspex.Windows/Window.cs @@ -10,6 +10,7 @@ namespace Perspex.Windows using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; + using System.Reactive.Linq; using System.Runtime.InteropServices; using Perspex.Controls; using Perspex.Input; @@ -32,6 +33,8 @@ namespace Perspex.Windows private IInputManager inputManager; + private bool layoutPending; + static Window() { FontSizeProperty.OverrideDefaultValue(typeof(Window), 18.0); @@ -51,22 +54,28 @@ namespace Perspex.Windows this.LayoutManager.LayoutNeeded.Subscribe(x => { + this.layoutPending = true; Dispatcher.CurrentDispatcher.BeginInvoke( DispatcherPriority.Render, () => { this.LayoutManager.ExecuteLayoutPass(); this.renderer.Render(this); + this.layoutPending = false; }); }); - this.RenderManager.RenderNeeded.Subscribe(x => + this.RenderManager.RenderNeeded + .Subscribe(x => { Dispatcher.CurrentDispatcher.BeginInvoke( DispatcherPriority.Render, () => { - this.renderer.Render(this); + if (!this.layoutPending) + { + this.renderer.Render(this); + } }); }); } diff --git a/Perspex/Controls/Border.cs b/Perspex/Controls/Border.cs index 51ef67613a..bd0be84e28 100644 --- a/Perspex/Controls/Border.cs +++ b/Perspex/Controls/Border.cs @@ -13,12 +13,10 @@ namespace Perspex.Controls public class Border : Decorator { - public Border() + static Border() { - Observable.Merge( - this.GetObservable(BackgroundProperty), - this.GetObservable(BorderBrushProperty)) - .Subscribe(_ => this.InvalidateVisual()); + AffectsRender(BackgroundProperty); + AffectsRender(BorderBrushProperty); } public override void Render(IDrawingContext context) diff --git a/Perspex/Controls/Control.cs b/Perspex/Controls/Control.cs index bbe8004298..ecd901e1b4 100644 --- a/Perspex/Controls/Control.cs +++ b/Perspex/Controls/Control.cs @@ -119,6 +119,11 @@ namespace Perspex.Controls private Styles styles; + static Control() + { + AffectsMeasure(IsVisibleProperty); + } + public Control() { this.classes = new Classes(); @@ -128,8 +133,6 @@ namespace Perspex.Controls this.PointerLeave += (s, e) => this.IsPointerOver = false; this.AddPseudoClass(IsPointerOverProperty, ":pointerover"); this.AddPseudoClass(IsFocusedProperty, ":focus"); - - this.GetObservable(IsVisibleProperty).Subscribe(_ => this.InvalidateMeasure()); } public event EventHandler GotFocus @@ -368,6 +371,36 @@ namespace Perspex.Controls get { return Enumerable.Empty(); } } + protected static void AffectsArrange(PerspexProperty property) + { + property.Changed.Subscribe(AffectsArrangeInvalidate); + } + + protected static void AffectsMeasure(PerspexProperty property) + { + property.Changed.Subscribe(AffectsMeasureInvalidate); + } + + private static void AffectsArrangeInvalidate(PerspexPropertyChangedEventArgs e) + { + Control control = e.Sender as Control; + + if (control != null) + { + control.InvalidateArrange(); + } + } + + private static void AffectsMeasureInvalidate(PerspexPropertyChangedEventArgs e) + { + Control control = e.Sender as Control; + + if (control != null) + { + control.InvalidateMeasure(); + } + } + public ILayoutRoot GetLayoutRoot() { return this.GetVisualAncestorOrSelf(); diff --git a/Perspex/Controls/TextBlock.cs b/Perspex/Controls/TextBlock.cs index 8d0b7f7065..e689866472 100644 --- a/Perspex/Controls/TextBlock.cs +++ b/Perspex/Controls/TextBlock.cs @@ -16,9 +16,9 @@ namespace Perspex.Controls public static readonly PerspexProperty TextProperty = PerspexProperty.Register("Text"); - public TextBlock() + static TextBlock() { - this.GetObservable(TextProperty).Subscribe(_ => this.InvalidateVisual()); + AffectsRender(TextProperty); } public string Text diff --git a/Perspex/PerspexObject.cs b/Perspex/PerspexObject.cs index f12b02dcf9..061c2a350b 100644 --- a/Perspex/PerspexObject.cs +++ b/Perspex/PerspexObject.cs @@ -548,9 +548,9 @@ namespace Perspex if (this.PropertyChanged != null) { - this.PropertyChanged( - this, - new PerspexPropertyChangedEventArgs(property, oldValue, newValue)); + PerspexPropertyChangedEventArgs e = new PerspexPropertyChangedEventArgs(this, property, oldValue, newValue); + property.NotifyChanged(e); + this.PropertyChanged(this, e); } } } diff --git a/Perspex/PerspexProperty.cs b/Perspex/PerspexProperty.cs index f02925f724..152b2cab01 100644 --- a/Perspex/PerspexProperty.cs +++ b/Perspex/PerspexProperty.cs @@ -9,6 +9,7 @@ namespace Perspex using System; using System.Collections.Generic; using System.Linq; + using System.Reactive.Subjects; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -31,6 +32,11 @@ namespace Perspex /// private Dictionary defaultValues = new Dictionary(); + /// + /// Observable fired when this property changes on any . + /// + private Subject changed = new Subject(); + /// /// Initializes a new instance of the class. /// @@ -77,6 +83,15 @@ namespace Perspex /// public bool Inherits { get; private set; } + /// + /// Gets an observable that is fired when this property changes on any + /// instance. + /// + public IObservable Changed + { + get { return this.changed; } + } + /// /// Registers a . /// @@ -219,6 +234,11 @@ namespace Perspex return this.Name; } + internal void NotifyChanged(PerspexPropertyChangedEventArgs e) + { + this.changed.OnNext(e); + } + public class BindingAccessor { public BindingAccessor(PerspexProperty property, BindingPriority priority) diff --git a/Perspex/PerspexPropertyChangedEventArgs.cs b/Perspex/PerspexPropertyChangedEventArgs.cs index 86c1f18eb6..5a48bea354 100644 --- a/Perspex/PerspexPropertyChangedEventArgs.cs +++ b/Perspex/PerspexPropertyChangedEventArgs.cs @@ -18,15 +18,23 @@ namespace Perspex public class PerspexPropertyChangedEventArgs { public PerspexPropertyChangedEventArgs( + PerspexObject sender, PerspexProperty property, object oldValue, object newValue) { + this.Sender = sender; this.Property = property; this.OldValue = oldValue; this.NewValue = newValue; } + /// + /// Gets the that the property changed on. + /// + /// + public PerspexObject Sender { get; private set; } + /// /// Gets the property that changed. /// diff --git a/Perspex/Visual.cs b/Perspex/Visual.cs index fd0b1ba7f3..5fa310fac2 100644 --- a/Perspex/Visual.cs +++ b/Perspex/Visual.cs @@ -23,9 +23,9 @@ namespace Perspex private Rect bounds; - public Visual() + static Visual() { - this.GetObservable(IsVisibleProperty).Subscribe(_ => this.InvalidateVisual()); + AffectsRender(IsVisibleProperty); } public bool IsVisible @@ -74,6 +74,21 @@ namespace Perspex } } + protected static void AffectsRender(PerspexProperty property) + { + property.Changed.Subscribe(AffectsRenderInvalidate); + } + + private static void AffectsRenderInvalidate(PerspexPropertyChangedEventArgs e) + { + Visual visual = e.Sender as Visual; + + if (visual != null) + { + visual.InvalidateVisual(); + } + } + public void InvalidateVisual() { IRendered root = this.GetVisualAncestorOrSelf();