2016-02-27 99 views
0

我想创建TabItem标头与使用户关闭标签的按钮。该对象的视觉表示和数据绑定就好了。TabControl与可关闭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); 
    } 

我强烈希望的是坚持我的做法 “背后没有代码” 的解决方案。

+0

你能展示你的viewmodel – Flaugzig

+0

那么'SelectedItem'呢?它有什么问题? –

+0

@Eugene Podksal使用SelectedItem是我的第一个方法。但是,这将始终关闭所选的选项卡(即活动选项卡),该选项卡不一定是应该关闭的选项卡。 – Fang

回答

1

正如评论中指出的那样,您可以使用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作为上下文。这是关闭命令的定义。

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观察到的集合,这将调用所有注册的命令,调用时注册到每个DatabaseViewModel小号CloseCommand

CloseDatabaseEvent是一个非常简单的,只是一个标记,确定有效载荷它接收,这是在这种情况下DatabaseViewModel的类型。

public class CloseDatabaseEvent : PubSubEvent<DatabaseViewModel> { } 

在实际应用中要避免使用视图模型(在这里DatabaseViewModel)作为有效载荷,因为这导致紧密耦合,该事件聚合模式是为了避免。

在这种情况下,它可以被认为是可以接受的,因为DatabasesViewModel需要知道DatabaseViewModel,但如果可能的话最好使用ID(Guid,int,string)。

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

+0

这肯定使我走上了正轨。万分感谢。 – Fang