2011-01-28 2 views
4

Я хотел бы расширить элемент управления вкладкой, чтобы иметь закрывающиеся элементы табуляции.Silverlight 4: Создание закрываемых табитов

Я нашел это решение WPF Кента: On the WPF TabControl - can I add content next to the tab headers?

Я открыл копию существующего SilverLight TabControl в Blend. Однако структура выглядит совсем не так, как в WPF tabcontrol. Я не могу понять это в шаблоне управления Silverlight.

Кто-нибудь знает хороший ресурс для меня?

ответ

4

Вы можете Template TabItem иметь какую-то кнопку закрытия, которую вы можете подключить по коду, чтобы закрыть текущую выбранную вкладку.

<Style TargetType="TabItem"> 
      <Setter.Value> 
       <ControlTemplate TargetType="sdk:TabItem"> 
          <Button x:Name="PART_btnClose" 
              Height="15" 
              Width="15" 
              Grid.Column="1" 
              HorizontalAlignment="Right" 
              VerticalAlignment="Center" 
              Margin="20,0,3,8" BorderThickness="1" Cursor="Hand" /> 
</ControlTemplate> 
</Setter.Value> 
</Style> 

После этого на шаблоне «Применить» вы можете подписаться на событие ButtonClicked.

Что-то вроде этого:

public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 

     PART_btnClose = GetTemplateChild("PART_btnClose") as Button; 

     if (PART_btnClose != null) 
     { 
      PART_btnClose.Click += new RoutedEventHandler(PART_btnClose_Click); 
     } 

В этом случае, вы можете закрыть вкладку.

Надеюсь, это поможет, код может не работать как есть, просто сделал это быстро.

Ty Rozak

+0

спасибо за ваш ответ. Не могли бы вы подробнее рассказать о том, как это должно работать? Если я создам класс TabItemExt, расширяющий TabItem, я могу переопределить OnApplyTemplate и сделать то, что вы сказали. Но мне также нужен TabControlExt, расширяющий TabControl, который будет использовать этот новый TabItemExt вместо обычного TabItem внутри. Как мне это сделать?Many Thanks – Houman

+0

Если вы не добавляете поведение в TabControl, вам не нужно настраивать CustomTabControl, вы можете просто добавить «CustomTabItem» в Xaml или Code, когда вы обычно добавляете обычный TabItem, это потому, что ваш CustomTabItem должен быть получен из TabItem , только с добавленным поведением. –

5

У меня была такая же проблема раньше, и тогда я решил использовать расширенный TabControl. Я не знаю, где я его нашел, но это не имеет значения, теперь это в моем проекте.

С этим TabControl Я могу добавить или удалить элементы из коллекции ViewModel, и мои изменения будут отражены в пользовательском интерфейсе.

MyTabControl.cs

public class MyTabControl : TabControl 
{ 
    public MyTabControl() 
     : base() 
    { 
     this.SelectionChanged += OnSelectionChanged; 
    } 

    #region Tabs with databinding and templates 
    /// <summary> 
    /// Template for a TabItem header 
    /// </summary> 
    public DataTemplate TabHeaderItemTemplate 
    { 
     get { return (DataTemplate)GetValue(TabHeaderItemTemplateProperty); } 
     set { SetValue(TabHeaderItemTemplateProperty, value); } 
    } 
    public static readonly DependencyProperty TabHeaderItemTemplateProperty = 
     DependencyProperty.Register("TabHeaderItemTemplate", typeof(DataTemplate), typeof(MyTabControl), new PropertyMetadata(
      (sender, e) => 
      { 
       ((MyTabControl)sender).InitTabs(); 
      })); 

    /// <summary> 
    /// Template for a content 
    /// </summary> 
    public DataTemplate TabItemTemplate 
    { 
     get { return (DataTemplate)GetValue(TabItemTemplateProperty); } 
     set { SetValue(TabItemTemplateProperty, value); } 
    } 
    public static readonly DependencyProperty TabItemTemplateProperty = 
     DependencyProperty.Register("TabItemTemplate", typeof(DataTemplate), typeof(MyTabControl), new PropertyMetadata(
      (sender, e) => 
      { 
       ((MyTabControl)sender).InitTabs(); 
      })); 

    /// <summary> 
    /// Source of clr-objects 
    /// </summary> 
    public IEnumerable MyItemsSource 
    { 
     get 
     { 
      return (IEnumerable)GetValue(MyItemsSourceProperty); 
     } 
     set 
     { 
      SetValue(MyItemsSourceProperty, value); 
     } 
    } 

    public static readonly DependencyProperty MyItemsSourceProperty = 
     DependencyProperty.Register("MyItemsSource", typeof(IEnumerable), typeof(MyTabControl), new PropertyMetadata(
      (sender, e) => 
      { 
       MyTabControl control = (MyTabControl)sender; 
       INotifyCollectionChanged incc = e.OldValue as INotifyCollectionChanged; 
       if (incc != null) 
       { 
        incc.CollectionChanged -= control.MyItemsSourceCollectionChanged; 
       } 
       control.InitTabs(); 

       incc = e.NewValue as INotifyCollectionChanged; 
       if (incc != null) 
       { 
        incc.CollectionChanged += control.MyItemsSourceCollectionChanged; 
       } 
      })); 


