From 435f4df665a53c04e2edea4dc352ccd4216bcb7c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 10 Sep 2014 22:06:10 +0200 Subject: [PATCH] Added start of ItemsControl. --- Perspex/Application.cs | 21 ++++++ Perspex/Controls/ContentPresenter.cs | 35 ++------- Perspex/Controls/Control.cs | 43 +++++++++++ Perspex/Controls/DataTemplate.cs | 6 ++ Perspex/Controls/ItemsControl.cs | 99 ++++++++++++++++++++++++++ Perspex/Controls/ItemsPanelTemplate.cs | 20 ++++++ Perspex/Controls/Panel.cs | 2 +- Perspex/Perspex.csproj | 2 + Perspex/PerspexList.cs | 19 +++-- TestApplication/Program.cs | 35 +++++++-- 10 files changed, 240 insertions(+), 42 deletions(-) create mode 100644 Perspex/Controls/ItemsControl.cs create mode 100644 Perspex/Controls/ItemsPanelTemplate.cs diff --git a/Perspex/Application.cs b/Perspex/Application.cs index 092f5f5ec2..9e7127c46e 100644 --- a/Perspex/Application.cs +++ b/Perspex/Application.cs @@ -6,12 +6,15 @@ namespace Perspex { + using Perspex.Controls; using Perspex.Input; using Perspex.Styling; using Splat; public class Application { + private DataTemplates dataTemplates; + private Styles styles; public Application() @@ -27,6 +30,24 @@ namespace Perspex private set; } + public DataTemplates DataTemplates + { + get + { + if (this.dataTemplates == null) + { + this.dataTemplates = new DataTemplates(); + } + + return this.dataTemplates; + } + + set + { + this.dataTemplates = value; + } + } + public IFocusManager FocusManager { get; diff --git a/Perspex/Controls/ContentPresenter.cs b/Perspex/Controls/ContentPresenter.cs index a87f6363e9..953f3b38f6 100644 --- a/Perspex/Controls/ContentPresenter.cs +++ b/Perspex/Controls/ContentPresenter.cs @@ -46,6 +46,11 @@ namespace Perspex.Controls set { this.SetValue(ContentProperty, value); } } + IEnumerable IVisual.ExistingVisualChildren + { + get { return Enumerable.Repeat(this.visualChild, this.visualChild != null ? 1 : 0); } + } + IEnumerable IVisual.VisualChildren { get @@ -154,35 +159,5 @@ namespace Perspex.Controls return new Size(); } - - private DataTemplate FindDataTemplate(object content) - { - ILogical node = this; - - while (node != null) - { - Control control = node as Control; - - if (control != null) - { - foreach (DataTemplate dt in control.DataTemplates.Reverse()) - { - if (dt.Match(content)) - { - return dt; - } - } - } - - node = node.LogicalParent; - - if (node == null && control != null) - { - node = control.TemplatedParent as ILogical; - } - } - - return null; - } } } diff --git a/Perspex/Controls/Control.cs b/Perspex/Controls/Control.cs index 1f9a26afdc..dab6df339e 100644 --- a/Perspex/Controls/Control.cs +++ b/Perspex/Controls/Control.cs @@ -208,5 +208,48 @@ namespace Perspex.Controls } }); } + + protected DataTemplate FindDataTemplate(object content) + { + ILogical node = this; + + while (node != null) + { + Control control = node as Control; + + if (control != null) + { + foreach (DataTemplate dt in control.DataTemplates.Reverse()) + { + if (dt.Match(content)) + { + return dt; + } + } + } + + node = node.LogicalParent; + + if (node == null && control != null) + { + node = control.TemplatedParent as ILogical; + } + } + + foreach (DataTemplate dt in Application.Current.DataTemplates.Reverse()) + { + if (dt.Match(content)) + { + return dt; + } + } + + return null; + } + + protected DataTemplate GetDataTemplate(object content) + { + return this.FindDataTemplate(content) ?? DataTemplate.Default; + } } } diff --git a/Perspex/Controls/DataTemplate.cs b/Perspex/Controls/DataTemplate.cs index 6c15f6b3bd..44e9335276 100644 --- a/Perspex/Controls/DataTemplate.cs +++ b/Perspex/Controls/DataTemplate.cs @@ -11,6 +11,9 @@ namespace Perspex.Controls public class DataTemplate { + public static readonly DataTemplate Default = + new DataTemplate(typeof(object), o => new TextBox { Text = o.ToString() }); + public DataTemplate(Type type, Func build) : this(o => type.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo()), build) { @@ -18,6 +21,9 @@ namespace Perspex.Controls public DataTemplate(Func match, Func build) { + Contract.Requires(match != null); + Contract.Requires(build != null); + this.Match = match; this.Build = build; } diff --git a/Perspex/Controls/ItemsControl.cs b/Perspex/Controls/ItemsControl.cs new file mode 100644 index 0000000000..b574a5e296 --- /dev/null +++ b/Perspex/Controls/ItemsControl.cs @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using Perspex.Layout; + + public class ItemsControl : Control, IVisual + { + private static readonly ItemsPanelTemplate defaultPanel = + new ItemsPanelTemplate(() => new StackPanel { Orientation = Orientation.Vertical }); + + public static readonly PerspexProperty ItemsProperty = + PerspexProperty.Register("Items"); + + public static readonly PerspexProperty ItemsPanelProperty = + PerspexProperty.Register("ItemsPanel", defaultValue: defaultPanel); + + private Panel panel; + + public ItemsControl() + { + this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged); + } + + public IEnumerable Items + { + get { return this.GetValue(ItemsProperty); } + set { this.SetValue(ItemsProperty, value); } + } + + public ItemsPanelTemplate ItemsPanel + { + get { return this.GetValue(ItemsPanelProperty); } + set { this.SetValue(ItemsPanelProperty, value); } + } + + IEnumerable IVisual.ExistingVisualChildren + { + get { return Enumerable.Repeat(this.panel, this.panel != null ? 1 : 0); } + } + + IEnumerable IVisual.VisualChildren + { + get { return Enumerable.Repeat(this.GetPanel(), 1); } + } + + protected override Size MeasureOverride(Size availableSize) + { + Panel panel = this.GetPanel(); + panel.Measure(availableSize); + return panel.DesiredSize.Value; + } + + protected override Size ArrangeOverride(Size finalSize) + { + this.GetPanel().Arrange(new Rect(finalSize)); + return finalSize; + } + + private IEnumerable CreateItems(IEnumerable items) + { + if (items != null) + { + return items + .Cast() + .Select(x => this.GetDataTemplate(x).Build(x)) + .OfType(); + } + else + { + return Enumerable.Empty(); + } + } + + private Panel GetPanel() + { + if (this.panel == null) + { + this.panel = this.ItemsPanel.Build(); + this.panel.Children = new PerspexList(this.CreateItems(this.Items)); + } + + return this.panel; + } + + private void ItemsChanged(IEnumerable items) + { + this.GetPanel().Children = new PerspexList(this.CreateItems(items)); + } + } +} diff --git a/Perspex/Controls/ItemsPanelTemplate.cs b/Perspex/Controls/ItemsPanelTemplate.cs new file mode 100644 index 0000000000..1e2375c5b0 --- /dev/null +++ b/Perspex/Controls/ItemsPanelTemplate.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Controls +{ + public class ItemsPanelTemplate + { + public ItemsPanelTemplate(Func build) + { + Contract.Requires(build != null); + + this.Build = build; + } + + public Func Build { get; private set; } + } +} diff --git a/Perspex/Controls/Panel.cs b/Perspex/Controls/Panel.cs index ead4f2957d..239c4c32ff 100644 --- a/Perspex/Controls/Panel.cs +++ b/Perspex/Controls/Panel.cs @@ -53,7 +53,7 @@ namespace Perspex.Controls IEnumerable IVisual.VisualChildren { - get { return this.children; } + get { return this.children ?? Enumerable.Empty(); } } } } diff --git a/Perspex/Perspex.csproj b/Perspex/Perspex.csproj index 8395595291..90face9694 100644 --- a/Perspex/Perspex.csproj +++ b/Perspex/Perspex.csproj @@ -70,6 +70,8 @@ + + diff --git a/Perspex/PerspexList.cs b/Perspex/PerspexList.cs index 1735e73aaf..209699a388 100644 --- a/Perspex/PerspexList.cs +++ b/Perspex/PerspexList.cs @@ -16,10 +16,13 @@ namespace Perspex { public PerspexList() { - this.Changed = Observable.FromEvent( - handler => (sender, e) => handler(e), - handler => this.CollectionChanged += handler, - handler => this.CollectionChanged -= handler); + this.Initialize(); + } + + public PerspexList(IEnumerable items) + : base(items) + { + this.Initialize(); } public IObservable Changed @@ -35,5 +38,13 @@ namespace Perspex this.Add(item); } } + + private void Initialize() + { + this.Changed = Observable.FromEvent( + handler => (sender, e) => handler(e), + handler => this.CollectionChanged += handler, + handler => this.CollectionChanged -= handler); + } } } \ No newline at end of file diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index 9266712d01..7bd1db0e1e 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -36,6 +36,12 @@ namespace TestApplication } } + class Item + { + public string Name { get; set; } + public string Value { get; set; } + } + class Program { static void Main(string[] args) @@ -76,23 +82,38 @@ namespace TestApplication Source = new Bitmap("github_icon.png"), Width = 200, }, - new ContentControl + new ItemsControl { DataTemplates = new DataTemplates { - new DataTemplate(o => new Border + new DataTemplate(o => new Border { Background = Brushes.Red, BorderBrush = Brushes.Black, - BorderThickness = 2, - Content = new TextBlock + BorderThickness = 1, + Content = new StackPanel { - Text = o, + Orientation = Orientation.Vertical, + Children = new PerspexList + { + new TextBlock + { + Text = o.Name, + }, + new TextBlock + { + Text = o.Value, + } + }, } }), }, - Content = "Data Template", - }, + Items = new[] + { + new Item { Name = "Foo", Value = "Bar" }, + new Item { Name = "Buzz", Value = "Aldrin" }, + }, + } } } };