【Win10】让 AppBarButton 支持更复杂的 Icon 内容
最近有一个需求,需要制作这么一个 AppBarButton:
这个 AppBarButton 的 Icon 是一个评论框图标里面再显示评论数(大于 99 条则显示 99+)。其中评论数是通过数据绑定得来的。
要单独显示这个评论框图标或者单独显示评论数都不是难题,然而要同时显示这两者就成了一个大问题了,因为 AppBarButton 的 Icon 属性只能设置一个。再看看 Icon 属性的类型,是 IconElement 这个类。但是,这个类的构造函数是内部的,我们也没法继承它。那么,从 Icon 属性上入手是不可能了。
但是,要注意到,绝大部分的控件都是有模板的,然后可以修改的。对于 AppBarButton,是不是也这样呢?答案是肯定的。
在 MSDN 上,我们可以查阅到 AppBarButton 的默认样式和模板:https://msdn.microsoft.com/zh-cn/library/windows/apps/mt299105.aspx
<!-- Default style for Windows.UI.Xaml.Controls.AppBarButton --> <Style TargetType="AppBarButton"> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/> <Setter Property="FontWeight" Value="Normal"/> <Setter Property="Width" Value="68"/> <Setter Property="UseSystemFocusVisuals" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="AppBarButton"> <Grid x:Name="Root" MinWidth="{TemplateBinding MinWidth}" MaxWidth="{TemplateBinding MaxWidth}" Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ApplicationViewStates"> <VisualState x:Name="FullSize"/> <VisualState x:Name="Compact"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Overflow"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="OverflowWithToggleButtons"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel" Storyboard.TargetProperty="Margin"> <DiscreteObjectKeyFrame KeyTime="0" Value="38,11,0,13"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <Storyboard> <PointerUpThemeAnimation Storyboard.TargetName="OverflowTextLabel" /> </Storyboard> </VisualState> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <PointerUpThemeAnimation Storyboard.TargetName="OverflowTextLabel" /> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListMediumBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <PointerDownThemeAnimation Storyboard.TargetName="OverflowTextLabel" /> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <StackPanel x:Name="ContentRoot" MinHeight="{ThemeResource AppBarThemeCompactHeight}"> <ContentPresenter x:Name="Content" Height="20" Margin="0,14,0,4" Content="{TemplateBinding Icon}" Foreground="{TemplateBinding Foreground}" HorizontalAlignment="Stretch" AutomationProperties.AccessibilityView="Raw"/> <TextBlock x:Name="TextLabel" Text="{TemplateBinding Label}" Foreground="{TemplateBinding Foreground}" FontSize="12" FontFamily="{TemplateBinding FontFamily}" TextAlignment="Center" TextWrapping="Wrap" Margin="0,0,0,6"/> </StackPanel> <TextBlock x:Name="OverflowTextLabel" Text="{TemplateBinding Label}" Foreground="{TemplateBinding Foreground}" FontSize="15" FontFamily="{TemplateBinding FontFamily}" TextAlignment="Left" TextTrimming="Clip" TextWrapping="NoWrap" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="12,11,0,13" Visibility="Collapsed"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
仔细看一下这个模板,我们可以发现这么一句:
Content="{TemplateBinding Icon}"
我们尝试一下将 Icon 修改为 Content。(Content 属性是 AppBarButton 从 Button 类继承得来的,但是 AppBarButton 它自身并没有使用到这个属性。)
然后编写 XAML 测试下:
<CommandBar> <AppBarButton Style="{StaticResource MyAppBarButtonStyle}" Label="评论"> <AppBarButton.Content> <Grid> <FontIcon Glyph="" FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Text="99+" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="9" RenderTransformOrigin="0.5,0.5"> <TextBlock.RenderTransform> <TranslateTransform Y="-2.5" /> </TextBlock.RenderTransform> </TextBlock> </Grid> </AppBarButton.Content> </AppBarButton> </CommandBar>
运行后,你应该可以看到文章开头的效果了。
最后,当然是稍微封装下了,因为 AppBarButton 是能够继承的。由于没啥技术含量-_-|||,这里就大家自己弄了。