Browse Source

Lots of changes involving ItemsControls.

Added ItemContainerGenerator mainly...
pull/4/head
Steven Kirk 12 years ago
parent
commit
c7a6582501
  1. 16
      Perspex.Base/IReadOnlyPerspexList.cs
  2. 1
      Perspex.Base/Perspex.Base.csproj
  3. 240
      Perspex.Base/PerspexList.cs
  4. 48
      Perspex.Controls/Control.cs
  5. 62
      Perspex.Controls/DataTemplateExtensions.cs
  6. 38
      Perspex.Controls/Generators/IItemContainerGenerator.cs
  7. 126
      Perspex.Controls/Generators/ItemContainerGenerator.cs
  8. 56
      Perspex.Controls/Generators/TreeItemContainerGenerator.cs
  9. 29
      Perspex.Controls/Generators/TypedItemContainerGenerator.cs
  10. 71
      Perspex.Controls/ItemsControl.cs
  11. 96
      Perspex.Controls/ItemsPresenter.cs
  12. 5
      Perspex.Controls/Perspex.Controls.csproj
  13. 44
      Perspex.Controls/TabControl.cs
  14. 43
      Perspex.Controls/TabStrip.cs
  15. 40
      Perspex.Controls/TreeView.cs
  16. 31
      Perspex.Controls/TreeViewItem.cs
  17. 2
      Perspex.SceneGraph/IVisual.cs
  18. 6
      Perspex.SceneGraph/Visual.cs
  19. 33
      TestApplication/Program.cs

16
Perspex.Base/IReadOnlyPerspexList.cs

@ -0,0 +1,16 @@
// -----------------------------------------------------------------------
// <copyright file="IReadOnlyPerspexList.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
public interface IReadOnlyPerspexList<T> : IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
}
}

1
Perspex.Base/Perspex.Base.csproj

@ -38,6 +38,7 @@
<Compile Include="Contract.cs" />
<Compile Include="IDescription.cs" />
<Compile Include="IObservableDescription.cs" />
<Compile Include="IReadOnlyPerspexList.cs" />
<Compile Include="PerspexList.cs" />
<Compile Include="PerspexObject.cs" />
<Compile Include="PerspexProperty.cs" />

240
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<T> : ObservableCollection<T>
public class PerspexList<T> : IList<T>, IList, IReadOnlyPerspexList<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
private List<T> inner;
public PerspexList()
: this(Enumerable.Empty<T>())
{
this.Initialize();
}
public PerspexList(IEnumerable<T> items)
: base(items)
{
this.Initialize();
this.inner = new List<T>(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<NotifyCollectionChangedEventArgs> 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<T> 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<T>();
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<T> 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<T> 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<T> items)
{
List<T> removed = new List<T>();
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<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
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();
}
}
}

48
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<Control>())
{
foreach (DataTemplate dt in i.DataTemplates.Reverse())
{
if (dt.Match(content))
{
return dt;
}
}
}
IGlobalDataTemplates global = Locator.Current.GetService<IGlobalDataTemplates>();
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);
}
}
}
}

62
Perspex.Controls/DataTemplateExtensions.cs

@ -0,0 +1,62 @@
// -----------------------------------------------------------------------
// <copyright file="DataTemplateExtensions.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
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<Control>())
{
foreach (DataTemplate dt in i.DataTemplates.Reverse())
{
if (dt.Match(data))
{
return dt;
}
}
}
IGlobalDataTemplates global = Locator.Current.GetService<IGlobalDataTemplates>();
if (global != null)
{
foreach (DataTemplate dt in global.DataTemplates.Reverse())
{
if (dt.Match(data))
{
return dt;
}
}
}
return null;
}
}
}

38
Perspex.Controls/Generators/IItemContainerGenerator.cs

@ -0,0 +1,38 @@
// -----------------------------------------------------------------------
// <copyright file="IItemContainerGenerator.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
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<Tuple<object, Control>> GetAll();
IEnumerable<Control> Generate(IEnumerable items);
IEnumerable<Control> Remove(IEnumerable item);
void RemoveAll();
}
}

126
Perspex.Controls/Generators/ItemContainerGenerator.cs

