Как в TabControl привязать закладки из ViewModel?

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

гавно

Интеренсное решение. А можно взглянуть на весь код?