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.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
public class ItemsControl : TemplatedControl
@ -22,11 +24,13 @@ namespace Perspex.Controls
public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty =
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()
{
this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged);
this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged);
}
public IEnumerable Items
@ -44,30 +48,61 @@ namespace Perspex.Controls
public Control GetControlForItem(object item)
{
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;
}
public IEnumerable<Control> GetAllItemControls()
{
return this.itemControls.Values;
return this.controlsByItem.Values;
}
internal Control CreateItemControl(object item)
{
Control control = this.CreateItemControlOverride(item);
this.itemControls.Add(item, control);
this.itemsByControl.Add(control, item);
this.controlsByItem.Add(item, 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)
{
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");
}
@ -75,6 +110,28 @@ namespace Perspex.Controls
{
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.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Perspex.Layout;
@ -24,7 +25,7 @@ namespace Perspex.Controls
public ItemsPresenter()
{
this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged);
this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged);
}
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()
{
if (this.panel == null && this.ItemsPanel != null)
{
this.panel = this.ItemsPanel.Build();
((IVisual)this.panel).VisualParent = this;
this.ItemsChanged(this.Items);
this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items));
}
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)
{
var controls = this.CreateItemControls(items).ToList();
var controls = this.CreateItemControls(value.Item2).ToList();
foreach (var control in controls)
{
@ -115,6 +148,42 @@ namespace Perspex.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>
/// Base class for controls that can contain multiple children.
/// </summary>
public class Panel : Control, IVisual
public class Panel : Control, ILogical, IVisual
{
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
{
get { return this.children ?? Enumerable.Empty<IVisual>(); }

8
Perspex/Controls/TabControl.cs

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

2
Perspex/Controls/TreeView.cs

@ -66,6 +66,8 @@ namespace Perspex.Controls
{
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="Properties\AssemblyInfo.cs" />
<Compile Include="ControlExtensions.cs" />
<Compile Include="LogicalExtensions.cs" />
<Compile Include="VisualExtensions.cs" />
</ItemGroup>
<ItemGroup>

50
TestApplication/Program.cs

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

Loading…
Cancel
Save