使用mvvm动态地在选项卡项中包含关闭按钮

Ani*_*317 3 c# wpf tabcontrol mvvm

我在MainWindow中有一个tabcontrol.tabcontrol的默认/第一个选项卡是主用户控件.在主页中,我有一个可以添加更多选项卡的按钮.

MainWindow.xaml:

<Window.Resources>
    <DataTemplate x:Key="ClosableTabItemTemplate"> 
        <Button Content="X" Cursor="Hand" DockPanel.Dock="Right" Focusable="False"
                FontFamily="Courier" FontSize="9" FontWeight="Bold"  Margin="0,1,0,0" Padding="0" 
                VerticalContentAlignment="Bottom" Width="16" Height="16"/>            
    </DataTemplate>
</Window.Resources>

<Grid>
    <TabControl Name="tabMain" ItemsSource="{Binding TabItems,UpdateSourceTrigger=PropertyChanged}" />   
</Grid>
Run Code Online (Sandbox Code Playgroud)

在我的视图模型中,我有添加功能,其中添加了新选项卡.我需要所有这些新添加的标签的关闭按钮.

public MainViewModel()
    {
        try
        {
            Home Item2 = new Home();                           
            TabItems.Add(new TabItem() { Header = "Home", Content = Item2 });                             
        }
        catch(Exception ex)
        {
            MessageBox.Show("Exception "+ex);
        }

//Function to add new tabs.
public void AddNewTabs()
{
            ChildWindow childContent = new ChildWindow();
            TabItem item = new TabItem() { Header = "New Tab", Content = childContent};                
            item.MouseDoubleClick += new MouseButtonEventHandler(tab_MouseDoubleClick);
            TabItems.Add(item);
}
Run Code Online (Sandbox Code Playgroud)

现在正在添加新选项卡,但没有关闭按钮.我试过给

item.HeaderTemplate = FindResource("ClosableTabItemTemplate") as DataTemplate; 
Run Code Online (Sandbox Code Playgroud)

但它显示错误.

任何帮助,将不胜感激.

提前致谢.

Jam*_*ock 9

你可以看看 Dragablz,它可以做到这一点以及更多.

在此输入图像描述

免责声明:这是我的图书馆,但它是开源的,所以尽情享受.


Sam*_*Dev 6

您查看模型不得直接与视图交互以尊重Mvvm模式,这意味着您需要使用命令而不是事件,不要在View模型逻辑中使用任何与视图相关的控件.

这里有一种更清洁的方式来实现您的目标:

首先在视图中使用TabControl ContentTemplateItemTemplate而不是:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <Button Content="Add new tab" Command="{Binding AddNewTabCommand}"></Button>
    <TabControl  Grid.Row="1" Name="TabMain" ItemsSource="{Binding TabItems,UpdateSourceTrigger=PropertyChanged}" >
        <TabControl.ItemTemplate>
            <DataTemplate >
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Header}"/>
                    <Button Content="X" Cursor="Hand" DockPanel.Dock="Right" Focusable="False"
            FontFamily="Courier" FontSize="9" FontWeight="Bold"  Margin="0,1,0,0" Padding="0" 
            VerticalContentAlignment="Bottom" Width="16" Height="16" Command="{Binding DataContext.CloseTabCommand,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding ElementName=TabMain,Path=SelectedItem}"/>
                </StackPanel>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Content}" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Grid>
Run Code Online (Sandbox Code Playgroud)

,在ViewModel中创建一个TabItem类,它将包含一个选项卡Content和header(根据需要自定义),INotifyPropertyChanged如果该类反映了对视图的任何更改,您可能希望实现该接口,

第三,定义从TabItems添加和删除TabItem的命令ObservableCollection,

这里的viewModel代码:

  public class TabItem
{
    public String Header { get; set; }
    public String Content { get; set; }
}
public class MainViewModel : INotifyPropertyChanged
{
    private ObservableCollection<TabItem> _tabItems;
    public ObservableCollection<TabItem> TabItems
    {
        get
        {
            return _tabItems;
        }

        set
        {
            if (_tabItems == value)
            {
                return;
            }

            _tabItems = value;
            OnPropertyChanged();
        }
    }
    private RelayCommand _addNewTabCommand;
    public RelayCommand AddNewTabCommand
    {
        get
        {
            return _addNewTabCommand
                ?? (_addNewTabCommand = new RelayCommand(
                () =>
                {
                    TabItems.Add(new TabItem()
                    {
                        Header = "NewTab",
                        Content = "NewContent"
                    });
                }));
        }
    }
    private RelayCommand<TabItem> _closeTabCommand;
    public RelayCommand<TabItem> CloseTabCommand
    {
        get
        {
            return _closeTabCommand
                ?? (_closeTabCommand = new RelayCommand<TabItem>(
                (t) =>
                {
                    TabItems.Remove(t);
                }));
        }
    }

    public MainViewModel()
    {
        TabItems = new ObservableCollection<TabItem>()
       {
            new TabItem()
           {
               Header = "Home",
               Content = "Home Content"
           },
           new TabItem()
           {
               Header = "Header1",
               Content = "Content1"
           }
       };

    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出 :

在此输入图像描述

Ps:我的MainWindow视图DataContext设置为MainWindowViewModel,这就是我AncestorType用来查找的原因CloseTabCommand