在WPF中动态添加/删除选项卡






4.95/5 (24投票s)
本文展示如何在WPF中实现 TabControl 的添加/删除选项卡功能。
引言
本文展示了如何在WPF(Windows Presentation Foundation)中创建动态选项卡,类似于你在大多数Web浏览器中看到的,在末尾有一个空选项卡用于添加新选项卡,并且每个选项卡上都有一个关闭按钮来关闭该选项卡。有几种技术可以实现这一点。这里,使用List
类型的 TabItem
来保存绑定到 TabControl
的选项卡。List
的最后一个项目是一个空选项卡,用于在选择它时向列表中添加新的 TabItem
。DataTemplate
用于选项卡标题,以便在每个选项卡中添加一个 **Delete** 按钮。
XAML
第一步是为 TabControl
定义 XAML。将 ItemsSource
属性设置为 {Binding}
,而 DataContext
将由代码隐藏中的 TabItem
的 List
提供。接下来添加 SelectionChanged
事件。
<TabControl Name="tabDynamic" ItemsSource="{Binding}"
SelectionChanged="tabDynamic_SelectionChanged">
</TabControl>
在 TabControl.Resources
标记下为选项卡标题定义 DataTemplate
,并将 DataType
设置为 TabItem
。使用任何容器添加和对齐 TextBlock
和删除 Button
。我使用了 DockPanel
,因为它很容易使用 DockPanel.Dock
属性将删除 Button
对齐到右侧。将 Click
事件添加到按钮,并将 CommandParameter
属性绑定到选项卡的 Name
。这将用于识别按钮的 Click
事件处理程序中要删除的选项卡。将 TextBlock
的 Text
属性绑定到 TabItem
的 Header
。Header
是一个 string
值,将在代码隐藏中设置。
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0"
Padding="0" Click="btnDelete_Click"
CommandParameter="{Binding RelativeSource=
{RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Source="/delete.gif" Height="11" Width="11"></Image>
</Button>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=
{x:Type TabItem}}, Path=Header}" />
</DockPanel>
</DataTemplate>
这是完整的 XAML,其中还包括添加到每个选项卡的 TextBox
控件的样式
<TabControl Name="tabDynamic" ItemsSource="{Binding}"
SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<Button Name="btnDelete" DockPanel.Dock="Right"
Margin="5,0,0,0" Padding="0" Click="btnDelete_Click"
CommandParameter="{Binding RelativeSource=
{RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Source="/delete.gif" Height="11" Width="11"></Image>
</Button>
<TextBlock Text="{Binding RelativeSource=
{RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
</DockPanel>
</DataTemplate>
<Style TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="AcceptsReturn" Value="True"></Setter>
<Setter Property="TextWrapping" Value="WrapWithOverflow"></Setter>
<Setter Property="MaxLines" Value="5000"></Setter>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Auto"></Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Auto"></Setter>
</Style>
</TabControl.Resources>
</TabControl>
现在我解释包含添加和删除 TabItems 逻辑的代码。
后台代码
在代码隐藏文件中,定义两个数据成员。一个是 _tabItems
,它是一个 TabItem
列表,用于保存选项卡,另一个是 _tabAdd
,它是一个指向最后一个 TabItem
的引用,用于动态添加新选项卡。
private List<TabItem> _tabItems;
private TabItem _tabAdd;
在构造函数中初始化以上数据成员,并将最后一个选项卡的标题设置为文本“**+**”或您喜欢的任何图像。使用函数 AddItemItem()
添加第一个 TabItem
,然后将 TabControl
绑定到 List
并选择第一个 TabItem
。
public MainWindow()
{
try
{
InitializeComponent();
// initialize tabItem array
_tabItems = new List<TabItem>();
// add a tabItem with + in header
TabItem tabAdd = new TabItem();
tabAdd.Header = "+";
_tabItems.Add(tabAdd);
// add first tab
this.AddTabItem();
// bind tab control
tabDynamic.DataContext = _tabItems;
tabDynamic.SelectedIndex = 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
在上面使用的函数 AddTabItem()
中,向列表 _tabItems
添加一个新的 TabItem
,并返回该 TabItem
的实例。将 HeaderTemplate
设置为我们上面定义的那个。给 TabItem
一个唯一的名称,例如“TabXX”,其中 XX 是一个计数器,使名称唯一。您也可以使用相同的唯一字符串作为 Header
文本。设置 Header
和 Name
属性非常重要,因为在上面 XAML 中定义的 DataTemplate
中,我们将 TextBlock.Text
和 Button.CommandParameter
绑定到这些属性。接下来,添加您想要在 TabItem
中拥有的控件,并将 TabItem
添加到列表 _tabItems
中。在这里,我添加了一个简单的 TextBox
控件。另请注意,新选项卡插入在用于添加新选项卡的最后一个选项卡之前。
private TabItem AddTabItem()
{
int count = _tabItems.Count;
// create new tab item
TabItem tab = new TabItem();
tab.Header = string.Format("Tab {0}", count);
tab.Name = string.Format("tab{0}", count);
tab.HeaderTemplate = tabDynamic.FindResource("TabHeader") as DataTemplate;
// add controls to tab item, this case I added just a text box
TextBox txt = new TextBox();
txt.Name = "txt";
tab.Content = txt;
// insert tab item right before the last (+) tab item
_tabItems.Insert(count - 1, tab);
return tab;
}
在 SelectionChanged
事件处理程序的实现中,检查选定的 TabItem
是否是最后一个,然后使用 AddTabItem()
函数添加一个新的 TabItem
,重新绑定 TabControl
并选择新添加的 TabItem
。对于其他选项卡,您可以为此事件添加您的代码(如果有)。
private void tabDynamic_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TabItem tab = tabDynamic.SelectedItem as TabItem;
if (tab != null && tab.Header != null)
{
if (tab.Header.Equals(_addTabHeader))
{
// clear tab control binding
tabDynamic.DataContext = null;
// add new tab
TabItem newTab = this.AddTabItem();
// bind tab control
tabDynamic.DataContext = _tabItems;
// select newly added tab item
tabDynamic.SelectedItem = newTab;
}
else
{
// your code here...
}
}
}
最后,实现添加到 DataTemplate
的删除按钮的 Click
事件处理程序。在这里,使用 CommandParameter
识别要删除的选项卡,后者又给出 TabItem.Name
。您可以使用 linq 在 TabControl.Items
集合中轻松找到此选项卡。验证此选项卡不是列表中剩下的唯一选项卡,然后在确认后,从列表 _tabItems
中删除该选项卡,并重新绑定 TabControl
。如果删除了一个活动选项卡,则在重新绑定后选择第一个选项卡,否则选择删除之前选择的选项卡。
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
string tabName = (sender as Button).CommandParameter.ToString();
var item = tabDynamic.Items.Cast<tabitem>().Where
(i => i.Name.Equals(tabName)).SingleOrDefault();
TabItem tab = item as TabItem;
if (tab != null)
{
if (_tabItems.Count < 3)
{
MessageBox.Show("Cannot remove last tab.");
}
else if (MessageBox.Show(string.Format
("Are you sure you want to remove the tab '{0}'?", tab.Header.ToString()),
"Remove Tab", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
// get selected tab
TabItem selectedTab = tabDynamic.SelectedItem as TabItem;
// clear tab control binding
tabDynamic.DataContext = null;
_tabItems.Remove(tab);
// bind tab control
tabDynamic.DataContext = _tabItems;
// select previously selected tab. if that is removed then select first tab
if (selectedTab == null || selectedTab.Equals(tab))
{
selectedTab = _tabItems[0];
}
tabDynamic.SelectedItem = selectedTab;
}
}
}
历史
- 2012年11月18日:初始版本