    /// <summary> 
    /// Selected item as object 
    /// </summary> 
    public object MySelectedItem 
    { 
     get { return (object)GetValue(MySelectedItemProperty); } 
     set { SetValue(MySelectedItemProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for MySelectedItem. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty MySelectedItemProperty = 
     DependencyProperty.Register("MySelectedItem", typeof(object), typeof(MyTabControl), new PropertyMetadata(
      (sender, e) => 
      { 
       MyTabControl control = (MyTabControl)sender; 

       if (e.NewValue == null) 
        control.SelectedItem = null; 
       else 
       { 
        var tab = control.Items.Cast<TabItem>().FirstOrDefault(ti => ti.DataContext == e.NewValue); 
        if (tab != null && control.SelectedItem != tab) 
         control.SelectedItem = tab; 
       } 
      })); 

    private void InitTabs() 
    { 
     Items.Clear(); 
     if (MyItemsSource != null && MyItemsSource.OfType<object>().Any()) 
     { 
      int i = 0; 
      foreach (var item in MyItemsSource) 
      { 
       var newitem = new TabItem(); 

       if (TabItemTemplate != null) 
        newitem.Content = TabItemTemplate.LoadContent(); 

       if (TabHeaderItemTemplate != null) 
        newitem.Header = TabHeaderItemTemplate.LoadContent(); 

       newitem.DataContext = item; 
       Items.Add(newitem); 
      } 
      VisualStateManager.GoToState(this, "Normal", true); 
     } 
     else VisualStateManager.GoToState(this, "NoTabs", true); 
    } 

    private void MyItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (e.Action == NotifyCollectionChangedAction.Add) 
     { 
      if (e.NewStartingIndex > -1) 
      { 
       foreach (var item in e.NewItems) 
       { 
        var newitem = new TabItem(); 

        if (TabItemTemplate != null) 
         newitem.Content = TabItemTemplate.LoadContent(); 

        if (TabHeaderItemTemplate != null) 
         newitem.Header = TabHeaderItemTemplate.LoadContent(); 

        newitem.DataContext = item; 
        Items.Add(newitem); 
       } 
      } 
     } 
     else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) 
     { 
      if (e.OldStartingIndex > -1) 
      { 
       var ti = (TabItem)this.Items[e.OldStartingIndex]; 
       Items.RemoveAt(e.OldStartingIndex); 
      } 
     } 
     else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace) 
     { 
      Items.RemoveAt(e.OldStartingIndex); 

      var newitem = new TabItem(); 

      if (TabItemTemplate != null) 
       newitem.Content = TabItemTemplate.LoadContent(); 

      if (TabHeaderItemTemplate != null) 
       newitem.Header = TabHeaderItemTemplate.LoadContent(); 

      newitem.DataContext = e.NewItems[0]; 

      Items.Add(newitem); 
      Items.Insert(e.NewStartingIndex, newitem); 
     } 
     else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset) 
     { 
      InitTabs(); 
     } 
    } 

    #endregion 

    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     var si = e.AddedItems.Cast<TabItem>().FirstOrDefault(); 
     if (si != null) 
      this.MySelectedItem = si.DataContext; 
     else this.MySelectedItem = null; 
    } 
} 

MainPage.xaml

<my:MyTabControl MyItemsSource="{Binding Items}" MySelectedItem="{Binding SelectedITem}"> 
     <my:MyTabControl.TabHeaderItemTemplate> 
      <DataTemplate> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="*"/> 
         <ColumnDefinition Width="Auto"/> 
        </Grid.ColumnDefinitions> 
        <TextBlock Text="{Binding Title}" VerticalAlignment="Center"/> 
        <Button Content="X" Margin="3" Width="20" Height="20" Grid.Column="1" 
          Command="{Binding RequestCloseCommand}"/> 
       </Grid> 
      </DataTemplate> 
     </my:MyTabControl.TabHeaderItemTemplate> 
     <my:MyTabControl.TabItemTemplate> 
      <DataTemplate> 
       <ContentControl Content="{Binding Content}"/> 
      </DataTemplate> 
     </my:MyTabControl.TabItemTemplate> 
    </my:MyTabControl> 

Отметим, что свойства называются MyItemsSource и MySelectedItem, потому что это TabControl объектов использования, не TabItem.

И два ViewModels: MainViewModel.cs

public class MainViewModel 
{ 
    public MainViewModel() 
    { 
     this.Items = new ObservableCollection<TabItemViewModel> 
         { 
          new TabItemViewModel("Tab 1", OnItemRequestClose), 
          new TabItemViewModel("Tab item 2", OnItemRequestClose) 
         }; 
    } 

    public ObservableCollection<TabItemViewModel> Items { get; set; } 

    public void OnItemRequestClose(TabItemViewModel item) 
    { 
     this.Items.Remove(item); 
    } 
} 

TabItemViewModel.cs

public class TabItemViewModel 
{ 
    public TabItemViewModel(string title, Action<TabItemViewModel> onClose) 
    { 
     this.Title = title; 
     this.RequestCloseCommand = new DelegateCommand(_ => onClose(this)); 

     //Just a demontration 
     this.Content = "Test content "+title; 
    } 

    public string Title { get; set; } 

    public ICommand RequestCloseCommand { get; set; } 

    public object Content { get; set; }  
}