[英]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
中時注冊到每個DatabaseViewModel
的CloseCommand
,調用時將調用所有已注冊的命令。
CloseDatabaseEvent
是一個非常簡單的標記,它確定接收到的有效負載的類型,在本例中為DatabaseViewModel
。
public class CloseDatabaseEvent : PubSubEvent<DatabaseViewModel> { }
在實際的應用程序中,您要避免使用ViewModel(在此為DatabaseViewModel
)作為有效負載,因為這會導致緊密耦合,因此應避免使用事件聚合器模式。
在這種情況下,它可以被認為是可接受的,因為DatabasesViewModel
需要了解DatabaseViewModel
S,但如果可能的話,最好使用一個ID(GUID,整型,字符串)。
這樣做的好處是,您還可以通過其他方式(例如,菜單,功能區或上下文菜單)關閉選項卡,而您可能沒有對DatabasesViewModel
數據上下文的引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.