diff --git a/Perspex.Base/IReadOnlyPerspexList.cs b/Perspex.Base/IReadOnlyPerspexList.cs
new file mode 100644
index 0000000000..87b7074bd2
--- /dev/null
+++ b/Perspex.Base/IReadOnlyPerspexList.cs
@@ -0,0 +1,16 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex
+{
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.ComponentModel;
+
+ public interface IReadOnlyPerspexList : IReadOnlyList, INotifyCollectionChanged, INotifyPropertyChanged
+ {
+ }
+}
\ No newline at end of file
diff --git a/Perspex.Base/Perspex.Base.csproj b/Perspex.Base/Perspex.Base.csproj
index 48a0db4c96..637a4d6120 100644
--- a/Perspex.Base/Perspex.Base.csproj
+++ b/Perspex.Base/Perspex.Base.csproj
@@ -38,6 +38,7 @@
+
diff --git a/Perspex.Base/PerspexList.cs b/Perspex.Base/PerspexList.cs
index 209699a388..80fdaeb37d 100644
--- a/Perspex.Base/PerspexList.cs
+++ b/Perspex.Base/PerspexList.cs
@@ -7,44 +7,252 @@
namespace Perspex
{
using System;
+ using System.Collections;
using System.Collections.Generic;
- using System.Collections.ObjectModel;
using System.Collections.Specialized;
- using System.Reactive.Linq;
+ using System.ComponentModel;
+ using System.Linq;
- public class PerspexList : ObservableCollection
+ public class PerspexList : IList, IList, IReadOnlyPerspexList, INotifyCollectionChanged, INotifyPropertyChanged
{
+ private List inner;
+
public PerspexList()
+ : this(Enumerable.Empty())
{
- this.Initialize();
}
public PerspexList(IEnumerable items)
- : base(items)
{
- this.Initialize();
+ this.inner = new List(items);
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public T this[int index]
+ {
+ get
+ {
+ return this.inner[index];
+ }
+
+ set
+ {
+ T old = this.inner[index];
+ this.inner[index] = value;
+
+ if (this.CollectionChanged != null)
+ {
+ var e = new NotifyCollectionChangedEventArgs(
+ NotifyCollectionChangedAction.Replace,
+ value,
+ old);
+ this.CollectionChanged(this, e);
+ }
+ }
+ }
+
+ public int Count
+ {
+ get { return this.inner.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ bool IList.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ bool IList.IsReadOnly
+ {
+ get { return false; }
}
- public IObservable Changed
+ object IList.this[int index]
{
- get;
- private set;
+ get { return this[index]; }
+
+ set { this[index] = (T)value; }
+ }
+
+ int ICollection.Count
+ {
+ get { return this.inner.Count; }
+ }
+
+ bool ICollection.IsSynchronized
+ {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot
+ {
+ get { return null; }
+ }
+
+ public void Add(T item)
+ {
+ int index = this.inner.Count;
+ this.inner.Add(item);
+ this.NotifyAdd(new[] { item });
}
public void AddRange(IEnumerable items)
{
- foreach (T item in items)
+ int index = this.inner.Count;
+ this.inner.AddRange(items);
+ this.NotifyAdd((items as IList) ?? items.ToList());
+ }
+
+ public void Clear()
+ {
+ var old = this.inner;
+ this.inner = new List();
+ this.NotifyRemove(old);
+ }
+
+ public bool Contains(T item)
+ {
+ return this.inner.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ this.inner.CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return this.inner.GetEnumerator();
+ }
+
+ public int IndexOf(T item)
+ {
+ return this.inner.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ this.inner.Insert(index, item);
+ this.NotifyAdd(new[] { item });
+ }
+
+ public void InsertRange(int index, IEnumerable items)
+ {
+ this.inner.InsertRange(index, items);
+ this.NotifyAdd((items as IList) ?? items.ToList());
+ }
+
+ public bool Remove(T item)
+ {
+ bool result = this.inner.Remove(item);
+ this.NotifyRemove(new[] { item });
+ return result;
+ }
+
+ public void RemoveAll(IEnumerable items)
+ {
+ List removed = new List();
+
+ foreach (var i in items)
+ {
+ if (this.inner.Remove(i))
+ {
+ removed.Add(i);
+ }
+ }
+
+ this.NotifyRemove(removed);
+ }
+
+ public void RemoveAt(int index)
+ {
+ T item = this.inner[index];
+ this.inner.RemoveAt(index);
+ this.NotifyRemove(new[] { item });
+ }
+
+ int IList.Add(object value)
+ {
+ int index = this.Count;
+ this.Add((T)value);
+ return index;
+ }
+
+ bool IList.Contains(object value)
+ {
+ return this.Contains((T)value);
+ }
+
+ void IList.Clear()
+ {
+ this.Clear();
+ }
+
+ int IList.IndexOf(object value)
+ {
+ return this.IndexOf((T)value);
+ }
+
+ void IList.Insert(int index, object value)
+ {
+ this.Insert(index, (T)value);
+ }
+
+ void IList.Remove(object value)
+ {
+ this.Remove((T)value);
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ this.RemoveAt(index);
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ this.inner.CopyTo((T[])array, index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.inner.GetEnumerator();
+ }
+
+ private void NotifyAdd(IList t)
+ {
+ if (this.CollectionChanged != null)
{
- this.Add(item);
+ var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, t);
+ this.CollectionChanged(this, e);
}
+
+ this.NotifyCountChanged();
}
- private void Initialize()
+ private void NotifyCountChanged()
{
- this.Changed = Observable.FromEvent(
- handler => (sender, e) => handler(e),
- handler => this.CollectionChanged += handler,
- handler => this.CollectionChanged -= handler);
+ if (this.PropertyChanged != null)
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
+ }
+ }
+
+ private void NotifyRemove(IList t)
+ {
+ if (this.CollectionChanged != null)
+ {
+ var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, t);
+ this.CollectionChanged(this, e);
+ }
+
+ this.NotifyCountChanged();
}
}
}
\ No newline at end of file
diff --git a/Perspex.Controls/Control.cs b/Perspex.Controls/Control.cs
index 75f68f838a..62271e5fa9 100644
--- a/Perspex.Controls/Control.cs
+++ b/Perspex.Controls/Control.cs
@@ -188,53 +188,5 @@ namespace Perspex.Controls
}
});
}
-
- protected virtual DataTemplate FindDataTemplate(object content)
- {
- // TODO: This needs to traverse the logical tree, not the visual.
- foreach (var i in this.GetSelfAndVisualAncestors().OfType())
- {
- foreach (DataTemplate dt in i.DataTemplates.Reverse())
- {
- if (dt.Match(content))
- {
- return dt;
- }
- }
- }
-
- IGlobalDataTemplates global = Locator.Current.GetService();
-
- if (global != null)
- {
- foreach (DataTemplate dt in global.DataTemplates.Reverse())
- {
- if (dt.Match(content))
- {
- return dt;
- }
- }
- }
-
- return null;
- }
-
- protected Control ApplyDataTemplate(object content)
- {
- DataTemplate result = this.FindDataTemplate(content);
-
- if (result != null)
- {
- return result.Build(content);
- }
- else if (content is Control)
- {
- return (Control)content;
- }
- else
- {
- return DataTemplate.Default.Build(content);
- }
- }
}
}
diff --git a/Perspex.Controls/DataTemplateExtensions.cs b/Perspex.Controls/DataTemplateExtensions.cs
new file mode 100644
index 0000000000..48e6c51c82
--- /dev/null
+++ b/Perspex.Controls/DataTemplateExtensions.cs
@@ -0,0 +1,62 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls
+{
+ using System.Linq;
+ using Splat;
+
+ public static class DataTemplateExtensions
+ {
+ public static Control ApplyDataTemplate(this Control control, object data)
+ {
+ DataTemplate result = control.FindDataTemplate(data);
+
+ if (result != null)
+ {
+ return result.Build(data);
+ }
+ else if (data is Control)
+ {
+ return (Control)data;
+ }
+ else
+ {
+ return DataTemplate.Default.Build(data);
+ }
+ }
+
+ public static DataTemplate FindDataTemplate(this Control control, object data)
+ {
+ // TODO: This needs to traverse the logical tree, not the visual.
+ foreach (var i in control.GetSelfAndVisualAncestors().OfType())
+ {
+ foreach (DataTemplate dt in i.DataTemplates.Reverse())
+ {
+ if (dt.Match(data))
+ {
+ return dt;
+ }
+ }
+ }
+
+ IGlobalDataTemplates global = Locator.Current.GetService();
+
+ if (global != null)
+ {
+ foreach (DataTemplate dt in global.DataTemplates.Reverse())
+ {
+ if (dt.Match(data))
+ {
+ return dt;
+ }
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Perspex.Controls/Generators/IItemContainerGenerator.cs b/Perspex.Controls/Generators/IItemContainerGenerator.cs
new file mode 100644
index 0000000000..29c6f4a57f
--- /dev/null
+++ b/Perspex.Controls/Generators/IItemContainerGenerator.cs
@@ -0,0 +1,38 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls.Generators
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+
+ public enum ItemContainerGeneratorState
+ {
+ NoStarted,
+ Generating,
+ Generated,
+ }
+
+ public interface IItemContainerGenerator
+ {
+ event EventHandler StateChanged;
+
+ ItemContainerGeneratorState State { get; }
+
+ Control GetContainerForItem(object item);
+
+ object GetItemForContainer(Control container);
+
+ IEnumerable> GetAll();
+
+ IEnumerable Generate(IEnumerable items);
+
+ IEnumerable Remove(IEnumerable item);
+
+ void RemoveAll();
+ }
+}
diff --git a/Perspex.Controls/Generators/ItemContainerGenerator.cs b/Perspex.Controls/Generators/ItemContainerGenerator.cs
new file mode 100644
index 0000000000..129b52d3ba
--- /dev/null
+++ b/Perspex.Controls/Generators/ItemContainerGenerator.cs
@@ -0,0 +1,126 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls.Generators
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class ItemContainerGenerator : IItemContainerGenerator
+ {
+ private Dictionary