Browse Source

Some work on TabControl/TabStrip.

pull/39/head
Steven Kirk 12 years ago
parent
commit
d25c4e0b4c
  1. 2
      Perspex.Controls/ControlExtensions.cs
  2. 19
      Perspex.Controls/ItemsControl.cs
  3. 32
      Perspex.Controls/Presenters/ItemsPresenter.cs
  4. 2
      Perspex.Controls/Primitives/SelectingItemsControl.cs
  5. 6
      Perspex.Controls/Primitives/TabStrip.cs
  6. 35
      Perspex.Controls/Primitives/TemplatedControl.cs
  7. 39
      Perspex.Controls/TabControl.cs
  8. 1
      Perspex.Themes.Default/ItemsControlStyle.cs
  9. 2
      Perspex.Themes.Default/TabControlStyle.cs
  10. 1
      Perspex.Themes.Default/TreeViewStyle.cs
  11. 4
      Tests/Perspex.Controls.UnitTests/ListBoxTests.cs
  12. 3
      Tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj
  13. 31
      Tests/Perspex.Controls.UnitTests/SelectingItemsControlTests.cs
  14. 161
      Tests/Perspex.Controls.UnitTests/TabControlTests.cs
  15. 102
      Tests/Perspex.Controls.UnitTests/TabStripTests.cs

2
Perspex.Controls/ControlExtensions.cs

@ -31,7 +31,7 @@ namespace Perspex.Controls
{
return visual.GetVisualDescendents()
.OfType<Control>()
.Where(x => x.TemplatedParent == control);
.Where(x => x.TemplatedParent != null);
}
else
{

19
Perspex.Controls/ItemsControl.cs

@ -87,21 +87,10 @@ namespace Perspex.Controls
this.logicalChildren = null;
}
this.presenter = this.FindTemplateChild<ItemsPresenter>("itemsPresenter");
if (this.presenter != null)
{
var panel = (IVisual)this.presenter.GetVisualChildren().Single();
this.logicalChildren = new PerspexReadOnlyListView<IVisual, ILogical>(
panel.VisualChildren,
x => (ILogical)x);
}
else
{
this.logicalChildren = new PerspexReadOnlyListView<IVisual, ILogical>(
new PerspexList<IVisual>(),
x => (ILogical)x);
}
this.presenter = this.GetTemplateChild<ItemsPresenter>("itemsPresenter");
this.logicalChildren = new PerspexReadOnlyListView<IVisual, ILogical>(
((IVisual)presenter.Panel).VisualChildren,
x => (ILogical)x);
}
private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value)

32
Perspex.Controls/Presenters/ItemsPresenter.cs

@ -21,8 +21,6 @@ namespace Perspex.Controls.Presenters
public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty =
ItemsControl.ItemsPanelProperty.AddOwner<ItemsPresenter>();
private Panel panel;
private bool createdPanel;
public ItemsPresenter()
@ -42,6 +40,12 @@ namespace Perspex.Controls.Presenters
set { this.SetValue(ItemsPanelProperty, value); }
}
public Panel Panel
{
get;
private set;
}
public override sealed void ApplyTemplate()
{
if (!this.createdPanel)
@ -52,23 +56,23 @@ namespace Perspex.Controls.Presenters
protected override Size MeasureOverride(Size availableSize)
{
this.panel.Measure(availableSize);
return this.panel.DesiredSize.Value;
this.Panel.Measure(availableSize);
return this.Panel.DesiredSize.Value;
}
protected override Size ArrangeOverride(Size finalSize)
{
this.panel.Arrange(new Rect(finalSize));
this.Panel.Arrange(new Rect(finalSize));
return finalSize;
}
private void CreatePanel()
{
this.ClearVisualChildren();
this.panel = this.ItemsPanel.Build();
this.panel.TemplatedParent = this;
((IItemsPanel)this.panel).ChildLogicalParent = this.TemplatedParent as ILogical;
this.AddVisualChild(this.panel);
this.Panel = this.ItemsPanel.Build();
this.Panel.TemplatedParent = this;
((IItemsPanel)this.Panel).ChildLogicalParent = this.TemplatedParent as ILogical;
this.AddVisualChild(this.Panel);
this.createdPanel = true;
this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items));
}
@ -93,7 +97,7 @@ namespace Perspex.Controls.Presenters
if (value.Item1 != null)
{
this.panel.Children.RemoveAll(generator.Remove(value.Item1));
this.Panel.Children.RemoveAll(generator.Remove(value.Item1));
INotifyCollectionChanged incc = value.Item1 as INotifyCollectionChanged;
@ -103,11 +107,11 @@ namespace Perspex.Controls.Presenters
}
}
if (this.panel != null)
if (this.Panel != null)
{
if (value.Item2 != null)
{
this.panel.Children.AddRange(generator.Generate(this.Items));
this.Panel.Children.AddRange(generator.Generate(this.Items));
INotifyCollectionChanged incc = value.Item2 as INotifyCollectionChanged;
@ -130,11 +134,11 @@ namespace Perspex.Controls.Presenters
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this.panel.Children.AddRange(generator.Generate(e.NewItems));
this.Panel.Children.AddRange(generator.Generate(e.NewItems));
break;
case NotifyCollectionChangedAction.Remove:
this.panel.Children.RemoveAll(generator.Remove(e.OldItems));
this.Panel.Children.RemoveAll(generator.Remove(e.OldItems));
break;
}

2
Perspex.Controls/Primitives/SelectingItemsControl.cs

@ -130,6 +130,8 @@ namespace Perspex.Controls.Primitives
this.SelectedItem = item;
}
}
e.Handled = true;
}
private void SelectedItemChanged(object selected)

