Browse Source

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.
pull/5/head
Steven Kirk 11 years ago
parent
commit
76ba899dbd
  1. 4
      Perspex.Controls/Generators/ItemContainerGenerator.cs
  2. 2
      Perspex.Controls/Generators/TreeItemContainerGenerator.cs
  3. 51
      Perspex.Controls/Presenters/ContentPresenter.cs
  4. 27
      Perspex.Controls/Presenters/ItemsPresenter.cs
  5. 41
      Perspex.Controls/Primitives/TemplatedControl.cs
  6. 2
      Perspex.Controls/StackPanel.cs
  7. 80
      Perspex.Layout/Layoutable.cs
  8. 15
      Perspex.SceneGraph.UnitTests/TestVisual.cs
  9. 41
      Perspex.SceneGraph.UnitTests/VisualTests.cs
  10. 33
      Perspex.SceneGraph/Visual.cs
  11. 3
      Perspex.UnitTests/PerspexObjectTests.cs
  12. 2
      Perspex.Windows/Window.cs

4
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;
}
}

2
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),

51
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<object> ContentProperty =
ContentControl.ContentProperty.AddOwner<Control>();
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;
}
}
}

27
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<IEnumerable, IEnumerable> value)
{
var generator = this.GetGenerator();

41
Perspex.Controls/Primitives/TemplatedControl.cs

@ -18,6 +18,8 @@ namespace Perspex.Controls.Primitives
public static readonly PerspexProperty<ControlTemplate> TemplateProperty =
PerspexProperty.Register<TemplatedControl, ControlTemplate>("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<T>(string id) where T : Control
{
return this.GetTemplateControls().OfType<T>().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;
}
}
}

2
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;

80
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)

15
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<ParamEventArgs<Visual>> VisualParentChangedCalled;
public event EventHandler<ParamEventArgs<IRenderRoot>> AttachedToVisualTreeCalled;
@ -40,6 +38,11 @@ namespace Perspex.SceneGraph.UnitTests
this.AddVisualChild(v);
}
public void AddChildren(IEnumerable<Visual> 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)

41
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++;

33
Perspex.SceneGraph/Visual.cs

@ -39,6 +39,12 @@ namespace Perspex
AffectsRender(IsVisibleProperty);
}
public Visual()
{
this.visualChildren = new PerspexList<IVisual>();
this.visualChildren.CollectionChanged += VisualChildrenChanged;
}
public bool IsVisible
{
get { return this.GetValue(IsVisibleProperty); }
@ -70,11 +76,7 @@ namespace Perspex
IReadOnlyPerspexList<IVisual> IVisual.VisualChildren
{
get
{
this.EnsureVisualChildrenCreated();
return this.visualChildren;
}
get { return this.visualChildren; }
}
IVisual IVisual.VisualParent
@ -111,7 +113,6 @@ namespace Perspex
{
Contract.Requires<ArgumentNullException>(visual != null);
this.EnsureVisualChildrenCreated();
this.visualChildren.Add(visual);
}
@ -119,14 +120,11 @@ namespace Perspex
{
Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(visual != null);
this.EnsureVisualChildrenCreated();
this.visualChildren.Remove(visual);
}
@ -147,8 +144,6 @@ namespace Perspex
{
Contract.Requires<ArgumentNullException>(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<IVisual>();
this.visualChildren.CollectionChanged += VisualChildrenChanged;
this.CreateVisualChildren();
}
}
private void SetVisualParent(Visual value)
{
if (this.visualParent != value)

3
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);

2
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)

Loading…
Cancel
Save