4

我想扩展选项卡控件以具有可关闭的选项卡项。

我找到了 Kent 的这个 WPF 解决方案: 在 WPF TabControl 上 - 我可以在选项卡标题旁边添加内容吗?

我在 Blend 中打开了现有 silverlight tabcontrol 的副本。但是,该结构看起来与 WPF 选项卡控件完全不同。我无法将它直接放入 Silverlight 控件模板。

有谁知道我的好资源?

4

2 回答 2

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

主页.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> 

请注意,属性被称为MyItemsSourceand MySelectedItem,因为它TabControl使用对象,而不是TabItem

和两个 ViewModel: 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; }      
}
于 2011-01-28T16:02:53.113 回答
4

您可以模板 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);
        }

在这种情况下,您可以关闭您的标签。

希望这会有所帮助,代码可能无法按原样工作,只是快速完成。

泰罗扎克

于 2011-01-28T15:53:21.223 回答