TabControl将ItemsSource绑定到带有现有TabItem的列表
问题描述:
我认为我在寻找的是MVVM,但我看过的示例并没有帮助,因此基于我的问题的示例应该可以提供帮助。我已经有了TabControl中的内容,所以我一直无法使用TabControl.ItemsSource = CourtCases
,因为它会引发异常;TabControl将ItemsSource绑定到带有现有TabItem的列表
Items collection must be empty before using ItemsSource.
...和使用可能需要更多的工作,因为一些标签将不得不关闭。
我有一个TabControl,其中第一个选项卡包含一个DataGrid与人员列表,我希望每当在DataGrid中单击一个项目时,会创建一个包含该人员详细信息的新选项卡。我想将人物对象传递给TabItem“范围/类”,并显示该人的内容。我为人物详细信息TabItem创建了一个DataTemplate
,您可以在下面看到它;
<TabControl Name="AttorneysTabControl" Grid.Column="2" Grid.Row="0">
<TabControl.Resources>
<DataTemplate x:Key="AttorneyTabHeader">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Attorney.Names}" Margin="2,0,0,0" FontSize="16" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AttorneyTabContent">
<StackPanel>
<TextBlock Text="{Binding Attorney.Names}" />
<TextBlock Text="{Binding Attorney.Age}"/>
<ToolBar>
<Button ToolTip="">Delete</Button>
<Button ToolTip="">Edit</Button>
</ToolBar>
</StackPanel>
</DataTemplate>
</TabControl.Resources>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Attorneys" Margin="2,0,0,0" FontSize="16" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<TabItem.Content>
<Grid Background="#FFE5E5E5" Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
...
</Grid>
</TabItem.Content>
</TabItem>
<!-- This part here -->
<!-- I want this to repeat. I think I should use a UserControl for this since I want the content to have it's own class -->
<TabItem ContentTemplate="{StaticResource AttorneyTabContent}" HeaderTemplate="{StaticResource AttorneyTabHeader}" />
</TabControl>
答
这不是不可能的,除非我错过了关于您的请求的内容。我会说它听起来不像一个好的UX,但你比我更了解你的用户和你的用例。
我放在一起,我认为会做你想做的事情。你需要适应你的数据。我希望它有助于不管。请记住,这仅仅是概念代码的证明。
首先是MainWindow
的XAML。这里没什么特别的。它开始作为一个简单的TabControl
托管TabItem
与Listbox
。
<Window x:Class="WpfTabControl1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTabControl1"
mc:Ignorable="d"
Title="TabControl Sample" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="Items" Type="{x:Type local:Item}">
<local:Item Name="Item A" Value="1" />
<local:Item Name="Item B" Value="2" />
<local:Item Name="Item C" Value="3" />
</x:Array>
</Window.Resources>
<Grid>
<TabControl x:Name="ItemTabControl">
<TabItem Header="Items">
<ListBox x:Name="ItemListBox" ItemsSource="{StaticResource Items}"
SelectionChanged="ListBox_SelectionChanged" />
</TabItem>
</TabControl>
</Grid>
</Window>
背后的代码是大多数有趣的事情发生的地方。我在事件处理程序中根据项目的标签是否已经存在做了两件事之一 - 我创建标签或选择标签。
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ItemListBox.SelectedItem is Item item) {
// select the tab if one was already created for the item;
// otherwise, create a new tab for it
if (TabExists(item.Name, out TabItem tab)) {
ItemTabControl.SelectedItem = tab;
}
else {
var newItem = new ItemTabItem() {
Item = (Item)ItemListBox.SelectedItem
};
int newIndex = ItemTabControl.Items.Add(newItem);
ItemTabControl.SelectedIndex = newIndex;
}
}
}
bool TabExists(string name, out TabItem tab)
{
tab = (from object item in ItemTabControl.Items
let t = item as ItemTabItem
where t != null && t.Item.Name == name
select t).FirstOrDefault();
return (tab != null);
}
}
我ItemTabItem
(我知道对不对真棒名)负责显示Item
类的实例。这里是XAML后面的代码。
<TabItem x:Class="WpfTabControl1.ItemTabItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfTabControl1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Name" />
<TextBlock Grid.Row="1" Text="Value" />
<TextBox Grid.Column="1" Text="{Binding Name, Mode=TwoWay}" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Value, Mode=TwoWay}" />
</Grid>
</TabItem>
现在后面的代码。我们可以直接设置DataContext
,但该属性使它更清晰,并允许我设置标题(也许可以将数据绑定它)。
public partial class ItemTabItem : TabItem
{
private Item item;
public ItemTabItem() => InitializeComponent();
public Item Item
{
get => item;
set
{
item = value;
DataContext = value;
Header = item?.Name;
}
}
}
Item
类没什么特别的。
public class Item
{
public string Name { get; set; }
public int Value { get; set; }
public override string ToString() => Name;
}
查看主 - 细节绑定。这是一个[SAMPLE](https://code.msdn.microsoft.com/windowsdesktop/CSWPFMasterDetailBinding-c78566ae)。 – jsanalytics
你正试图以一种他们真正没有设计的方式来使用控件。在这种情况下,您应该将控件包装在UserControl中,然后公开您自己的ItemsSource DP,并在代码隐藏中手动管理选项卡。 – Will
是的,这就是我所做的。我意识到这是不可能的。 – LogicDev