// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Controls.Presenters { using System; using System.Collections; using System.Collections.Specialized; using System.Reactive.Linq; using Perspex.Controls.Generators; public class ItemsPresenter : Control, IVisual { public static readonly PerspexProperty ItemsProperty = ItemsControl.ItemsProperty.AddOwner(); public static readonly PerspexProperty ItemsPanelProperty = ItemsControl.ItemsPanelProperty.AddOwner(); private Panel panel; private bool createdPanel; public ItemsPresenter() { this.GetObservableWithHistory(ItemsProperty).Skip(1).Subscribe(this.ItemsChanged); } public IEnumerable Items { get { return this.GetValue(ItemsProperty); } set { this.SetValue(ItemsProperty, value); } } public ItemsPanelTemplate ItemsPanel { get { return this.GetValue(ItemsPanelProperty); } set { this.SetValue(ItemsPanelProperty, value); } } protected override Size MeasureOverride(Size availableSize) { if (!this.createdPanel) { this.CreatePanel(); } panel.Measure(availableSize); return panel.DesiredSize.Value; } protected override Size ArrangeOverride(Size finalSize) { this.panel.Arrange(new Rect(finalSize)); return finalSize; } private void CreatePanel() { this.ClearVisualChildren(); this.panel = this.ItemsPanel.Build(); this.AddVisualChild(this.panel); this.createdPanel = true; this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items)); } private IItemContainerGenerator GetGenerator() { ItemsControl i = this.TemplatedParent as ItemsControl; if (i == null) { throw new InvalidOperationException("ItemsPresenter must be part of an ItemsControl template."); } return i.ItemContainerGenerator; } private void ItemsChanged(Tuple value) { if (this.createdPanel) { 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) { incc.CollectionChanged -= this.ItemsCollectionChanged; } } if (this.panel != null) { if (value.Item2 != null) { this.panel.Children.AddRange(generator.Generate(this.Items)); INotifyCollectionChanged incc = value.Item2 as INotifyCollectionChanged; if (incc != null) { incc.CollectionChanged += this.ItemsCollectionChanged; } } } } } private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (this.createdPanel) { var generator = this.GetGenerator(); // TODO: Handle Move and Replace etc. switch (e.Action) { case NotifyCollectionChangedAction.Add: this.panel.Children.AddRange(generator.Generate(e.NewItems)); break; case NotifyCollectionChangedAction.Remove: this.panel.Children.RemoveAll(generator.Remove(e.OldItems)); break; } this.InvalidateMeasure(); } } } }