6
Perspex.Controls/Primitives/TabStrip.cs

@ -26,15 +26,13 @@ namespace Perspex.Controls.Primitives
public TabStrip()
{
this.Bind(
SelectedTabProperty,
this.GetObservable(SelectedItemProperty).Select(x => x as TabItem));
this.BindTwoWay(SelectedTabProperty, this, SelectingItemsControl.SelectedItemProperty);
}
public TabItem SelectedTab
{
get { return this.GetValue(SelectedTabProperty); }
private set { this.SetValue(SelectedTabProperty, value); }
set { this.SetValue(SelectedTabProperty, value); }
}
protected override ItemContainerGenerator CreateItemContainerGenerator()

35
Perspex.Controls/Primitives/TemplatedControl.cs

@ -164,7 +164,37 @@ namespace Perspex.Controls.Primitives
protected T FindTemplateChild<T>(string id) where T : Control
{
return (T)this.GetTemplateControls().SingleOrDefault(x => x.Id == id);
var matches = this.GetTemplateControls()
.OfType<T>()
.Where(x => x.Id == id)
.ToList();
if (matches.Count == 1)
{
return matches[0];
}
else if (matches.Count == 0)
{
return null;
}
else
{
// If there are multiple matches, try filtering out nested matches.
matches = matches.Where(x => x.TemplatedParent == this).ToList();
if (matches.Count > 1)
{
throw new InvalidOperationException(string.Format(
"Found multiple template children '{0}' of type '{1}' in template for '{2}'.",
id,
typeof(T).FullName,
this.GetType().FullName));
}
else
{
return matches.FirstOrDefault();
}
}
}
protected T GetTemplateChild<T>(string id) where T : Control
@ -174,8 +204,9 @@ namespace Perspex.Controls.Primitives
if (result == null)
{
throw new InvalidOperationException(string.Format(
"Could not find template child '{0}' in template for '{1}'.",
"Could not find template child '{0}' of type '{1}' in template for '{2}'.",
id,
typeof(T).FullName,
this.GetType().FullName));
}

39
Perspex.Controls/TabControl.cs

@ -14,7 +14,7 @@ namespace Perspex.Controls
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
public class TabControl : SelectingItemsControl, ILogical
public class TabControl : SelectingItemsControl
{
public static readonly PerspexProperty<object> SelectedContentProperty =
PerspexProperty.Register<TabControl, object>("SelectedContent");
@ -22,12 +22,6 @@ namespace Perspex.Controls
public static readonly PerspexProperty<TabItem> SelectedTabProperty =
PerspexProperty.Register<TabControl, TabItem>("SelectedTab");
private TabStrip tabStrip;
private ContentPresenter presenter;
private IDisposable presenterSubscription;
private SingleItemPerspexList<ILogical> logicalChild = new SingleItemPerspexList<ILogical>();
public TabControl()
@ -39,9 +33,7 @@ namespace Perspex.Controls
this.SetValue(SelectedContentProperty, content);
});
this.Bind(
SelectedTabProperty,
this.GetObservable(SelectedItemProperty).Select(x => x as TabItem));
this.BindTwoWay(SelectedTabProperty, this, SelectingItemsControl.SelectedItemProperty);
}
public object SelectedContent
@ -53,37 +45,12 @@ namespace Perspex.Controls
public TabItem SelectedTab
{
get { return this.GetValue(SelectedTabProperty); }
private set { this.SetValue(SelectedTabProperty, value); }
}
IReadOnlyPerspexList<ILogical> ILogical.LogicalChildren
{
get { return this.logicalChild; }
set { this.SetValue(SelectedTabProperty, value); }
}
protected override ItemContainerGenerator CreateItemContainerGenerator()
{
return new TypedItemContainerGenerator<TabItem>(this);
}
protected override void OnTemplateApplied()
{
if (this.presenterSubscription != null)
{
this.presenterSubscription.Dispose();
this.presenterSubscription = null;
}
this.presenter = this.FindTemplateChild<ContentPresenter>("contentPresenter");
if (this.presenter != null)
{
this.presenterSubscription = this.presenter.ChildObservable
.Subscribe(x => this.logicalChild.SingleItem = x);
}
this.tabStrip = this.GetTemplateControls().OfType<TabStrip>().FirstOrDefault();
this.BindTwoWay(TabControl.SelectedItemProperty, this.tabStrip, TabControl.SelectedItemProperty);
}
}
}

1
Perspex.Themes.Default/ItemsControlStyle.cs

@ -31,6 +31,7 @@ namespace Perspex.Themes.Default
{
return new ItemsPresenter
{
Id = "itemsPresenter",
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
};

2
Perspex.Themes.Default/TabControlStyle.cs

@ -42,7 +42,9 @@ namespace Perspex.Themes.Default
{
new TabStrip
{
Id = "tabStrip",
[~TabStrip.ItemsProperty] = control[~TabControl.ItemsProperty],
[~~TabStrip.SelectedTabProperty] = control[~~TabControl.SelectedTabProperty],
},
new ContentPresenter
{

1
Perspex.Themes.Default/TreeViewStyle.cs

@ -42,6 +42,7 @@ namespace Perspex.Themes.Default
{
Content = new ItemsPresenter
{
Id = "itemsPresenter",
[~ItemsPresenter.ItemsProperty] = control[~TreeView.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~TreeView.ItemsPanelProperty],
}

4
Tests/Perspex.Controls.UnitTests/ListBoxTests.cs

@ -21,7 +21,7 @@ namespace Perspex.Controls.UnitTests
{
var target = new ListBox
{
Template = new ControlTemplate(x => this.CreateListBoxTemplate(x)),
Template = new ControlTemplate(this.CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
};
@ -39,7 +39,7 @@ namespace Perspex.Controls.UnitTests
{
return new ScrollViewer
{
Template = new ControlTemplate(x => this.CreateScrollViewerTemplate(x)),
Template = new ControlTemplate(this.CreateScrollViewerTemplate),
Content = new ItemsPresenter
{
Id = "itemsPresenter",

3
Tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

@ -65,10 +65,13 @@
</Choose>
<ItemGroup>
<Compile Include="DropDownTests.cs" />
<Compile Include="SelectingItemsControlTests.cs" />
<Compile Include="ListBoxTests.cs" />
<Compile Include="ItemsControlTests.cs" />
<Compile Include="ContentControlTests.cs" />
<Compile Include="DecoratorTests.cs" />
<Compile Include="TabStripTests.cs" />
<Compile Include="TabControlTests.cs" />
<Compile Include="PanelTests.cs" />
<Compile Include="ControlTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

31
Tests/Perspex.Controls.UnitTests/SelectingItemsControlTests.cs

@ -0,0 +1,31 @@
// -----------------------------------------------------------------------
// <copyright file="SelectingItemsControlTests.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.UnitTests
{
using Perspex.Controls.Primitives;
using Perspex.Input;
using Perspex.Interactivity;
using Xunit;
public class SelectingItemsControlTests
{
[Fact]
public void PointerPressed_Event_Should_Be_Handled()
{
var target = new SelectingItemsControl();
var e = new PointerPressEventArgs
{
RoutedEvent = InputElement.PointerPressedEvent
};
target.RaiseEvent(e);
Assert.True(e.Handled);
}
}
}

161
Tests/Perspex.Controls.UnitTests/TabControlTests.cs

@ -0,0 +1,161 @@
// -----------------------------------------------------------------------
// <copyright file="TabControlTests.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.UnitTests
{
using System.Linq;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.LogicalTree;
using Perspex.Styling;
using Xunit;
public class TabControlTests
{
[Fact]
public void First_Tab_Should_Be_Selected_By_Default()
{
var target = new TabControl
{
Template = ControlTemplate.Create<TabControl>(this.CreateTabControlTemplate),
Items = new[]
{
new TabItem
{
Id = "first"
},
new TabItem
{
Id = "second"
},
}
};
target.ApplyTemplate();
Assert.NotNull(target.SelectedTab);
Assert.Equal(target.SelectedTab, target.SelectedItem);
}
[Fact]
public void First_Tab_Content_Should_Be_Displayed_By_Default()
{
var target = new TabControl
{
Template = ControlTemplate.Create<TabControl>(this.CreateTabControlTemplate),
Items = new[]
{
new TabItem
{
Content = new TextBlock(),
},
new TabItem
{
Content = new Border(),
},
}
};
target.ApplyTemplate();
Assert.IsType<TextBlock>(target.SelectedContent);
}
[Fact]
public void Setting_SelectedItem_Should_Set_SelectedTab()
{
var target = new TabControl
{
Template = ControlTemplate.Create<TabControl>(this.CreateTabControlTemplate),
Items = new[]
{
new TabItem
{
Id = "first"
},
new TabItem
{
Id = "second"
},
}
};
target.ApplyTemplate();
target.SelectedItem = target.Items.Cast<TabItem>().ElementAt(1);
Assert.Same(target.SelectedTab, target.SelectedItem);
}
[Fact]
public void Setting_SelectedTab_Should_Set_SelectedItem()
{
var target = new TabControl
{
Template = ControlTemplate.Create<TabControl>(this.CreateTabControlTemplate),
Items = new[]
{
new TabItem
{
Id = "first"
},
new TabItem
{
Id = "second"
},
}
};
target.ApplyTemplate();
target.SelectedTab = target.Items.Cast<TabItem>().ElementAt(1);
Assert.Same(target.SelectedItem, target.SelectedTab);
}
[Fact]
public void Logical_Children_Should_Be_TabItems()
{
var target = new TabControl
{
Template = ControlTemplate.Create<TabControl>(this.CreateTabControlTemplate),
Items = new[] { "Foo", "Bar" },
};
target.ApplyTemplate();
Assert.Equal(2, target.GetLogicalChildren().Count());
}
private Control CreateTabControlTemplate(TabControl parent)
{
return new StackPanel
{
Children = new Controls
{
new TabStrip
{
Id = "tabStrip",
Template = ControlTemplate.Create<TabStrip>(this.CreateTabStripTemplate),
[~TabStrip.ItemsProperty] = parent[~TabControl.ItemsProperty],
[~~TabStrip.SelectedTabProperty] = parent[~~TabControl.SelectedTabProperty]
},
new ContentPresenter
{
Id = "contentPresenter",
}
}
};
}
private Control CreateTabStripTemplate(TabStrip parent)
{
return new ItemsPresenter
{
Id = "itemsPresenter",
[~ItemsPresenter.ItemsProperty] = parent[~TabStrip.ItemsProperty],
};
}
}
}

102
Tests/Perspex.Controls.UnitTests/TabStripTests.cs

@ -0,0 +1,102 @@
// -----------------------------------------------------------------------
// <copyright file="TabStripTests.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.UnitTests
{
using System.Linq;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.LogicalTree;
using Perspex.Styling;
using Xunit;
public class TabStripTests
{
[Fact]
public void First_Tab_Should_Be_Selected_By_Default()
{
var target = new TabStrip
{
Template = ControlTemplate.Create<TabStrip>(this.CreateTabStripTemplate),
Items = new[]
{
new TabItem
{
Id = "first"
},
new TabItem
{
Id = "second"
},
}
};
target.ApplyTemplate();
Assert.Equal(target.Items.Cast<TabItem>().First(), target.SelectedItem);
Assert.Equal(target.Items.Cast<TabItem>().First(), target.SelectedTab);
}
[Fact]
public void Setting_SelectedItem_Should_Set_SelectedTab()
{
var target = new TabStrip
{
Template = ControlTemplate.Create<TabStrip>(this.CreateTabStripTemplate),
Items = new[]
{
new TabItem
{
Id = "first"
},
new TabItem
{
Id = "second"
},
}
};
target.ApplyTemplate();
target.SelectedItem = target.Items.Cast<TabItem>().ElementAt(1);
Assert.Same(target.SelectedTab, target.SelectedItem);
}
[Fact]
public void Setting_SelectedTab_Should_Set_SelectedItem()
{
var target = new TabStrip
{
Template = ControlTemplate.Create<TabStrip>(this.CreateTabStripTemplate),
Items = new[]
{
new TabItem
{
Id = "first"
},
new TabItem
{
Id = "second"
},
}
};
target.ApplyTemplate();
target.SelectedTab = target.Items.Cast<TabItem>().ElementAt(1);
Assert.Same(target.SelectedItem, target.SelectedTab);
}
private Control CreateTabStripTemplate(TabStrip parent)
{
return new ItemsPresenter
{
Id = "itemsPresenter",
[~ItemsPresenter.ItemsProperty] = parent[~TabStrip.ItemsProperty],
};
}
}
}
Loading…
Cancel
Save