Silverlight 4 : Making Closeable Tabitems

前端 未结 2 549
天命终不由人
天命终不由人 2020-12-11 07:26

I would like to extend the tab control to have closeable tab items.

I have found this WPF solution of Kent: On the WPF TabControl - can I add content next to the tab

相关标签:
2条回答
  • 2020-12-11 08:16

    I had the same problem before, and then I decided to use an extended TabControl. I don't know where I've found it, but it doesn't matter, now it is in my project.

    With this TabControl I can add or remove items from a collection of ViewModel and my changes will be reflected on user interface.

    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> 
    

    Notice, that the properties are called MyItemsSource and MySelectedItem, because this TabControl use objects, not TabItem.

    And two 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; }      
    }
    
    0 讨论(0)
  • 2020-12-11 08:21

    You can Template TabItem to have some sort of close button that you can hook up in code behind to close the currently selected tab.

    <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>
    

    After this, in on apply template you can subscribe to the ButtonClicked Event.

    Something like this:

    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);
            }
    

    In that event, you can close your tab.

    Hope This helps, code might not work as is, just did it quickly.

    Ty Rozak

    0 讨论(0)
提交回复
热议问题