@ -0,0 +1,126 @@
// -----------------------------------------------------------------------
// <copyright file="ItemContainerGenerator.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Generators
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class ItemContainerGenerator : IItemContainerGenerator
{
private Dictionary<object, Control> containersByItem = new Dictionary<object, Control>();
private Dictionary<Control, object> itemsByContainer = new Dictionary<Control, object>();
private ItemContainerGeneratorState state;
public ItemContainerGenerator(ItemsControl owner)
{
this.Owner = owner;
}
public event EventHandler StateChanged;
public ItemContainerGeneratorState State
{
get
{
return this.state;
}
set
{
if (this.state != value)
{
this.state = value;
if (this.StateChanged != null)
{
this.StateChanged(this, EventArgs.Empty);
}
}
}
}
protected ItemsControl Owner
{
get;
private set;
}
public Control GetContainerForItem(object item)
{
Control result;
this.containersByItem.TryGetValue(item, out result);
return result;
}
public object GetItemForContainer(Control container)
{
object result;
this.itemsByContainer.TryGetValue(container, out result);
return result;
}
public IEnumerable<Tuple<object, Control>> GetAll()
{
return this.containersByItem.Select(x => Tuple.Create(x.Key, x.Value));
}
IEnumerable<Control> IItemContainerGenerator.Generate(IEnumerable items)
{
List<Control> result = new List<Control>();
this.State = ItemContainerGeneratorState.Generating;
try
{
foreach (object item in items)
{
Control container = this.CreateContainerOverride(item);
container.TemplatedParent = null;
this.containersByItem.Add(item, container);
this.itemsByContainer.Add(container, item);
result.Add(container);
}
}
finally
{
this.State = ItemContainerGeneratorState.Generated;
}
return result;
}
IEnumerable<Control> IItemContainerGenerator.Remove(IEnumerable items)
{
List<Control> result = new List<Control>();
foreach (var item in items)
{
Control container = this.containersByItem[item];
this.containersByItem.Remove(item);
this.itemsByContainer.Remove(container);
result.Add(container);
}
return result;
}
void IItemContainerGenerator.RemoveAll()
{
this.containersByItem.Clear();
this.itemsByContainer.Clear();
}
protected virtual Control CreateContainerOverride(object item)
{
return this.Owner.ApplyDataTemplate(item);
}
}
}

56
Perspex.Controls/Generators/TreeItemContainerGenerator.cs

@ -0,0 +1,56 @@
// -----------------------------------------------------------------------
// <copyright file="TreeItemContainerGenerator.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Generators
{
public class TreeItemContainerGenerator<T> : ItemContainerGenerator where T : TreeViewItem, new()
{
public TreeItemContainerGenerator(ItemsControl owner)
: base(owner)
{
}
protected override Control CreateContainerOverride(object item)
{
T result = item as T;
if (result == null)
{
TreeDataTemplate template = this.GetTreeDataTemplate(item);
System.Diagnostics.Debug.WriteLine("{0} created item for {1}", this.GetHashCode(), item);
result = new T
{
Header = template.Build(item),
Items = template.ItemsSelector(item),
IsExpanded = template.IsExpanded(item),
};
}
return result;
}
private TreeDataTemplate GetTreeDataTemplate(object item)
{
DataTemplate template = this.Owner.FindDataTemplate(item);
if (template == null)
{
template = DataTemplate.Default;
}
TreeDataTemplate treeTemplate = template as TreeDataTemplate;
if (treeTemplate == null)
{
treeTemplate = new TreeDataTemplate(template.Build, x => null);
}
return treeTemplate;
}
}
}

29
Perspex.Controls/Generators/TypedItemContainerGenerator.cs

@ -0,0 +1,29 @@
// -----------------------------------------------------------------------
// <copyright file="TypedItemContainerGenerator.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Generators
{
public class TypedItemContainerGenerator<T> : ItemContainerGenerator where T : ContentControl, new()
{
public TypedItemContainerGenerator(ItemsControl owner)
: base(owner)
{
}
protected override Control CreateContainerOverride(object item)
{
T result = item as T;
if (result == null)
{
result = new T();
result.Content = this.Owner.ApplyDataTemplate(item);
}
return result;
}
}
}

71
Perspex.Controls/ItemsControl.cs

@ -8,10 +8,9 @@ namespace Perspex.Controls
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Perspex.Controls.Generators;
public class ItemsControl : TemplatedControl
{
@ -24,73 +23,41 @@ namespace Perspex.Controls
public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty =
PerspexProperty.Register<ItemsControl, ItemsPanelTemplate>("ItemsPanel", defaultValue: DefaultPanel);
private Dictionary<object, Control> controlsByItem = new Dictionary<object, Control>();
private Dictionary<Control, object> itemsByControl = new Dictionary<Control, object>();
private ItemContainerGenerator itemContainerGenerator;
public ItemsControl()
{
this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged);
}
public IEnumerable Items
public ItemContainerGenerator ItemContainerGenerator
{
get { return this.GetValue(ItemsProperty); }
set { this.SetValue(ItemsProperty, value); }
}
public ItemsPanelTemplate ItemsPanel
{
get { return this.GetValue(ItemsPanelProperty); }
set { this.SetValue(ItemsPanelProperty, value); }
}
public Control GetControlForItem(object item)
{
Control result;
this.controlsByItem.TryGetValue(item, out result);
return result;
}
public object GetItemForControl(Control control)
{
object result;
this.itemsByControl.TryGetValue(control, out result);
return result;
}
public IEnumerable<Control> GetAllItemControls()
{
return this.controlsByItem.Values;
}
get
{
if (this.itemContainerGenerator == null)
{
this.itemContainerGenerator = this.CreateItemContainerGenerator();
}
internal Control CreateItemControl(object item)
{
Control control = this.CreateItemControlOverride(item);
this.itemsByControl.Add(control, item);
this.controlsByItem.Add(item, control);
return control;
return this.itemContainerGenerator;
}
}
internal void RemoveItemControls(IEnumerable items)
protected virtual ItemContainerGenerator CreateItemContainerGenerator()
{
foreach (object i in items)
{
Control control = this.GetControlForItem(i);
this.controlsByItem.Remove(i);
this.itemsByControl.Remove(control);
}
return new ItemContainerGenerator(this);
}
internal void ClearItemControls()
public IEnumerable Items
{
this.controlsByItem.Clear();
this.itemsByControl.Clear();
get { return this.GetValue(ItemsProperty); }
set { this.SetValue(ItemsProperty, value); }
}
protected virtual Control CreateItemControlOverride(object item)
public ItemsPanelTemplate ItemsPanel
{
return this.ApplyDataTemplate(item);
get { return this.GetValue(ItemsPanelProperty); }
set { this.SetValue(ItemsPanelProperty, value); }
}
private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value)

96
Perspex.Controls/ItemsPresenter.cs

@ -8,9 +8,9 @@ namespace Perspex.Controls
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Controls.Generators;
public class ItemsPresenter : Control, IVisual
{
@ -24,7 +24,7 @@ namespace Perspex.Controls
public ItemsPresenter()
{
this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged);
this.GetObservableWithHistory(ItemsProperty).Skip(1).Subscribe(this.ItemsChanged);
}
public IEnumerable Items
@ -58,53 +58,16 @@ namespace Perspex.Controls
this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items));
}
private Control CreateItemControl(object item)
private IItemContainerGenerator GetGenerator()
{
ItemsControl i = this.TemplatedParent as ItemsControl;
if (i != null)
if (i == null)
{
return i.CreateItemControl(item);
throw new InvalidOperationException("ItemsPresenter must be part of an ItemsControl template.");
}
else
{
return this.ApplyDataTemplate(item);
}
}
private IEnumerable<Control> CreateItemControls(IEnumerable items)
{
if (items != null)
{
return items
.Cast<object>()
.Select(x => this.CreateItemControl(x))
.OfType<Control>();
}
else
{
return Enumerable.Empty<Control>();
}
}
private void ClearItemControls()
{
ItemsControl i = this.TemplatedParent as ItemsControl;
if (i != null)
{
i.ClearItemControls();
}
}
private void RemoveItemControls(IEnumerable items)
{
ItemsControl i = this.TemplatedParent as ItemsControl;
if (i != null)
{
i.RemoveItemControls(items);
}
return i.ItemContainerGenerator;
}
private Panel GetPanel()
@ -119,8 +82,12 @@ namespace Perspex.Controls
private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value)
{
var generator = this.GetGenerator();
if (value.Item1 != null)
{
this.panel.Children.RemoveAll(generator.Remove(value.Item1));
INotifyCollectionChanged incc = value.Item1 as INotifyCollectionChanged;
if (incc != null)
@ -129,24 +96,18 @@ namespace Perspex.Controls
}
}
this.ClearItemControls();
if (this.panel != null)
{
var controls = this.CreateItemControls(value.Item2).ToList();
foreach (var control in controls)
if (value.Item2 != null)
{
control.TemplatedParent = null;
}
this.panel.Children.AddRange(generator.Generate(this.Items));
this.panel.Children = new Controls(controls);
INotifyCollectionChanged incc = value.Item2 as INotifyCollectionChanged;
INotifyCollectionChanged incc = value.Item2 as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged += this.ItemsCollectionChanged;
if (incc != null)
{
incc.CollectionChanged += this.ItemsCollectionChanged;
}
}
}
}
@ -155,28 +116,21 @@ namespace Perspex.Controls
{
if (this.panel != null)
{
// TODO: Handle Move and Replace.
var generator = this.GetGenerator();
// TODO: Handle Move and Replace etc.
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
var controls = this.CreateItemControls(e.NewItems).ToList();
foreach (var control in controls)
{
control.TemplatedParent = null;
}
this.panel.Children.AddRange(controls);
this.panel.Children.AddRange(generator.Generate(e.NewItems));
break;
case NotifyCollectionChangedAction.Remove:
this.RemoveItemControls(e.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
this.ItemsChanged(Tuple.Create(this.Items, this.Items));
this.panel.Children.RemoveAll(generator.Remove(e.OldItems));
break;
}
this.InvalidateMeasure();
}
}
}

5
Perspex.Controls/Perspex.Controls.csproj

@ -45,7 +45,11 @@
<Compile Include="ControlExtensions.cs" />
<Compile Include="Controls.cs" />
<Compile Include="ControlTemplate.cs" />
<Compile Include="DataTemplateExtensions.cs" />
<Compile Include="DataTemplate.cs" />
<Compile Include="Generators\TreeItemContainerGenerator.cs" />
<Compile Include="Generators\TypedItemContainerGenerator.cs" />
<Compile Include="Generators\ItemContainerGenerator.cs" />
<Compile Include="IGlobalDataTemplates.cs" />
<Compile Include="DataTemplates.cs" />
<Compile Include="Decorator.cs" />
@ -56,6 +60,7 @@
<Compile Include="HeaderedItemsControl.cs" />
<Compile Include="IHeadered.cs" />
<Compile Include="Image.cs" />
<Compile Include="Generators\IItemContainerGenerator.cs" />
<Compile Include="ItemsControl.cs" />
<Compile Include="ItemsPanelTemplate.cs" />
<Compile Include="ItemsPresenter.cs" />

44
Perspex.Controls/TabControl.cs

@ -7,9 +7,9 @@
namespace Perspex.Controls
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Controls.Generators;
public class TabControl : SelectingItemsControl
{
@ -20,43 +20,23 @@ namespace Perspex.Controls
public TabControl()
{
this.GetObservable(SelectedItemProperty).Skip(1).Subscribe(this.SelectedItemChanged);
this.GetObservable(SelectedItemProperty).Subscribe(x =>
{
ContentControl c = x as ContentControl;
object content = (c != null) ? c.Content : null;
this.SetValue(SelectedContentProperty, content);
});
}
protected override void OnTemplateApplied()
protected override ItemContainerGenerator CreateItemContainerGenerator()
{
this.tabStrip = this.GetTemplateControls()
.OfType<TabStrip>()
.FirstOrDefault();
if (this.tabStrip != null)
{
if (this.IsSet(SelectedItemProperty))
{
this.SelectedItem = SelectedItem;
}
this.tabStrip.GetObservable(TabStrip.SelectedItemProperty).Subscribe(x =>
{
this.SelectedItem = x;
});
}
return new TypedItemContainerGenerator<TabItem>(this);
}
private void SelectedItemChanged(object item)
protected override void OnTemplateApplied()
{
this.SelectedItem = item;
ContentControl content = item as ContentControl;
if (content != null)
{
this.SetValue(SelectedContentProperty, content.Content);
}
else
{
this.SetValue(SelectedContentProperty, item);
}
this.tabStrip = this.GetTemplateControls().OfType<TabStrip>().FirstOrDefault();
this.BindTwoWay(SelectedItemProperty, this.tabStrip, SelectedItemProperty);
}
}
}

