WPF/Silverlight有个叫做模板的东西,可以为控件创建自定义的样式。
比如,有一组RadioButton(俗称“单选框”),在WrapPanel中自由排列,如下图的样子:
打算山寨一下淘宝网的界面,做成这样:
开始折腾···
自定义的用户控件,也就是图中“数据生成时间”以及下方的RadioButton。
取名FilterBox,布局很简单,Grid上方是一个标题,下方WrapPanel:
<UserControl>
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="35*"/>
<RowDefinition Height="91*"/>
</Grid.RowDefinitions>
<toolkit:WrapPanel Name="wrap" Grid.Row="1" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</toolkit:WrapPanel>
<TextBlock Name="title" Margin="0" Height="Auto" Text="标题" HorizontalAlignment="Center" VerticalAlignment="Center">
</TextBlock>
</Grid>
</UserControl>
WrapPanel能够让他的Children横向排列,一行排满之后,会自动换行,用在这里是在适合不过了。
选中的RadioButton样式可以看成一个粗边框+右下角的一个三角形;未选中的则用一个细的边框表示。
在UserControl下面增加Resources属性:
<Control.Resources>
<!--控件模板,Key为必需,并且设定目标类型为RadioButton-->
<ControlTemplate x:Key="Checked" TargetType="RadioButton">
<Grid>
<!--选中状态:粗边框-->
<Border Margin="1" BorderBrush="Blue" BorderThickness="2" Padding="4,1,4,0">
<!--内部的文字内容绑定了RadioButton的Content属性-->
<TextBlock Text="{TemplateBinding ContentControl.Content}"/>
</Border>
<!--三角形用Polygon表示-->
<Polygon Margin="1" Points="10,1 10,10 0,10" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Polygon.Fill>
<SolidColorBrush Color="Blue"/>
</Polygon.Fill>
</Polygon>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="UnChecked" TargetType="RadioButton">
<Grid>
<!--未选中状态:细边框-->
<Border Margin="2" BorderBrush="Gray" BorderThickness="1" Padding="4,1,4,0">
<TextBlock Text="{TemplateBinding ContentControl.Content}"/>
</Border>
</Grid>
</ControlTemplate>
</Control.Resources>
在WrapPanel中添加两个RadioButton,分别设定两种不同样式,转到设计界面查看效果:
<toolkit:WrapPanel Name="wrap" Grid.Row="1" HorizontalAlignment="Stretch" Margin="0" VerticalAlignment="Stretch">
<RadioButton Content="Checked" Template="{StaticResource Checked}"/>
<RadioButton Content="UnChecked" Template="{StaticResource UnChecked}"/>
</toolkit:WrapPanel>
在实际使用中,需要动态添加RadioButton。
在WPF中,可以使用Setter样式,对控件某个属性的不同状态设定不同的样式(即选择不同的模板);而在Silverlight中,Setter是不被支持的,所以只能编写C#代码实现:
publicpartialclass FilterBox : UserControl
{
publicstring Title { get { return title.Text; } }
publicstring SelectedValue { get; privateset; }
publicevent RoutedEventHandler CheckChanged;
public FilterBox()
{
InitializeComponent();
}
public FilterBox(string name, IEnumerable<string> values)
{
InitializeComponent();
this.title.Text = name;
// 添加“全部”选择项
var c = CreateItem("全部");
c.IsChecked =true;
this.wrap.Children.Add(c);
this.SelectedValue = c.Content asstring;
// 添加一般选择项
foreach (var i in values)
this.wrap.Children.Add(CreateItem(i));
}
privatevoid WhenCheckChanged(object sender, RoutedEventArgs e)
{
var c = sender as RadioButton;
var isChecked = c.IsChecked ??false;
// 依据IsChecked属性值选择模板
c.Template = Resources[isChecked ?"Checked" : "UnChecked"] as ControlTemplate;
if (isChecked)
{
this.SelectedValue = c.Content asstring;
// 触发CheckChanged事件,以便外部代码访问
if (CheckChanged !=null)
CheckChanged(this, null);
}
}
// 创建新的单选项
private RadioButton CreateItem(string item)
{
var radio =new RadioButton();
radio.Content = item;
// 设定缺省的UnChecked模板
radio.Template =this.Resources["UnChecked"] as ControlTemplate;
// 订阅事件
radio.Checked += WhenCheckChanged;
radio.Unchecked += WhenCheckChanged;
return radio;
}
}
接着创建一个ChildWindow用户控件,取名ChildWindowX。在Loaded事件响应方法中创建FilterBox:
privatevoid ChildWindow_Loaded(object sender, RoutedEventArgs e)
{
LayoutRoot.Children.Add(new FilterBox("数据生成时间",
newstring[] { "更早", "2006", "2007", "2008", "2009", "2010", "2011" }));
}
运行,看下效果: