Browse Source

Track changes to collections in ItemsControl.

And add a simple UI to test adding nodes to tree view.
pull/4/head
Steven Kirk 12 years ago
parent
commit
c5566a19d3
  1. 71
      Perspex/Controls/ItemsControl.cs
  2. 77
      Perspex/Controls/ItemsPresenter.cs
  3. 7
      Perspex/Controls/Panel.cs
  4. 8
      Perspex/Controls/TabControl.cs
  5. 2
      Perspex/Controls/TreeView.cs
  6. 37
      Perspex/LogicalExtensions.cs
  7. 1
      Perspex/Perspex.csproj
  8. 50
      TestApplication/Program.cs

71
Perspex/Controls/ItemsControl.cs

@ -9,6 +9,8 @@ namespace Perspex.Controls
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq; using System.Linq;
public class ItemsControl : TemplatedControl public class ItemsControl : TemplatedControl
@ -22,11 +24,13 @@ namespace Perspex.Controls
public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty = public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty =
PerspexProperty.Register<ItemsControl, ItemsPanelTemplate>("ItemsPanel", defaultValue: DefaultPanel); PerspexProperty.Register<ItemsControl, ItemsPanelTemplate>("ItemsPanel", defaultValue: DefaultPanel);
private Dictionary<object, Control> itemControls = new Dictionary<object, Control>(); private Dictionary<object, Control> controlsByItem = new Dictionary<object, Control>();
private Dictionary<Control, object> itemsByControl = new Dictionary<Control, object>();
public ItemsControl() public ItemsControl()
{ {
this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged); this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged);
} }
public IEnumerable Items public IEnumerable Items
@ -44,30 +48,61 @@ namespace Perspex.Controls
public Control GetControlForItem(object item) public Control GetControlForItem(object item)
{ {
Control result; Control result;
this.itemControls.TryGetValue(item, out result); this.controlsByItem.TryGetValue(item, out result);
return result;
}
public object GetItemForControl(Control control)
{
object result;
this.itemsByControl.TryGetValue(control, out result);
return result; return result;
} }
public IEnumerable<Control> GetAllItemControls() public IEnumerable<Control> GetAllItemControls()
{ {
return this.itemControls.Values; return this.controlsByItem.Values;
} }
internal Control CreateItemControl(object item) internal Control CreateItemControl(object item)
{ {
Control control = this.CreateItemControlOverride(item); Control control = this.CreateItemControlOverride(item);
this.itemControls.Add(item, control); this.itemsByControl.Add(control, item);
this.controlsByItem.Add(item, control);
return control; return control;
} }
internal void RemoveItemControls(IEnumerable items)
{
foreach (object i in items)
{
Control control = this.GetControlForItem(i);
this.controlsByItem.Remove(i);
this.itemsByControl.Remove(control);
}
}
internal void ClearItemControls()
{
this.controlsByItem.Clear();
this.itemsByControl.Clear();
}
protected virtual Control CreateItemControlOverride(object item) protected virtual Control CreateItemControlOverride(object item)
{ {
return (item as Control) ?? this.GetDataTemplate(item).Build(item); return (item as Control) ?? this.GetDataTemplate(item).Build(item);
} }
private void ItemsChanged(IEnumerable items) private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value)
{ {
if (items == null || !items.OfType<object>().Any()) INotifyPropertyChanged inpc = value.Item1 as INotifyPropertyChanged;
if (inpc != null)
{
inpc.PropertyChanged -= ItemsPropertyChanged;
}
if (value.Item2 == null || !value.Item2.OfType<object>().Any())
{ {
this.Classes.Add(":empty"); this.Classes.Add(":empty");
} }
@ -75,6 +110,28 @@ namespace Perspex.Controls
{ {
this.Classes.Remove(":empty"); this.Classes.Remove(":empty");
} }
inpc = value.Item2 as INotifyPropertyChanged;
if (inpc != null)
{
inpc.PropertyChanged += ItemsPropertyChanged;
}
}
private void ItemsPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Count")
{
if (((IList)sender).Count == 0)
{
this.Classes.Add(":empty");
}
else
{
this.Classes.Remove(":empty");
}
}
} }
} }
} }

77
Perspex/Controls/ItemsPresenter.cs

@ -9,6 +9,7 @@ namespace Perspex.Controls
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using Perspex.Layout; using Perspex.Layout;
@ -24,7 +25,7 @@ namespace Perspex.Controls
public ItemsPresenter() public ItemsPresenter()
{ {
this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged); this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged);
} }
public IEnumerable Items public IEnumerable Items
@ -91,23 +92,55 @@ namespace Perspex.Controls
} }
} }
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);
}
}
private Panel GetPanel() private Panel GetPanel()
{ {
if (this.panel == null && this.ItemsPanel != null) if (this.panel == null && this.ItemsPanel != null)
{ {
this.panel = this.ItemsPanel.Build(); this.panel = this.ItemsPanel.Build();
((IVisual)this.panel).VisualParent = this; ((IVisual)this.panel).VisualParent = this;
this.ItemsChanged(this.Items); this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items));
} }
return this.panel; return this.panel;
} }
private void ItemsChanged(IEnumerable items) private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value)
{ {
if (value.Item1 != null)
{
INotifyCollectionChanged incc = value.Item1 as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged -= this.ItemsCollectionChanged;
}
}
this.ClearItemControls();
if (this.panel != null) if (this.panel != null)
{ {
var controls = this.CreateItemControls(items).ToList(); var controls = this.CreateItemControls(value.Item2).ToList();
foreach (var control in controls) foreach (var control in controls)
{ {
@ -115,6 +148,42 @@ namespace Perspex.Controls
} }
this.panel.Children = new Controls(controls); this.panel.Children = new Controls(controls);
INotifyCollectionChanged incc = value.Item2 as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged += this.ItemsCollectionChanged;
}
}
}
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (this.panel != null)
{
// TODO: Handle Move and Replace.
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);
break;
case NotifyCollectionChangedAction.Remove:
this.RemoveItemControls(e.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
this.ItemsChanged(Tuple.Create(this.Items, this.Items));
break;
}
} }
} }
} }

7
Perspex/Controls/Panel.cs

@ -17,7 +17,7 @@ namespace Perspex.Controls
/// <summary> /// <summary>
/// Base class for controls that can contain multiple children. /// Base class for controls that can contain multiple children.
/// </summary> /// </summary>
public class Panel : Control, IVisual public class Panel : Control, ILogical, IVisual
{ {
private Controls children; private Controls children;
@ -51,6 +51,11 @@ namespace Perspex.Controls
} }
} }
IEnumerable<ILogical> ILogical.LogicalChildren
{
get { return this.children ?? Enumerable.Empty<ILogical>(); }
}
IEnumerable<IVisual> IVisual.VisualChildren IEnumerable<IVisual> IVisual.VisualChildren
{ {
get { return this.children ?? Enumerable.Empty<IVisual>(); } get { return this.children ?? Enumerable.Empty<IVisual>(); }

8
Perspex/Controls/TabControl.cs

@ -7,10 +7,11 @@
namespace Perspex.Controls namespace Perspex.Controls
{ {
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
public class TabControl : SelectingItemsControl public class TabControl : SelectingItemsControl, ILogical
{ {
public static readonly PerspexProperty<object> SelectedContentProperty = public static readonly PerspexProperty<object> SelectedContentProperty =
PerspexProperty.Register<TabControl, object>("SelectedContent"); PerspexProperty.Register<TabControl, object>("SelectedContent");
@ -22,6 +23,11 @@ namespace Perspex.Controls
this.GetObservable(SelectedItemProperty).Skip(1).Subscribe(this.SelectedItemChanged); this.GetObservable(SelectedItemProperty).Skip(1).Subscribe(this.SelectedItemChanged);
} }
IEnumerable<ILogical> ILogical.LogicalChildren
{
get { return this.Items.OfType<TabItem>(); }
}
protected override void OnTemplateApplied() protected override void OnTemplateApplied()
{ {
this.tabStrip = this.GetTemplateControls() this.tabStrip = this.GetTemplateControls()

2
Perspex/Controls/TreeView.cs

@ -66,6 +66,8 @@ namespace Perspex.Controls
{ {
i.IsSelected = i == item; i.IsSelected = i == item;
} }
this.SelectedItem = this.GetItemForControl(item);
} }
} }

37
Perspex/LogicalExtensions.cs

@ -0,0 +1,37 @@
// -----------------------------------------------------------------------
// <copyright file="LogicalExtensions.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
using System.Collections.Generic;
using System.Linq;
using Perspex.Controls;
using Perspex.Styling;
public static class LogicalExtensions
{
public static T FindControl<T>(this ILogical control, string id) where T : Control
{
return control.GetLogicalDescendents()
.OfType<T>()
.FirstOrDefault(x => x.Id == id);
}
public static IEnumerable<ILogical> GetLogicalDescendents(this ILogical control)
{
foreach (ILogical child in control.LogicalChildren)
{
yield return child;
foreach (ILogical descendent in child.GetLogicalDescendents())
{
yield return descendent;
}
}
}
}
}

1
Perspex/Perspex.csproj

@ -218,6 +218,7 @@
<Compile Include="Visual.cs" /> <Compile Include="Visual.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ControlExtensions.cs" /> <Compile Include="ControlExtensions.cs" />
<Compile Include="LogicalExtensions.cs" />
<Compile Include="VisualExtensions.cs" /> <Compile Include="VisualExtensions.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

50
TestApplication/Program.cs

@ -1,5 +1,6 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Perspex; using Perspex;
using Perspex.Controls; using Perspex.Controls;
using Perspex.Layout; using Perspex.Layout;
@ -34,18 +35,23 @@ namespace TestApplication
class Node class Node
{ {
public Node()
{
this.Children = new PerspexList<Node>();
}
public string Name { get; set; } public string Name { get; set; }
public IEnumerable<Node> Children { get; set; } public PerspexList<Node> Children { get; set; }
} }
class Program class Program
{ {
private static Node[] treeData = new[] private static PerspexList<Node> treeData = new PerspexList<Node>
{ {
new Node new Node
{ {
Name = "Root 1", Name = "Root 1",
Children = new[] Children = new PerspexList<Node>
{ {
new Node new Node
{ {
@ -54,7 +60,7 @@ namespace TestApplication
new Node new Node
{ {
Name = "Child 2", Name = "Child 2",
Children = new[] Children = new PerspexList<Node>
{ {
new Node new Node
{ {
@ -176,7 +182,7 @@ namespace TestApplication
IsSelected = true, IsSelected = true,
Content = new StackPanel Content = new StackPanel
{ {
Orientation = Orientation.Vertical, Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Gap = 8, Gap = 8,
@ -184,8 +190,27 @@ namespace TestApplication
{ {
new TreeView new TreeView
{ {
Id = "treeView",
Items = treeData, 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",
},
}
},
} }
}, },
}, },
@ -193,7 +218,20 @@ namespace TestApplication
} }
}; };
//System.Console.WriteLine(Perspex.Diagnostics.Debug.PrintVisualTree(window)); 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(); window.Show();
Dispatcher.Run(); Dispatcher.Run();

Loading…
Cancel
Save