Раньше я как-то обходился без подобного в Silverlight. Всегда размещал TabItem в XAML коде, а не байндил коллекцию объектов, и при помощи DataTemplate настраивал вид того, что находится в TabItem.Content. Просто не было необходимости байндить коллекцию моих объектов (неких BindingModel) на TabControl.ItemsSource, а тут, буквально недавно, захотелось немного отрефакторить код, так как коллекция табов все росла, и управлять ею уже было сложно, и как раз придумал как это возможно сделать через описанный выше способ. Сказано – сделано. Потратил пару часов, переписал код, запускаю, и обнаруживаю такой вот exception:
System.ArgumentException: Unable to cast object of type 'SilverlightTabControl.Foo' to type 'System.Windows.Controls.TabItem'.
Быстро гуглю, нахожу на форумах Silverlight тему Databinding a TabControl (Я не одинок! Как показало более глубокое угугление, я совсем не одинок), а там
This is because currently TabControl doesn't override PrepareContainerForItemOverride, so it won't automatically wrap your data source in TabItems.
Ну и в качестве решения предлагается написать свой TabConverter. Microsoft, ну я точно помню, что в WPF байндинг на ItemsSource у TabControl’а работает прекрасно. Я это делал. Ладно, терпим, что в Silverlight контролах достаточно много багов, но тут-то просто ребята немного не доделали, а контрол зарелизили, да и сколько версий он уже живет? В реальности на первый взгляд нужно сделать 2 вещи:
В общем, ничего не оставалось, и я тоже написал конвертер из коллекции объектов в коллекцию TabItem.
/// <summary>
/// Convert collection ob objects to List of <see cref="TabItem"/>.
/// </summary>
public class CollectionToTabItemsConverter : IValueConverter
{
/// <summary>
/// Set <see cref="ControlTemplate"/> object to parameter
/// to change view of <see cref="TabItem"/>'s <see cref="TabItem.Content"/>
/// </summary>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IEnumerable source = value as IEnumerable;
if (source != null)
{
var controlTemplate = parameter as ControlTemplate;
List<TabItem> result = new List<TabItem>();
foreach (object item in source)
{
PropertyInfo[] propertyInfos = item.GetType().GetProperties();
// Reflection Magic: trying to get possible header properties
PropertyInfo propertyInfo = propertyInfos.First(x => x.Name == "Header" || x.Name == "Name");
string headerText = null;
if (propertyInfo != null)
{
object propValue = propertyInfo.GetValue(item, null);
headerText = (propValue ?? string.Empty).ToString();
}
Read more: outcoldman
System.ArgumentException: Unable to cast object of type 'SilverlightTabControl.Foo' to type 'System.Windows.Controls.TabItem'.
Быстро гуглю, нахожу на форумах Silverlight тему Databinding a TabControl (Я не одинок! Как показало более глубокое угугление, я совсем не одинок), а там
This is because currently TabControl doesn't override PrepareContainerForItemOverride, so it won't automatically wrap your data source in TabItems.
Ну и в качестве решения предлагается написать свой TabConverter. Microsoft, ну я точно помню, что в WPF байндинг на ItemsSource у TabControl’а работает прекрасно. Я это делал. Ладно, терпим, что в Silverlight контролах достаточно много багов, но тут-то просто ребята немного не доделали, а контрол зарелизили, да и сколько версий он уже живет? В реальности на первый взгляд нужно сделать 2 вещи:
- Переопределить метод ItemsControl.GetContainerForItemOverride, чтобы он возвращал TabItem.
- Переопределить метод ItemsControl.PrepareContainerForItemOverride, чтобы он тому созданному контейнеру из шага 1 выставлял нужный Header из DisplayMemberPath (там простая строка, путь до свойства), а так же выставил в Content элемент полученный из DataTemplate, указанный в ItemTemplate, а если не указан, то просто выставить туда элемент вашей байндинг модели.
В общем, ничего не оставалось, и я тоже написал конвертер из коллекции объектов в коллекцию TabItem.
/// <summary>
/// Convert collection ob objects to List of <see cref="TabItem"/>.
/// </summary>
public class CollectionToTabItemsConverter : IValueConverter
{
/// <summary>
/// Set <see cref="ControlTemplate"/> object to parameter
/// to change view of <see cref="TabItem"/>'s <see cref="TabItem.Content"/>
/// </summary>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IEnumerable source = value as IEnumerable;
if (source != null)
{
var controlTemplate = parameter as ControlTemplate;
List<TabItem> result = new List<TabItem>();
foreach (object item in source)
{
PropertyInfo[] propertyInfos = item.GetType().GetProperties();
// Reflection Magic: trying to get possible header properties
PropertyInfo propertyInfo = propertyInfos.First(x => x.Name == "Header" || x.Name == "Name");
string headerText = null;
if (propertyInfo != null)
{
object propValue = propertyInfo.GetValue(item, null);
headerText = (propValue ?? string.Empty).ToString();
}
Read more: outcoldman