// -----------------------------------------------------------------------
//
// Copyright 2014 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
namespace Perspex.Controls.Presenters
{
using System;
using System.Collections;
using System.Collections.Specialized;
using Perspex.Controls.Generators;
using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.Styling;
///
/// Displays items inside an .
///
public class ItemsPresenter : Control, IItemsPresenter, ITemplatedControl
{
///
/// Defines the property.
///
public static readonly PerspexProperty ItemsProperty =
ItemsControl.ItemsProperty.AddOwner();
///
/// Defines the property.
///
public static readonly PerspexProperty ItemsPanelProperty =
ItemsControl.ItemsPanelProperty.AddOwner();
private bool createdPanel;
private IItemContainerGenerator generator;
///
/// Initializes static members of the class.
///
static ItemsPresenter()
{
KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue(
typeof(ItemsPresenter),
KeyboardNavigationMode.Once);
ItemsProperty.Changed.AddClassHandler(x => x.ItemsChanged);
}
///
/// Initializes a new instance of the class.
///
public ItemsPresenter()
{
}
///
/// Gets the used to generate item container
/// controls.
///
public IItemContainerGenerator ItemContainerGenerator
{
get
{
if (this.generator == null)
{
var i = this.TemplatedParent as ItemsControl;
this.generator = i?.ItemContainerGenerator ?? new ItemContainerGenerator(this);
}
return this.generator;
}
set
{
if (this.generator != null)
{
throw new InvalidOperationException("ItemContainerGenerator is already set.");
}
this.generator = value;
}
}
///
/// Gets or sets the items to be displayed.
///
public IEnumerable Items
{
get { return this.GetValue(ItemsProperty); }
set { this.SetValue(ItemsProperty, value); }
}
///
/// Gets or sets a template which creates the used to display the items.
///
public ItemsPanelTemplate ItemsPanel
{
get { return this.GetValue(ItemsPanelProperty); }
set { this.SetValue(ItemsPanelProperty, value); }
}
///
/// Gets the panel used to display the items.
///
public Panel Panel
{
get;
private set;
}
///
public override sealed void ApplyTemplate()
{
if (!this.createdPanel)
{
this.CreatePanel();
}
}
///
protected override Size MeasureOverride(Size availableSize)
{
this.Panel.Measure(availableSize);
return this.Panel.DesiredSize;
}
///
protected override Size ArrangeOverride(Size finalSize)
{
this.Panel.Arrange(new Rect(finalSize));
return finalSize;
}
///
/// Creates the when is called for the first
/// time.
///
private void CreatePanel()
{
this.ClearVisualChildren();
this.Panel = this.ItemsPanel.Build();
this.Panel.SetValue(TemplatedParentProperty, this.TemplatedParent);
if (!this.Panel.IsSet(KeyboardNavigation.DirectionalNavigationProperty))
{
KeyboardNavigation.SetDirectionalNavigation(this.Panel, KeyboardNavigationMode.Contained);
}
this.AddVisualChild(this.Panel);
var logicalHost = this.FindReparentingHost();
if (logicalHost != null)
{
((IReparentingControl)this.Panel).ReparentLogicalChildren(
logicalHost,
logicalHost.LogicalChildren);
}
KeyboardNavigation.SetTabNavigation(this.Panel, KeyboardNavigation.GetTabNavigation(this));
this.createdPanel = true;
this.CreateItemsAndListenForChanges(this.Items);
}
///
/// Creates the items for a collection and starts listening for changes on the collection.
///
/// The items, may be null.
private void CreateItemsAndListenForChanges(IEnumerable items)
{
if (items != null)
{
this.Panel.Children.AddRange(
this.ItemContainerGenerator.CreateContainers(0, this.Items, null));
INotifyCollectionChanged incc = items as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged += this.ItemsCollectionChanged;
}
}
}
///
/// Called when the property changes.
///
/// The event args.
private void ItemsChanged(PerspexPropertyChangedEventArgs e)
{
if (this.createdPanel)
{
var generator = this.ItemContainerGenerator;
if (e.OldValue != null)
{
generator.ClearContainers();
this.Panel.Children.Clear();
INotifyCollectionChanged incc = e.OldValue as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged -= this.ItemsCollectionChanged;
}
}
if (this.Panel != null)
{
this.CreateItemsAndListenForChanges((IEnumerable)e.NewValue);
}
}
}
///
/// Called when the collection changes.
///
/// The sender.
/// The event args.
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (this.createdPanel)
{
var generator = this.ItemContainerGenerator;
// TODO: Handle Move and Replace etc.
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this.Panel.Children.AddRange(
generator.CreateContainers(e.NewStartingIndex, e.NewItems, null));
break;
case NotifyCollectionChangedAction.Remove:
this.Panel.Children.RemoveAll(
generator.RemoveContainers(e.OldStartingIndex, e.OldItems));
break;
}
this.InvalidateMeasure();
}
}
}
}