[置顶]【WP7-01】Remove Button default Margin / 删除Button默认的Margin
问题
前一段时间写一个布局控件,里面会放到一些Button,Textblock,TextBox等等控件,但是在Button和TextBox控件中发现了问题,发现他们的布局上下左右的“空格”比较大,很是纳闷,以前在Silverlight开发的时候是没有遇见过的。比如:
View Code
<Button Height="100"/>
<Button Height="100"/>
</StackPanel>
显示的效果如下:如果在silverlight上这么写,3个button应该是紧凑在一起的,而WP7却是前后左右都有一些空格。

查找原因
用Express Blend 打开此项目,选择其中一个button,右键点击选择Edit Template->Edit a Copy,在弹出框中随便起个名字,点击OK。
然后Blend会生成一个Button的Style:
View Code
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<Setter Property="Padding" Value="10,3,10,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneBackgroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
<ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
原因找到了,原来Button自带的Style有一个Border,并且这个Border里有一个默认的Margin。
大家可以去这个网址查看WP7所有默认的static resource值:http://msdn.microsoft.com/en-us/library/ff769552(v=VS.92).aspx
PhoneTouchTargetOverhang的值是Thickness(12,12,12,12);
也可以在程序中设置断点查看: var r = this.Resources["PhoneTouchTargetOverhang"];
解决方法:
1. 最简单的方法是把Style中Border的Margin的值删了,直接写个Margin = "0" 就OK了。
2. 不重写Style:通过上面的Style,我们看到Button的controlTemplate是一个Grid,Grid的children( index:0)是Border,所以只要我们找到这个Border,然后把border的Margin置为0就OK了。
这个结构叫做Visual Tree,说白了就是一个Button是有微软的人有N个控件组合在一起而形成的。而最上面xaml中的StackPanel-Button/Button/Button这个叫做Logical Tree。
我们要检索Visual Tree,所以会用到VisualTreeHelper这个静态类,对于我们来说,主要用到2个方法:
public static DependencyObject GetChild(DependencyObject reference, int childIndex); // 根据索引,获取child 控件,childIndex就是控件的children[index]
public static int GetChildrenCount(DependencyObject reference); // 获取reference控件的children数量
因为我们已经知道了Button的ControlTemplate结构,可以在Button的Loaded代码里直接这么写代码,如下,当然你也可以写一个通用的递归帮助方法来通过Name查找Border控件。
View Code
{
Button btn = sender as Button;
Grid grid = VisualTreeHelper.GetChild(btn, 0) as Grid;
Border border = VisualTreeHelper.GetChild(grid, 0) as Border;
border.Margin = new Thickness(0);
}

可以看到,现在3个Button紧密的凑合在一起了。当然,真正的使用还需要手动给分配Margin或者在定义Grid行列的时候自动带出来空隙。
PS:
1. this.Resources是一个字典ResourceDictionary类型,我当初试着操作这个字典,比如update或者先删除PhoneTouchTargetOverhang key再添加一个key,最后都不可以。看来微软是不让改系统自带的资源,如果有其他方法改动的话,请留言。
2. 对于TextBox思路也是一样的.
Demo 工程下载 (VS2010 7.1)
后话:大家都说违背了微软设计的初衷,我后来又想了想,我估计大家没有仔细看完,比如我上面那段话,大家肯定以为我是直接把3个紧挨着的Button拿来直接使用,其实不是这样的,我是要在一个Grid中统一布局,肯定会留出一些间隔/空隙。这样应该不会惹来那么多争议了把?
posted @ 2011-10-24 23:21 潇潇兮 阅读(931) 评论(8) 编辑


