TabControl这个控件想必大家都不陌生,无论是WinForm、Asp.net还是WPF中,这个通用控件都是非常有用的。
笔者最近在学习Silverlight中也应用到这个控件,发觉它与WPF中的TabControl是不尽相同的——这个事情让我非常的不解,主要是因为这个控件并不会涉及到安全因素,没有理由和WPF不一样(或许是我的类库版本有问题,也请大家有兴趣帮我验证一下)。
WPF的TabControl有ItemTemplate与ContentTemplate两个属性,前者可以为TabItem设置Header的模板,后者可以为TabItem设置Content模板。然而,在Silverlight的TabControl中,只有ItemTemplate这个属性(还有一个Template属性,但是一旦设置,就无法显示Header了)。于是退而求其次的,我只好为TabItem设置模板,幸运的是TabItem有HeaderTemplate与ContentTemplate两个属性,然而ContentTemplate并不Work,原因不明。。。
1<UserControl xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Extended" x:Class="SilverlightApplication1.Page"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Width="800" Height="600">
5 <Grid x:Name="LayoutRoot" Background="White">
6 <Grid.RowDefinitions>
7 <RowDefinition Height="30"/>
8 <RowDefinition Height="*"/>
9 </Grid.RowDefinitions>
10 <Button x:Name="myButton" Content="Click" Click="myButton_Click"/>
11 <my:TabControl x:Name="tab" Grid.Row="1">
12 <my:TabControl.Resources>
13 <Style x:Key="style" TargetType="my:TabItem">
14 <!--Template for the headers (buttons)-->
15 <Setter Property="HeaderTemplate">
16 <Setter.Value>
17 <DataTemplate>
18 <Grid Margin="3">
19 <Grid.ColumnDefinitions>
20 <ColumnDefinition Width="*" />
21 <ColumnDefinition Width="Auto" />
22 </Grid.ColumnDefinitions>
23 <TextBlock Text="{Binding HeaderText}"
24 Grid.Column="0"
25 FontSize="11"
26 Margin="5 0 5 0"
27 VerticalAlignment="Center" />
28 <Button Content="x" Click="Button_Click"
29 FontSize="11"
30 VerticalAlignment="Center"
31 Grid.Column="1"
32 Width="16"
33 Height="16" Tag="{Binding}"/>
34 </Grid>
35 </DataTemplate>
36 </Setter.Value>
37 </Setter>
38 <!--Template for the content, however, it doesn't work-->
39 <Setter Property="ContentTemplate">
40 <Setter.Value>
41 <DataTemplate>
42 <Grid>
43 <TextBlock Text="{Binding ContentText}"/>
44 </Grid>
45 </DataTemplate>
46 </Setter.Value>
47 </Setter>
48 </Style>
49 </my:TabControl.Resources>
50 </my:TabControl>
51 </Grid>
52</UserControl>
53
后台代码:
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Net;
5using System.Windows;
6using System.Windows.Controls;
7using System.Windows.Documents;
8using System.Windows.Input;
9using System.Windows.Media;
10using System.Windows.Media.Animation;
11using System.Windows.Shapes;
12using System.Collections.ObjectModel;
13
14namespace SilverlightApplication1
15{
16 public partial class Page : UserControl
17 {
18 public Page()
19 {
20 InitializeComponent();
21 this.tab.ItemsSource = source;
22 }
23
24 private ObservableCollection<TabItem> source = new ObservableCollection<TabItem>();
25 /**//*
26 * 这是一种比较强悍的手段,在内部直接维护了一个Dicionary
27 * 当然也可以通过LINQ来查询source来找到相应的TabItem
28 * 这样做的目的是为了在点击Header的"x"时,可以关掉相应的Tab
29 */
30 private Dictionary<Data, TabItem> hash = new Dictionary<Data, TabItem>();
31 private void myButton_Click(object sender, RoutedEventArgs e)
32 {
33 TabItem tabItem = new TabItem();
34 Data data = new Data()
35 {
36 HeaderText = "myHeader",
37 ContentText = "abcd"
38 };
39 tabItem.Style = (Style)(this.tab.Resources["style"]);
40 tabItem.DataContext = data;
41 source.Add(tabItem);
42 hash[data] = tabItem;
43 tab.SelectedItem = tabItem;
44 }
45
46 private void Button_Click(object sender, RoutedEventArgs e)
47 {
48 var btn = sender as Button;
49 if (btn != null)
50 {
51 var data = btn.Tag as Data;
52 source.Remove(hash[data]);
53 }
54 }
55
56 public class Data
57 {
58 public string HeaderText { get; set; }
59 public string ContentText { get; set; }
60 }
61 }
62}
63
原本希望在Style中为TabItem设置它的HeaderTemplate与ContentTemplate,这样就可以在绑定DataContext的时候,对相应的控件属性进行绑定。然而运行的时候,可以发现ContentTemplate已经被赋值,但是却不会显示出来那个TextBlock。
至于上面这段代码最让我满意的,就是我为了TabItem的Header加入了一个Button,点击它的话,就可以将这个TabItem从TabControl中移除掉。
由于使用ContentTemplate的想法失败了,于是我只好动用TabItem的Content属性,代码做一下修改:
1 private void myButton_Click(object sender, RoutedEventArgs e)
2 {
3 TabItem tabItem = new TabItem();
4 Data data = new Data()
5 {
6 HeaderText = "myHeader",
7 ContentText = "abcd"
8 };
9 tabItem.Style = (Style)(this.tab.Resources["style"]);
10 tabItem.DataContext = data;
11 Button button = new Button();
12 button.Content= data.ContentText;
13 tabItem.Content = button;
14 source.Add(tabItem);
15 hash[data] = tabItem;
16 tab.SelectedItem = tabItem;
17 }
这样就向TabItem的Content中加入了一人Button,Button的内容显示abcd。但是这样做有一个非常大的局限性,tabItem.Content属性虽然接受object类型,但是如果设置的不是ContentControl的子类,就会自动调用它的ToString()显示Content。于是这里有了一个权宜之计,就是加入一个Panel(如Canvas),这样向Pannel中就可以加入任何你想要的Control了。但是,我要说的是“但是”,这种方法无疑要求我们手写大量的cs代码而不是xaml代码,我认为这种方法是非常不好的。
我不知道我在设置TabItem的ContentTemplate的时候出了什么错,或者可以直接从TabControl的ItemTemplate(Template、ItemPanel等属性)中设置即可达到相应的效果,如果哪位做过类似的尝试或者有相关的资料可以告诉我,不胜感激。最后截一张运行时的效果图吧: