Как в TabControl привязать закладки из ViewModel?
WPF, MVVM, Silverlight | создано: 01.02.2011 | опубликовано: 01.02.2011 | обновлено: 13.01.2024 | просмотров: 5859 | всего комментариев: 2
Вот совершенно случайно потребовалось привязать некоторую коллекцию закладок в контролу TabControl причем из ViewModel. Но так как контрол (вернее завершенность и качество) оставляет желать лучшего, приходится сталкиваться с проблемами типа: "Unable to cast object of type 'TabControlBinding.Foo' to type 'System.Windows.Controls.TabItem'.". Я предлагаю решение при помощи AttachedProperty.
Есть пример, давайте его разберем кратко. Есть главная страница MainPage.xaml:
<UserControl
x:Class=TabControlBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:local="clr-namespace:TabControlBinding"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.DataContext>
<local:ViewModel />
</UserControl.DataContext>
<Grid
x:Name="LayoutRoot"
Background="White">
<Controls:TabControl
Grid.Row="1"
local:TabControlHelper.ItemsCollection="{Binding Path=Collection}">
</Controls:TabControl>
</Grid>
</UserControl>
Так же есть небольшой класс SuperTab (его-то и будем в закладки запихивать):
public class SuperTab
{
public string Header { get; set; }
public string SomeContent { get; set; }
}
И еще есть простенький ViewModel:
public class ViewModel
{
public ViewModel()
{
if (!DesignerProperties.IsInDesignTool)
{
this.Collection = new ObservableCollection<SuperTab>
{
new SuperTab { Header = "Закладка 1", SomeContent = "Содержание закладки 1" },
new SuperTab { Header = "Закладка 2", SomeContent = "Содержание закладки 2" },
new SuperTab { Header = "Закладка 3", SomeContent = "Содержание закладки 3" }
};
}
}
public ObservableCollection<SuperTab> Collection { get; set; }
}
И, собственно говоря, звезда номера класс TabControlHelper, который и сделает всю "грязную работу":
public class TabControlHelper
{
#region ItemsCollection
/// <summary>
/// свойство ItemsCollection
/// </summary>
public static readonly DependencyProperty ItemsCollectionProperty =
DependencyProperty.RegisterAttached("ItemsCollection", typeof(IEnumerable), typeof(TabControl),
new PropertyMetadata(null, new PropertyChangedCallback(OnItemsCollectionChanged)));
/// <summary>
/// Геттер свойства ItemsCollection.
/// </summary>
public static IEnumerable GetItemsCollection(DependencyObject d)
{
return (IEnumerable)d.GetValue(ItemsCollectionProperty);
}
/// <summary>
/// Сеттер свойства ItemsCollection.
/// </summary>
public static void SetItemsCollection(DependencyObject d, IEnumerable value)
{
d.SetValue(ItemsCollectionProperty, value);
}
/// <summary>
/// Обработка изменений свойства ItemsCollection.
/// </summary>
private static void OnItemsCollectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TabControl inst = (TabControl)d;
if (d != null)
{
IEnumerable<SuperTab> items = (IEnumerable<SuperTab>)e.NewValue;
foreach (var item in items)
{
inst.Items.Add(new TabItem() { Header = item.Header, Content = item.SomeContent });
}
}
}
#endregion
Это свойство делает привязку с обработкой полученных элементов, в данном случаи, SuperTab. Можно конечно возразить, что мол данный код не является универсальным, но с другой стороны, его простота по сравнению с затраченным временем на реализацию универсального кода вообще ни в какое сравнение не идет. Тем более если это свойство создавать при помощи Snippet-шаблона.
Не могу не упомянуть и другие варианты решения данной проблемы. Вот пример решения через конвертеры.
Комментарии к статье (2)
гавно