diff --git a/Perspex.Base/Collections/PerspexReadOnlyListView.cs b/Perspex.Base/Collections/PerspexReadOnlyListView.cs index 0f9d1cd0c4..b12e52efbb 100644 --- a/Perspex.Base/Collections/PerspexReadOnlyListView.cs +++ b/Perspex.Base/Collections/PerspexReadOnlyListView.cs @@ -122,7 +122,7 @@ namespace Perspex.Collections ev = new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, e.OldItems, - e.NewStartingIndex); + e.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: ev = new NotifyCollectionChangedEventArgs( @@ -248,7 +248,7 @@ namespace Perspex.Collections return this.GetEnumerator(); } - private IList ConvertList(IEnumerable list) + private IList ConvertList(IEnumerable list) { return list.Cast().Select(this.convert).ToList(); } @@ -271,7 +271,7 @@ namespace Perspex.Collections ev = new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, this.ConvertList(e.OldItems), - e.NewStartingIndex); + e.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: ev = new NotifyCollectionChangedEventArgs( diff --git a/Perspex.Controls/TopLevel.cs b/Perspex.Controls/TopLevel.cs index 32d0021ca8..8612155b71 100644 --- a/Perspex.Controls/TopLevel.cs +++ b/Perspex.Controls/TopLevel.cs @@ -128,6 +128,7 @@ namespace Perspex.Controls this.LayoutManager.Root = this; this.LayoutManager.LayoutNeeded.Subscribe(_ => this.HandleLayoutNeeded()); + this.LayoutManager.LayoutCompleted.Subscribe(_ => this.HandleLayoutCompleted()); this.renderManager.RenderNeeded.Subscribe(_ => this.HandleRenderNeeded()); IStyler styler = Locator.Current.GetService(); @@ -296,6 +297,14 @@ namespace Perspex.Controls this.dispatcher.InvokeAsync(LayoutManager.ExecuteLayoutPass, DispatcherPriority.Render); } + /// + /// Handles a layout completion request from . + /// + private void HandleLayoutCompleted() + { + this.renderManager.InvalidateRender(this); + } + /// /// Handles a render request from . /// diff --git a/Perspex.Layout/ILayoutManager.cs b/Perspex.Layout/ILayoutManager.cs index ac42ff713a..4572926a18 100644 --- a/Perspex.Layout/ILayoutManager.cs +++ b/Perspex.Layout/ILayoutManager.cs @@ -33,6 +33,11 @@ namespace Perspex.Layout /// IObservable LayoutNeeded { get; } + /// + /// Gets an observable that is fired when a layout pass is completed. + /// + IObservable LayoutCompleted { get; } + /// /// Gets a value indicating whether a layout is queued. /// diff --git a/Perspex.Layout/LayoutManager.cs b/Perspex.Layout/LayoutManager.cs index 99674b7c66..2a5975da35 100644 --- a/Perspex.Layout/LayoutManager.cs +++ b/Perspex.Layout/LayoutManager.cs @@ -33,6 +33,11 @@ namespace Perspex.Layout /// private Subject layoutNeeded; + /// + /// Called when a layout is completed. + /// + private Subject layoutCompleted; + /// /// Whether a measure is needed on the next layout pass. /// @@ -54,6 +59,7 @@ namespace Perspex.Layout public LayoutManager() { this.layoutNeeded = new Subject(); + this.layoutCompleted = new Subject(); } /// @@ -71,10 +77,12 @@ namespace Perspex.Layout /// /// Gets an observable that is fired when a layout pass is needed. /// - public IObservable LayoutNeeded - { - get { return this.layoutNeeded; } - } + public IObservable LayoutNeeded => this.layoutNeeded; + + /// + /// Gets an observable that is fired when a layout pass is completed. + /// + public IObservable LayoutCompleted => this.layoutCompleted; /// /// Gets a value indicating whether a layout is queued. @@ -111,6 +119,8 @@ namespace Perspex.Layout break; } } + + this.layoutCompleted.OnNext(Unit.Default); } /// diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index 8b26fdc430..f20334c20f 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -178,14 +178,14 @@ namespace TestApplication DevTools.Attach(window); - var renderer = ((IRenderRoot)window).Renderer; - var last = renderer.RenderCount; - DispatcherTimer.Run(() => - { - fps.Text = "FPS: " + (renderer.RenderCount - last); - last = renderer.RenderCount; - return true; - }, TimeSpan.FromSeconds(1)); + //var renderer = ((IRenderRoot)window).Renderer; + //var last = renderer.RenderCount; + //DispatcherTimer.Run(() => + //{ + // fps.Text = "FPS: " + (renderer.RenderCount - last); + // last = renderer.RenderCount; + // return true; + //}, TimeSpan.FromSeconds(1)); window.Show(); Application.Current.Run(window); diff --git a/Tests/Perspex.Controls.UnitTests/TopLevelTests.cs b/Tests/Perspex.Controls.UnitTests/TopLevelTests.cs index a360f415a0..f0301f9a31 100644 --- a/Tests/Perspex.Controls.UnitTests/TopLevelTests.cs +++ b/Tests/Perspex.Controls.UnitTests/TopLevelTests.cs @@ -17,6 +17,8 @@ namespace Perspex.Controls.UnitTests using Ploeh.AutoFixture; using Ploeh.AutoFixture.AutoMoq; using Splat; + using System.Reactive; + using System.Reactive.Subjects; using Xunit; public class TopLevelTests @@ -163,6 +165,27 @@ namespace Perspex.Controls.UnitTests } } + [Fact] + public void Render_Should_Be_Scheduled_After_Layout_Pass() + { + using (Locator.CurrentMutable.WithResolver()) + { + this.RegisterServices(); + var completed = new Subject(); + var layoutManagerMock = Mock.Get(Locator.Current.GetService()); + layoutManagerMock.Setup(x => x.LayoutCompleted).Returns(completed); + + var impl = new Mock(); + impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); + + var target = new TestTopLevel(impl.Object); + completed.OnNext(Unit.Default); + + var renderManagerMock = Mock.Get(Locator.Current.GetService()); + renderManagerMock.Verify(x => x.InvalidateRender(target)); + } + } + [Fact] public void Width_And_Height_Should_Be_Set_After_Window_Resize_Notification() {