繁体   English   中英

具有可关闭TabItem标头的TabControl

[英]TabControl with Closable TabItem Header

我正在尝试创建带有按钮的TabItem标头,以使用户能够关闭标签。 视觉表示和对象的数据绑定都很好。

我已经尝试过DataContext,但是到目前为止,我还没有找到可行的解决方案。

我的XAML:

<TabControl     
                    Grid.Column="3" 
                    Grid.Row="2"
                    x:Name="TabControlTargets" 
                    ItemsSource="{Binding Path=ViewModelTarget.IpcConfig.DatabasesList, UpdateSourceTrigger=PropertyChanged}"
                    SelectedItem="{Binding Path=ViewModelTarget.SelectedTab, UpdateSourceTrigger=PropertyChanged}">
                        <TabControl.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                                    <TextBlock FontFamily="Calibri" FontSize="15" FontWeight="Bold" Foreground="{Binding FontColor}" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" Margin="0,0,20,0"/>
                                    <Button HorizontalAlignment="Left" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}" Command="{Binding Path = ViewModelTarget.buttonRemoveDatabaseCommand}" 
                                            CommandParameter="**?**"
                                        >
                                        <Button.Content>
                                            <Image Height="15" Width="15" Source="pack://application:,,,/Images/cancel.png" />
                                        </Button.Content>
                                    </Button>
                                </StackPanel>
                            </DataTemplate>

我在弄清楚如何设置按钮的CommandParameter时遇到了麻烦,以便它指向正确的对象。

这是我的RelayCommand:

    public ICommand buttonRemoveDatabaseCommand
    {
        get
        {
            if (_buttonRemoveDatabaseCommand == null)
            {
                _buttonRemoveDatabaseCommand = new RelayCommand(
                    param => RemoveDatabase(param)
                    );
            }
            return _buttonRemoveDatabaseCommand;
        }
    }

这是我的RemoveDatabase函数:

public void RemoveDatabase(object dB)
    {
        this.IpcConfig.RemoveDataBase((PCDatabase)dB);
    }

我强烈希望解决方案坚持我的“无代码落后”方法。

如注释中所指出的,您可以使用CommandParameter="{Binding}"TabItem上下文传递给命令。

更好的方法是将命令移至TabItem的ViewModel。

这里是使用Prism和Prism的EventAggregator的示例实现。 当然,您可以使用其他所有MVVM框架来实现它,甚至可以自己实现,但这取决于您。

这将是您的TabControl ViewModel,其中包含所有数据库或其表示的任何列表。

public class DatabasesViewModel : BindableBase
{
    private readonly IEventAggregator eventAggregator;

    public ObservableCollection<DatabaseViewModel> Databases { get; private set; }
    public CompositeCommand CloseAllCommand { get; }

    public DatabasesViewModel(IEventAggregator eventAggregator)
    {
        if (eventAggregator == null)
            throw new ArgumentNullException(nameof(eventAggregator));

        this.eventAggregator = eventAggregator;

        // Composite Command to close all tabs at once
        CloseAllCommand = new CompositeCommand();
        Databases = new ObservableCollection<DatabaseViewModel>();

        // Add a sample object to the collection
        AddDatabase(new PcDatabase());

        // Register to the CloseDatabaseEvent, which will be fired from the child ViewModels on close
        this.eventAggregator
            .GetEvent<CloseDatabaseEvent>()
            .Subscribe(OnDatabaseClose);
    }

    private void AddDatabase(PcDatabase db)
    {
        // In reallity use the factory pattern to resolve the depencency of the ViewModel and assing the
        // database to it
        var viewModel = new DatabaseViewModel(eventAggregator)
        {
            Database = db
        };

        // Register to the close command of all TabItem ViewModels, so we can close then all with a single command
        CloseAllCommand.RegisterCommand(viewModel.CloseCommand);

        Databases.Add(viewModel);
    }

    // Called when the event is received
    private void OnDatabaseClose(DatabaseViewModel databaseViewModel)
    {
        Databases.Remove(databaseViewModel);
    }
}

每个选项卡将获得一个DatabaseViewModel作为其上下文。 这是定义close命令的地方。

public class DatabaseViewModel : BindableBase
{
    private readonly IEventAggregator eventAggregator;

    public DatabaseViewModel(IEventAggregator eventAggregator)
    {
        if (eventAggregator == null)
            throw new ArgumentNullException(nameof(eventAggregator));

        this.eventAggregator = eventAggregator;
        CloseCommand = new DelegateCommand(Close);
    }

    public PcDatabase Database { get; set; }

    public ICommand CloseCommand { get; }
    private void Close()
    {
        // Send a refence to ourself
        eventAggregator
            .GetEvent<CloseDatabaseEvent>()
            .Publish(this);
    }
}

当您单击TabItem上的关闭按钮时,将调用CloseCommand并发送一个事件,该事件将通知所有订阅者该选项卡应关闭。 在上面的示例中, DatabasesViewModel侦听此事件并将其接收,然后可以将其从ObservableCollection<DatabaseViewModel>集合中删除。

为了使这种方式的优势更加明显,我添加了一个CloseAllCommand ,它是一个CompositeCommand ,它在添加到Databases CloseCommand中时注册到每个DatabaseViewModelCloseCommand ,调用时将调用所有已注册的命令。

CloseDatabaseEvent是一个非常简单的标记,它确定接收到的有效负载的类型,在本例中为DatabaseViewModel

public class CloseDatabaseEvent : PubSubEvent<DatabaseViewModel> { }

在实际的应用程序中,您要避免使用ViewModel(在此为DatabaseViewModel )作为有效负载,因为这会导致紧密耦合,因此应避免使用事件聚合器模式。

在这种情况下,它可以被认为是可接受的,因为DatabasesViewModel需要了解DatabaseViewModel S,但如果可能的话,最好使用一个ID(GUID,整型,字符串)。

这样做的好处是,您还可以通过其他方式(例如,菜单,功能区或上下文菜单)关闭选项卡,而您可能没有对DatabasesViewModel数据上下文的引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM