TabControl是平常用的比较多的布局控件,虽然它是继承自ItemsControl,但是它的ItemsSource实在是很不好用,谁用谁知道。
prism里为TabControl提供了TabControlRegionAdapter和TabControlRegionSyncBehavior,参考其部分实现方式让我们也来实现个TabControl的扩展来更好的MVVM。
看代码:
public
class
TabControlExtensions
{
public
static
IEnumerable GetItemsSource(DependencyObject d)
{
return
(IEnumerable)d.GetValue(ItemsSourceProperty);
}
public
static
void
SetItemsSource(DependencyObject d, IEnumerable value)
{
d.SetValue(ItemsSourceProperty, value);
}
public
static
readonly
DependencyProperty ItemsSourceProperty
=
DependencyProperty.RegisterAttached(
"
ItemsSource
"
,
typeof
(IEnumerable),
typeof
(TabControl),
new
PropertyMetadata(OnItemsSourceChanged));
private
static
void
OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source
=
d
as
TabControl;
var items
=
e.NewValue
as
IEnumerable;
source.Items.Clear();
if
(items
!=
null
)
{
var headerTemplate
=
GetHeaderTemplate(source);
var contentTemplate
=
GetContentTempalte(source);
foreach
(var item
in
items)
{
var tabItem
=
new
TabItem
{
DataContext
=
item,
Header
=
item,
HeaderTemplate
=
headerTemplate,
Content
=
item,
ContentTemplate
=
contentTemplate,
};
source.Items.Add(tabItem);
}
}
}
public
static
DataTemplate GetHeaderTemplate(DependencyObject d)
{
return
(DataTemplate)d.GetValue(HeaderTemplateProperty);
}
public
static
void
SetHeaderTemplate(DependencyObject d, DataTemplate value)
{
d.SetValue(HeaderTemplateProperty, value);
}
public
static
readonly
DependencyProperty HeaderTemplateProperty
=
DependencyProperty.RegisterAttached(
"
HeaderTemplate
"
,
typeof
(DataTemplate),
typeof
(TabControl),
new
PropertyMetadata(OnHeaderTemplateChanged));
private
static
void
OnHeaderTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source
=
d
as
TabControl;
var template
=
e.NewValue
as
DataTemplate;
foreach
(TabItem item
in
source.Items)
{
item.HeaderTemplate
=
template;
}
}
public
static
DataTemplate GetContentTempalte(DependencyObject d)
{
return
(DataTemplate)d.GetValue(ContentTempalteProperty);
}
public
static
void
SetContentTempalte(DependencyObject d, DataTemplate value)
{
d.SetValue(ContentTempalteProperty, value);
}
public
static
readonly
DependencyProperty ContentTempalteProperty
=
DependencyProperty.Register(
"
ContentTempalte
"
,
typeof
(DataTemplate),
typeof
(TabControl),
new
PropertyMetadata(OnContentTempalteChanged));
private
static
void
OnContentTempalteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source
=
d
as
TabControl;
var template
=
e.NewValue
as
DataTemplate;
foreach
(TabItem item
in
source.Items)
{
item.ContentTemplate
=
template;
}
}
}
扩展类TabControlExtensions提供了三个附加属性:
ItemsSource用于设置TabControl的数据源(摒弃自身的ItemsSource不用了)。
HeaderTemplate和ContentTempalte方便设置数据的显示模板(ItemsControl也有ItemTemplate属性,为了清晰,此处就单独拎出来了)。
在三个回调方法里的操作很简单,一看就明白。
来个Model类:
public
class
Animal
{
public
string
Name {
get
;
set
; }
public
int
Count {
get
;
set
; }
public
string
From {
get
;
set
; }
}
再来个简单的ViewModel类:
public
class
Zoo
{
public
IEnumerable
<
Animal
>
Animals {
get
;
private
set
; }
public
Zoo()
{
this
.Animals
=
new
List
<
Animal
>
{
new
Animal{Name
=
"
斑马
"
,Count
=
10
,From
=
"
塞伦盖提
"
},
new
Animal{Name
=
"
企鹅
"
,Count
=
23
,From
=
"
加拉帕戈斯
"
},
new
Animal{Name
=
"
环尾狐猴
"
,Count
=
8
,From
=
"
马达加斯加
"
},
};
}
}
看下Xaml里的具体设置和最后的效果:
<
UserControl
x:Class
="TabControlExtensions.MainPage"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk
="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:local
="clr-namespace:TabControlExtensions"
>
<
UserControl.DataContext
>
<
local:Zoo
/>
</
UserControl.DataContext
>
<
UserControl.Resources
>
<
DataTemplate
x:Key
="HeaderTempalte"
>
<
TextBlock
Text
="
{Binding Name}
"
/>
</
DataTemplate
>
<
DataTemplate
x:Key
="ContentTemplate"
>
<
StackPanel
>
<
TextBlock
Text
="
{Binding Count,StringFormat='数量:\{0\}'}
"
/>
<
TextBlock
Text
="
{Binding From,StringFormat='来自:\{0\}'}
"
/>
</
StackPanel
>
</
DataTemplate
>
</
UserControl.Resources
>
<
Grid
x:Name
="LayoutRoot"
Background
="White"
>
<
sdk:TabControl
Margin
="30"
local:TabControlExtensions.ItemsSource
="
{Binding Animals}
"
local:TabControlExtensions.HeaderTemplate
="
{StaticResource HeaderTempalte}
"
local:TabControlExtensions.ContentTempalte
="
{StaticResource ContentTemplate}
"
/>
</
Grid
>
</
UserControl
>
只要想的到,其实一切都很简单。
另外现在的方式只能在ItemsSource里的每个Item拥有相同或者类似的属性时才有最好的效果,下次继续扩展。