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