Как в TabControl привязать закладки из ViewModel?
WPF, MVVM, Silverlight | создано: 01.02.2011 | опубликовано: 01.02.2011 | обновлено: 13.01.2024 | просмотров: 5557 | всего комментариев: 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)
гавно