From 76ba899dbdea30486c7a8e6de157b26c88d85d2f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 30 Nov 2014 16:42:15 +0100 Subject: [PATCH] Make templates etc get applied on measure. Instead of when iterating visual children. This because everything is likely to be set up by that point. --- .../Generators/ItemContainerGenerator.cs | 4 - .../Generators/TreeItemContainerGenerator.cs | 2 - .../Presenters/ContentPresenter.cs | 51 +++++++----- Perspex.Controls/Presenters/ItemsPresenter.cs | 27 +++---- .../Primitives/TemplatedControl.cs | 41 ++++++---- Perspex.Controls/StackPanel.cs | 2 +- Perspex.Layout/Layoutable.cs | 80 ++++++++++--------- Perspex.SceneGraph.UnitTests/TestVisual.cs | 15 ++-- Perspex.SceneGraph.UnitTests/VisualTests.cs | 41 ++-------- Perspex.SceneGraph/Visual.cs | 33 ++------ Perspex.UnitTests/PerspexObjectTests.cs | 3 - Perspex.Windows/Window.cs | 2 + 12 files changed, 135 insertions(+), 166 deletions(-) diff --git a/Perspex.Controls/Generators/ItemContainerGenerator.cs b/Perspex.Controls/Generators/ItemContainerGenerator.cs index ff0e31894b..91b0153493 100644 --- a/Perspex.Controls/Generators/ItemContainerGenerator.cs +++ b/Perspex.Controls/Generators/ItemContainerGenerator.cs @@ -86,8 +86,6 @@ namespace Perspex.Controls.Generators container.TemplatedParent = null; this.AddInternal(item, container); result.Add(container); - - System.Diagnostics.Debug.WriteLine("{0} : Generated container for {1} {2}: {3}", this.GetHashCode(), item, item.GetHashCode(), container); } } finally @@ -132,7 +130,6 @@ namespace Perspex.Controls.Generators object item = this.itemsByContainer[container]; this.containersByItem.Remove(item); this.itemsByContainer.Remove(container); - System.Diagnostics.Debug.WriteLine("{0} : Removed container for {1} {2}", this.GetHashCode(), item, item.GetHashCode()); return item; } @@ -141,7 +138,6 @@ namespace Perspex.Controls.Generators Control container = this.containersByItem[item]; this.containersByItem.Remove(item); this.itemsByContainer.Remove(container); - System.Diagnostics.Debug.WriteLine("{0} : Removed container for {1} {2}", this.GetHashCode(), item, item.GetHashCode()); return container; } } diff --git a/Perspex.Controls/Generators/TreeItemContainerGenerator.cs b/Perspex.Controls/Generators/TreeItemContainerGenerator.cs index 721a994d50..17ce092309 100644 --- a/Perspex.Controls/Generators/TreeItemContainerGenerator.cs +++ b/Perspex.Controls/Generators/TreeItemContainerGenerator.cs @@ -39,8 +39,6 @@ namespace Perspex.Controls.Generators { TreeDataTemplate template = this.GetTreeDataTemplate(item); - System.Diagnostics.Debug.WriteLine("{0} created item for {1}", this.GetHashCode(), item); - result = new T { Header = template.Build(item), diff --git a/Perspex.Controls/Presenters/ContentPresenter.cs b/Perspex.Controls/Presenters/ContentPresenter.cs index 4d9b565287..fc41d758a2 100644 --- a/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/Perspex.Controls/Presenters/ContentPresenter.cs @@ -9,6 +9,7 @@ namespace Perspex.Controls.Presenters using System; using System.Linq; using System.Reactive.Linq; + using Perspex.Controls.Primitives; using Perspex.Layout; public class ContentPresenter : Control, IVisual @@ -16,6 +17,8 @@ namespace Perspex.Controls.Presenters public static readonly PerspexProperty ContentProperty = ContentControl.ContentProperty.AddOwner(); + private bool createdChild; + public ContentPresenter() { this.GetObservable(ContentProperty).Skip(1).Subscribe(this.ContentChanged); @@ -27,10 +30,36 @@ namespace Perspex.Controls.Presenters set { this.SetValue(ContentProperty, value); } } - protected override void CreateVisualChildren() + protected override Size MeasureOverride(Size availableSize) + { + if (!this.createdChild) + { + this.CreateChild(); + } + + Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; + + if (child != null) + { + child.Measure(availableSize); + return child.DesiredSize.Value; + } + + return new Size(); + } + + private void ContentChanged(object content) + { + this.createdChild = false; + this.InvalidateMeasure(); + } + + private void CreateChild() { object content = this.Content; + this.ClearVisualChildren(); + if (content != null) { Control result; @@ -59,26 +88,8 @@ namespace Perspex.Controls.Presenters result.TemplatedParent = null; this.AddVisualChild(result); } - } - protected override Size MeasureOverride(Size availableSize) - { - Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; - - if (child != null) - { - child.Measure(availableSize); - return child.DesiredSize.Value; - } - - return new Size(); - } - - private void ContentChanged(object content) - { - this.ClearVisualChildren(); - this.CreateVisualChildren(); - this.InvalidateMeasure(); + this.createdChild = true; } } } diff --git a/Perspex.Controls/Presenters/ItemsPresenter.cs b/Perspex.Controls/Presenters/ItemsPresenter.cs index 4ff0db7ea7..2fd507830d 100644 --- a/Perspex.Controls/Presenters/ItemsPresenter.cs +++ b/Perspex.Controls/Presenters/ItemsPresenter.cs @@ -22,6 +22,8 @@ namespace Perspex.Controls.Presenters private Panel panel; + private bool createdPanel; + public ItemsPresenter() { this.GetObservableWithHistory(ItemsProperty).Skip(1).Subscribe(this.ItemsChanged); @@ -41,21 +43,28 @@ namespace Perspex.Controls.Presenters protected override Size MeasureOverride(Size availableSize) { - Panel panel = this.GetPanel(); + if (!this.createdPanel) + { + this.CreatePanel(); + } + panel.Measure(availableSize); return panel.DesiredSize.Value; } protected override Size ArrangeOverride(Size finalSize) { - this.GetPanel().Arrange(new Rect(finalSize)); + this.panel.Arrange(new Rect(finalSize)); return finalSize; } - protected override void CreateVisualChildren() + private void CreatePanel() { - this.AddVisualChild(this.GetPanel()); + this.ClearVisualChildren(); + this.panel = this.ItemsPanel.Build(); + this.AddVisualChild(this.panel); this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items)); + this.createdPanel = true; } private IItemContainerGenerator GetGenerator() @@ -70,16 +79,6 @@ namespace Perspex.Controls.Presenters return i.ItemContainerGenerator; } - private Panel GetPanel() - { - if (this.panel == null && this.ItemsPanel != null) - { - this.panel = this.ItemsPanel.Build(); - } - - return this.panel; - } - private void ItemsChanged(Tuple value) { var generator = this.GetGenerator(); diff --git a/Perspex.Controls/Primitives/TemplatedControl.cs b/Perspex.Controls/Primitives/TemplatedControl.cs index 08caa61615..c9a046b083 100644 --- a/Perspex.Controls/Primitives/TemplatedControl.cs +++ b/Perspex.Controls/Primitives/TemplatedControl.cs @@ -18,6 +18,8 @@ namespace Perspex.Controls.Primitives public static readonly PerspexProperty TemplateProperty = PerspexProperty.Register("Template"); + private bool templateApplied; + public ControlTemplate Template { get { return this.GetValue(TemplateProperty); } @@ -45,6 +47,11 @@ namespace Perspex.Controls.Primitives protected override Size MeasureOverride(Size availableSize) { + if (!this.templateApplied) + { + this.ApplyTemplate(); + } + Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; if (child != null) @@ -56,21 +63,6 @@ namespace Perspex.Controls.Primitives return new Size(); } - protected override void CreateVisualChildren() - { - if (this.Template != null) - { - this.Log().Debug( - "Creating template for {0} (#{1:x8})", - this.GetType().Name, - this.GetHashCode()); - - var child = this.Template.Build(this); - this.AddVisualChild(child); - this.OnTemplateApplied(); - } - } - protected T FindTemplateChild(string id) where T : Control { return this.GetTemplateControls().OfType().FirstOrDefault(x => x.Id == id); @@ -94,5 +86,24 @@ namespace Perspex.Controls.Primitives protected virtual void OnTemplateApplied() { } + + private void ApplyTemplate() + { + this.ClearVisualChildren(); + + if (this.Template != null) + { + this.Log().Debug( + "Creating template for {0} (#{1:x8})", + this.GetType().Name, + this.GetHashCode()); + + var child = this.Template.Build(this); + this.AddVisualChild(child); + this.OnTemplateApplied(); + } + + this.templateApplied = true; + } } } diff --git a/Perspex.Controls/StackPanel.cs b/Perspex.Controls/StackPanel.cs index 968ac99df8..b53b98944e 100644 --- a/Perspex.Controls/StackPanel.cs +++ b/Perspex.Controls/StackPanel.cs @@ -107,7 +107,7 @@ namespace Perspex.Controls arrangedWidth = 0; } - foreach (Control child in this.Children.Where(x => x.DesiredSize.HasValue)) + foreach (Control child in this.Children) { double childWidth = child.DesiredSize.Value.Width; double childHeight = child.DesiredSize.Value.Height; diff --git a/Perspex.Layout/Layoutable.cs b/Perspex.Layout/Layoutable.cs index 130de93420..3cf9cd9a6c 100644 --- a/Perspex.Layout/Layoutable.cs +++ b/Perspex.Layout/Layoutable.cs @@ -149,6 +149,11 @@ namespace Perspex.Layout throw new InvalidOperationException("Invalid Arrange rectangle."); } + if (!this.DesiredSize.HasValue) + { + throw new InvalidOperationException("Arrange called before Measure."); + } + this.Log().Debug( "Arrange of {0} (#{1:x8}) gave {2} ", this.GetType().Name, @@ -193,48 +198,51 @@ namespace Perspex.Layout protected virtual void ArrangeCore(Rect finalRect) { - double originX = finalRect.X + this.Margin.Left; - double originY = finalRect.Y + this.Margin.Top; - var size = new Size( - Math.Max(0, finalRect.Width - this.Margin.Left - this.Margin.Right), - Math.Max(0, finalRect.Height - this.Margin.Top - this.Margin.Bottom)); - - if (this.HorizontalAlignment != HorizontalAlignment.Stretch) + if (this.IsVisible) { - size = size.WithWidth(Math.Min(size.Width, this.DesiredSize.Value.Width)); - } + double originX = finalRect.X + this.Margin.Left; + double originY = finalRect.Y + this.Margin.Top; + var size = new Size( + Math.Max(0, finalRect.Width - this.Margin.Left - this.Margin.Right), + Math.Max(0, finalRect.Height - this.Margin.Top - this.Margin.Bottom)); - if (this.VerticalAlignment != VerticalAlignment.Stretch) - { - size = size.WithHeight(Math.Min(size.Height, this.DesiredSize.Value.Height)); - } + if (this.HorizontalAlignment != HorizontalAlignment.Stretch) + { + size = size.WithWidth(Math.Min(size.Width, this.DesiredSize.Value.Width)); + } + + if (this.VerticalAlignment != VerticalAlignment.Stretch) + { + size = size.WithHeight(Math.Min(size.Height, this.DesiredSize.Value.Height)); + } - size = LayoutHelper.ApplyLayoutConstraints(this, size); - size = this.ArrangeOverride(size).Constrain(size); + size = LayoutHelper.ApplyLayoutConstraints(this, size); + size = this.ArrangeOverride(size).Constrain(size); - switch (this.HorizontalAlignment) - { - case HorizontalAlignment.Center: - originX += (finalRect.Width - size.Width) / 2; - break; - case HorizontalAlignment.Right: - originX += finalRect.Width - size.Width; - break; - } + switch (this.HorizontalAlignment) + { + case HorizontalAlignment.Center: + originX += (finalRect.Width - size.Width) / 2; + break; + case HorizontalAlignment.Right: + originX += finalRect.Width - size.Width; + break; + } - switch (this.VerticalAlignment) - { - case VerticalAlignment.Center: - originY += (finalRect.Height - size.Height) / 2; - break; - case VerticalAlignment.Bottom: - originY += finalRect.Height - size.Height; - break; - } + switch (this.VerticalAlignment) + { + case VerticalAlignment.Center: + originY += (finalRect.Height - size.Height) / 2; + break; + case VerticalAlignment.Bottom: + originY += finalRect.Height - size.Height; + break; + } - var bounds = new Rect(originX, originY, size.Width, size.Height); - this.SetVisualBounds(bounds); - this.SetValue(ActualSizeProperty, bounds.Size); + var bounds = new Rect(originX, originY, size.Width, size.Height); + this.SetVisualBounds(bounds); + this.SetValue(ActualSizeProperty, bounds.Size); + } } protected virtual Size ArrangeOverride(Size finalSize) diff --git a/Perspex.SceneGraph.UnitTests/TestVisual.cs b/Perspex.SceneGraph.UnitTests/TestVisual.cs index 2b4348ae27..c223b8244c 100644 --- a/Perspex.SceneGraph.UnitTests/TestVisual.cs +++ b/Perspex.SceneGraph.UnitTests/TestVisual.cs @@ -27,8 +27,6 @@ namespace Perspex.SceneGraph.UnitTests get { return base.InheritanceParent; } } - public Visual[] InitialChildren { get; set; } - public event EventHandler> VisualParentChangedCalled; public event EventHandler> AttachedToVisualTreeCalled; @@ -40,6 +38,11 @@ namespace Perspex.SceneGraph.UnitTests this.AddVisualChild(v); } + public void AddChildren(IEnumerable v) + { + this.AddVisualChildren(v); + } + public void RemoveChild(Visual v) { this.RemoveVisualChild(v); @@ -50,14 +53,6 @@ namespace Perspex.SceneGraph.UnitTests this.ClearVisualChildren(); } - protected override void CreateVisualChildren() - { - if (this.InitialChildren != null) - { - this.AddVisualChildren(this.InitialChildren); - } - } - protected override void OnVisualParentChanged(Visual oldParent) { if (this.VisualParentChangedCalled != null) diff --git a/Perspex.SceneGraph.UnitTests/VisualTests.cs b/Perspex.SceneGraph.UnitTests/VisualTests.cs index 3338f858b3..a7e0755a47 100644 --- a/Perspex.SceneGraph.UnitTests/VisualTests.cs +++ b/Perspex.SceneGraph.UnitTests/VisualTests.cs @@ -12,32 +12,6 @@ namespace Perspex.SceneGraph.UnitTests [TestClass] public class VisualTests { - [TestMethod] - public void Initial_Children_Should_Be_Created() - { - var target = new TestVisual - { - InitialChildren = new[] { new Visual(), new Visual() } - }; - - var result = target.GetVisualChildren().ToList(); - - CollectionAssert.AreEqual(target.InitialChildren.ToList(), result); - } - - [TestMethod] - public void Initial_Children_Should_Have_VisualParent_Set() - { - var target = new TestVisual - { - InitialChildren = new[] { new Visual(), new Visual() } - }; - - var result = target.GetVisualChildren().Select(x => x.GetVisualParent()).ToList(); - - CollectionAssert.AreEqual(new[] { target, target }, result); - } - [TestMethod] public void Added_Child_Should_Have_VisualParent_Set() { @@ -87,14 +61,13 @@ namespace Perspex.SceneGraph.UnitTests [TestMethod] public void Clearing_Children_Should_Clear_VisualParent() { - var target = new TestVisual - { - InitialChildren = new[] { new Visual(), new Visual() } - }; + var children = new[] { new Visual(), new Visual() }; + var target = new TestVisual(); + target.AddChildren(children); target.ClearChildren(); - var result = target.InitialChildren.Select(x => x.GetVisualParent()).ToList(); + var result = children.Select(x => x.GetVisualParent()).ToList(); CollectionAssert.AreEqual(new Visual[] { null, null }, result); } @@ -108,11 +81,10 @@ namespace Perspex.SceneGraph.UnitTests int attched = 0; int i = 1; - target.InitialChildren = new[] { child }; child.VisualParentChangedCalled += (s, e) => changed = i++; child.AttachedToVisualTreeCalled += (s, e) => attched = i++; - target.GetVisualChildren().First(); + target.AddChild(child); Assert.AreEqual(1, changed); Assert.AreEqual(2, attched); @@ -127,8 +99,7 @@ namespace Perspex.SceneGraph.UnitTests int detached = 0; int i = 1; - target.InitialChildren = new[] { child }; - target.GetVisualChildren().First(); + target.AddChild(child); child.VisualParentChangedCalled += (s, e) => changed = i++; child.DetachedFromVisualTreeCalled += (s, e) => detached = i++; diff --git a/Perspex.SceneGraph/Visual.cs b/Perspex.SceneGraph/Visual.cs index e03737333c..18d8ec4ff6 100644 --- a/Perspex.SceneGraph/Visual.cs +++ b/Perspex.SceneGraph/Visual.cs @@ -39,6 +39,12 @@ namespace Perspex AffectsRender(IsVisibleProperty); } + public Visual() + { + this.visualChildren = new PerspexList(); + this.visualChildren.CollectionChanged += VisualChildrenChanged; + } + public bool IsVisible { get { return this.GetValue(IsVisibleProperty); } @@ -70,11 +76,7 @@ namespace Perspex IReadOnlyPerspexList IVisual.VisualChildren { - get - { - this.EnsureVisualChildrenCreated(); - return this.visualChildren; - } + get { return this.visualChildren; } } IVisual IVisual.VisualParent @@ -111,7 +113,6 @@ namespace Perspex { Contract.Requires(visual != null); - this.EnsureVisualChildrenCreated(); this.visualChildren.Add(visual); } @@ -119,14 +120,11 @@ namespace Perspex { Contract.Requires(visuals != null); - this.EnsureVisualChildrenCreated(); this.visualChildren.AddRange(visuals); } protected void ClearVisualChildren() { - this.EnsureVisualChildrenCreated(); - // TODO: Just call visualChildren.Clear() when we have a PerspexList that notifies of // the removed items. while (this.visualChildren.Count > 0) @@ -139,7 +137,6 @@ namespace Perspex { Contract.Requires(visual != null); - this.EnsureVisualChildrenCreated(); this.visualChildren.Remove(visual); } @@ -147,8 +144,6 @@ namespace Perspex { Contract.Requires(visuals != null); - this.EnsureVisualChildrenCreated(); - foreach (var v in visuals) { this.visualChildren.Remove(v); @@ -160,10 +155,6 @@ namespace Perspex this.bounds = bounds; } - protected virtual void CreateVisualChildren() - { - } - protected virtual void OnAttachedToVisualTree(IRenderRoot root) { } @@ -186,16 +177,6 @@ namespace Perspex } } - private void EnsureVisualChildrenCreated() - { - if (this.visualChildren == null) - { - this.visualChildren = new PerspexList(); - this.visualChildren.CollectionChanged += VisualChildrenChanged; - this.CreateVisualChildren(); - } - } - private void SetVisualParent(Visual value) { if (this.visualParent != value) diff --git a/Perspex.UnitTests/PerspexObjectTests.cs b/Perspex.UnitTests/PerspexObjectTests.cs index ebd5b14636..216b27a7d8 100644 --- a/Perspex.UnitTests/PerspexObjectTests.cs +++ b/Perspex.UnitTests/PerspexObjectTests.cs @@ -371,9 +371,6 @@ namespace Perspex.UnitTests Class1 source = new Class1(); Class1 target = new Class1(); - System.Diagnostics.Debug.WriteLine("source: " + source.GetHashCode()); - System.Diagnostics.Debug.WriteLine("target: " + target.GetHashCode()); - source.SetValue(Class1.FooProperty, "first"); target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty); diff --git a/Perspex.Windows/Window.cs b/Perspex.Windows/Window.cs index 8ff1ad59e3..a9df9ed39f 100644 --- a/Perspex.Windows/Window.cs +++ b/Perspex.Windows/Window.cs @@ -160,6 +160,8 @@ namespace Perspex.Windows lpszClassName = this.className, }; + System.Diagnostics.Debug.WriteLine("Registered class " + this.className); + ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); if (atom == 0)