43
Perspex.Controls/TabStrip.cs

@ -9,6 +9,7 @@ namespace Perspex.Controls
using System;
using System.Collections;
using System.Linq;
using Perspex.Controls.Generators;
using Perspex.Input;
public class TabStrip : SelectingItemsControl
@ -24,38 +25,38 @@ namespace Perspex.Controls
public TabStrip()
{
this.PointerPressed += this.OnPointerPressed;
this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged);
this.GetObservable(SelectedItemProperty).Subscribe(this.SelectedItemChanged);
}
protected override Control CreateItemControlOverride(object item)
protected override ItemContainerGenerator CreateItemContainerGenerator()
{
TabItem result = item as TabItem;
TabControl tabControl = this.TemplatedParent as TabControl;
ItemContainerGenerator result;
if (result == null)
if (tabControl != null)
{
result = new TabItem
{
Content = this.ApplyDataTemplate(item),
};
result = tabControl.ItemContainerGenerator;
}
else
{
result = new TypedItemContainerGenerator<TabItem>(this);
}
result.IsSelected = this.SelectedItem == item;
result.StateChanged += ItemsContainerGeneratorStateChanged;
return result;
}
private void ItemsChanged(IEnumerable items)
private void ItemsContainerGeneratorStateChanged(object sender, EventArgs e)
{
if (items != null)
if (this.ItemContainerGenerator.State == ItemContainerGeneratorState.Generated)
{
this.SelectedItem =
items.OfType<TabItem>().FirstOrDefault(x => x.IsSelected) ??
items.OfType<object>().FirstOrDefault();
}
else
{
this.SelectedItem = null;
var tabs = this.ItemContainerGenerator.GetAll()
.Select(x => x.Item2)
.OfType<TabItem>()
.ToList();
this.SelectedItem = tabs.FirstOrDefault(x => x.IsSelected) ?? tabs.FirstOrDefault();
}
}
@ -77,11 +78,11 @@ namespace Perspex.Controls
}
}
private void SelectedItemChanged(object selectedItem)
private void SelectedItemChanged(object selected)
{
foreach (TabItem item in this.GetAllItemControls())
foreach (TabItem item in this.ItemContainerGenerator.GetAll().Select(x => x.Item2))
{
item.IsSelected = item == selectedItem;
item.IsSelected = selected == item;
}
}
}

40
Perspex.Controls/TreeView.cs

@ -10,6 +10,7 @@ namespace Perspex.Controls
using System.Collections;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Controls.Generators;
using Perspex.Input;
public class TreeView : SelectingItemsControl
@ -19,42 +20,9 @@ namespace Perspex.Controls
this.PointerPressed += this.OnPointerPressed;
}
protected override Control CreateItemControlOverride(object item)
protected override ItemContainerGenerator CreateItemContainerGenerator()
{
TreeViewItem result = item as TreeViewItem;
if (result == null)
{
TreeDataTemplate template = this.GetTreeDataTemplate(item);
result = new TreeViewItem
{
Header = template.Build(item),
Items = template.ItemsSelector(item),
IsExpanded = template.IsExpanded(item),
};
}
return result;
}
private TreeDataTemplate GetTreeDataTemplate(object item)
{
DataTemplate template = this.FindDataTemplate(item);
if (template == null)
{
template = DataTemplate.Default;
}
TreeDataTemplate treeTemplate = template as TreeDataTemplate;
if (treeTemplate == null)
{
treeTemplate = new TreeDataTemplate(template.Build, x => null);
}
return treeTemplate;
return new TreeItemContainerGenerator<TreeViewItem>(this);
}
private void OnPointerPressed(object sender, PointerEventArgs e)
@ -75,7 +43,7 @@ namespace Perspex.Controls
i.IsSelected = i == item;
}
this.SelectedItem = this.GetItemForControl(item);
this.SelectedItem = this.ItemContainerGenerator.GetContainerForItem(item);
}
}

31
Perspex.Controls/TreeViewItem.cs

@ -8,6 +8,7 @@ namespace Perspex.Controls
{
using System;
using System.Linq;
using Perspex.Controls.Generators;
public class TreeViewItem : HeaderedItemsControl
{
@ -17,7 +18,7 @@ namespace Perspex.Controls
public static readonly PerspexProperty<bool> IsSelectedProperty =
PerspexProperty.Register<TreeViewItem, bool>("IsSelected");
TreeView parent;
TreeView treeView;
public TreeViewItem()
{
@ -37,21 +38,33 @@ namespace Perspex.Controls
set { this.SetValue(IsSelectedProperty, value); }
}
protected override Control CreateItemControlOverride(object item)
protected override ItemContainerGenerator CreateItemContainerGenerator()
{
if (this.parent != null)
if (this.treeView == null)
{
return this.parent.CreateItemControl(item);
}
else
{
throw new InvalidOperationException("TreeViewItem must be added to TreeView.");
throw new InvalidOperationException(
"Cannot get the ItemContainerGenerator for a TreeViewItem " +
"before it is added to a TreeView.");
}
return this.treeView.ItemContainerGenerator;
}
protected override void OnVisualParentChanged(Visual oldParent)
{
this.parent = this.GetVisualAncestors().OfType<TreeView>().FirstOrDefault();
if (this.GetVisualParent() != null)
{
this.treeView = this.GetVisualAncestors().OfType<TreeView>().FirstOrDefault();
if (this.treeView == null)
{
throw new InvalidOperationException("TreeViewItems must be added to a TreeView.");
}
}
else
{
this.treeView = null;
}
}
}
}

2
Perspex.SceneGraph/IVisual.cs

@ -51,7 +51,7 @@ namespace Perspex
/// <summary>
/// Gets the scene graph node's child nodes.
/// </summary>
IEnumerable<IVisual> VisualChildren { get; }
IReadOnlyPerspexList<IVisual> VisualChildren { get; }
/// <summary>
/// Gets the scene graph node's parent node.

6
Perspex.SceneGraph/Visual.cs

@ -30,7 +30,7 @@ namespace Perspex
private Rect bounds;
private PerspexList<Visual> visualChildren;
private PerspexList<IVisual> visualChildren;
private Visual visualParent;
@ -68,7 +68,7 @@ namespace Perspex
get { return this.bounds; }
}
IEnumerable<IVisual> IVisual.VisualChildren
IReadOnlyPerspexList<IVisual> IVisual.VisualChildren
{
get
{
@ -190,7 +190,7 @@ namespace Perspex
{
if (this.visualChildren == null)
{
this.visualChildren = new PerspexList<Visual>();
this.visualChildren = new PerspexList<IVisual>();
this.visualChildren.CollectionChanged += VisualChildrenChanged;
this.CreateVisualChildren();
}

33
TestApplication/Program.cs

@ -195,24 +195,6 @@ namespace TestApplication
Id = "treeView",
Items = treeData,
},
new StackPanel
{
Orientation = Orientation.Vertical,
Gap = 2.0,
Children = new Controls
{
new TextBox
{
Id = "newTreeViewItemText",
Text = "New Item"
},
new Button
{
Id = "addTreeViewItem",
Content = "Add",
},
}
},
}
},
},
@ -220,21 +202,6 @@ namespace TestApplication
}
};
//var treeView = window.FindControl<TreeView>("treeView");
//var newTreeViewItemText = window.FindControl<TextBox>("newTreeViewItemText");
//var addTreeViewItem = window.FindControl<Button>("addTreeViewItem");
//addTreeViewItem.Click += (s, e) =>
//{
// if (treeView.SelectedItem != null)
// {
// ((Node)treeView.SelectedItem).Children.Add(new Node
// {
// Name = newTreeViewItemText.Text,
// });
// }
//};
window.Show();
Application.Current.Run(window);
}

Loading…
Cancel
Save