问题
I've got a ribbon bar in my silverlight app and on one of the icons I would like for there to be a badge icon showing the number of items in the view that the icon activates.
Picture the Mail icon in OS X showing the number of unread messages or the notifications counter on an IOS app icon.
I don't know much about xaml styles, but it seems to me I could duplicate the default style for the ribbon bar button, then add to it with some sort of red circle, and a white text that took in its value from a new property on the ribbon bar button somehow so I would be able to bind to it.
Does anyone have an example of something like this I can start from?
Thanks Shawn for the answer. This is what I ended up doing:
In the xaml:
<telerikRibbonBar:RadRibbonRadioButton
Text="Expired Active Call Factors"
Size="Large"
LargeImage="/CallFactorDatabase.UI;component/Images/Ribbon/Large/ExpiredActiveView.png"
Command="{Binding ActivateViewCommand}"
CommandParameter="ExpiredActiveView">
<Grid>
<Grid.Resources>
<converters:BooleanToVisibilityConverter x:Key="visibleWhenTrueConverter" VisibilityWhenTrue="Visible" VisibilityWhenFalse="Collapsed" />
</Grid.Resources>
<Grid Width="27" Height="27" Visibility="{Binding ExpiredActiveCallFactors, Converter={StaticResource visibleWhenTrueConverter}}" Margin="50,-40,0,0">
<Ellipse Fill="Black" Width="27" Height="27"/>
<Ellipse Width="25" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Coral" Offset="0.0" />
<GradientStop Color="Red" Offset="1.0" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Viewbox Width="25" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center" >
<TextBlock Text="{Binding ExpiredActiveCallFactorsCount}" Foreground="White"/>
</Viewbox>
</Grid>
</Grid>
</telerikRibbonBar:RadRibbonRadioButton>
How it looks:
No luck getting it in front of the ribbon button but oh well.
回答1:
Here's my solution for this. By default, the badge will show in the upper right corner. You can change this by setting the "BadgeMarginOffset" property. I've attached a couple of images to show how it appears. One, which shows the badge wrapping a Telerik RadRibbonButton. You can also change the background and foreground colors of the badge (BadgeBackground, BadgeForeground). The defaults are shown below.
The UserControl's XAML
<UserControl x:Class="Foundation.Common.Controls.Wpf.Badge"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:Foundation.Common.Controls.Wpf.Converters"
Background="Transparent" x:Name="UserControl"
mc:Ignorable="d" >
<UserControl.Resources>
<converters:GreaterThanZeroBooleanConverter x:Key="GreaterThanZeroBooleanConverter" />
<converters:GreaterThanZeroVisibilityConverter x:Key="GreaterThanZeroVisibilityConverter"/>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid HorizontalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border CornerRadius="10"
UseLayoutRounding="True"
x:Name="BadgeBorder"
Grid.ZIndex="99"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Visibility="{Binding ElementName=UserControl, Path=Count, Mode=TwoWay, Converter={StaticResource GreaterThanZeroVisibilityConverter}}"
Grid.Row="0"
Margin="{Binding ElementName=UserControl, Path=BadgeMarginOffset}"
Height="22"
Padding="6.7,2,7,3"
Background="{Binding ElementName=UserControl, Path=BadgeBackground}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=UserControl, Path=Count, Mode=TwoWay, Converter={StaticResource GreaterThanZeroBooleanConverter}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0.0"
To="1.0"
Duration="0:0:0.7"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=UserControl, Path=ShowDropShadow}" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="6" ShadowDepth="4" Color="#949494"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding ElementName=UserControl, Path=Count}"
Foreground="{Binding ElementName=UserControl, Path=BadgeForeground}"
FontWeight="Bold"
FontSize="12">
</TextBlock>
</Border>
<ContentPresenter Grid.Row="0"
Grid.RowSpan="2"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
</Grid>
</ControlTemplate>
</UserControl.Template>
The UserControl's code behind
public partial class Badge : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty CountProperty =
DependencyProperty.Register("Count", typeof(int), typeof(Badge));
public static readonly DependencyProperty ShowDropShadowProperty =
DependencyProperty.Register("ShowDropShadow", typeof(bool), typeof(Badge), new PropertyMetadata(true));
public static readonly DependencyProperty BadgeMarginOffsetProperty =
DependencyProperty.Register("BadgeMarginOffset", typeof(Thickness), typeof(Badge));
public static readonly DependencyProperty BadgeBackgroundProperty =
DependencyProperty.Register("BadgeBackground", typeof(Brush), typeof(Badge), new PropertyMetadata(Brushes.Red));
public static readonly DependencyProperty BadgeForegroundProperty =
DependencyProperty.Register("BadgeForeground", typeof(Brush), typeof(Badge), new PropertyMetadata(Brushes.White));
#endregion Dependency Properties
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="Badge"/> class.
/// </summary>
public Badge()
{
this.InitializeComponent();
}
#endregion Constructor
#region Properties
/// <summary>
/// Gets or sets a value indicating whether [show drop shadow].
/// </summary>
/// <value>
/// <c>true</c> if [show drop shadow]; otherwise, <c>false</c>.
/// </value>
public bool ShowDropShadow
{
get => (bool)this.GetValue(ShowDropShadowProperty);
set => this.SetValue(ShowDropShadowProperty, value);
}
/// <summary>
/// Gets or sets the badge margin offset.
/// </summary>
/// <value>
/// The badge margin offset.
/// </value>
public Thickness BadgeMarginOffset
{
get => (Thickness)this.GetValue(BadgeMarginOffsetProperty);
set => this.SetValue(BadgeMarginOffsetProperty, value);
}
/// <summary>
/// Gets or sets the badge background.
/// </summary>
/// <value>
/// The badge background.
/// </value>
public Brush BadgeBackground
{
get => (Brush)this.GetValue(BadgeBackgroundProperty);
set => this.SetValue(BadgeBackgroundProperty, value);
}
/// <summary>
/// Gets or sets the badge foreground.
/// </summary>
/// <value>
/// The badge foreground.
/// </value>
public Brush BadgeForeground
{
get => (Brush)this.GetValue(BadgeForegroundProperty);
set => this.SetValue(BadgeBackgroundProperty, value);
}
/// <summary>
/// Gets or sets the count.
/// </summary>
/// <value>
/// The count.
/// </value>
public int Count
{
get => (int)this.GetValue(CountProperty);
set => this.SetValue(CountProperty, value);
}
#endregion Properties
}
Sample code for the top two images
<wpf:Badge Count="108" Margin="20" HorizontalAlignment="Left" BadgeMarginOffset="0,-5,-5,0">
<Button Height="100" Width="100">
<Button.Content>
<Image Source="Resources/about.png" />
</Button.Content>
</Button>
</wpf:Badge>
回答2:
This can be accomplished with a few bindings and an optional value converter. This samples assumes you are binding to a model that has an Items property and that that property is of type ObservableCollection so that the Count property of the collection will fire property changed when items are added/removed.
<Grid>
<Grid.Resources>
<local:CountToVisbilityConverter x:Key="CountToVis"/>
</Grid.Resources>
....
<Grid Width="25" Height="25" Visibility="{Binding Items.Count, Converter=CountToVis}">
<Ellipse Fill="Red" Width="25" Height="25"/>
<ViewBox Width="25" Height="25">
<TextBlock Text="{Binding Itmes.Count}" Foreground="White"/>
</Viewbox>
</Grid>
</Grid>
And the value converter:
public class CountToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value == null) return Visibility.Collapsed;
int count = System.Convert.ToInt32(value);
return count == 0 ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
The reason I saw optional" converter is because you can also use the Interaction DataTriggers like such
<Grid x:Name="UnreadNotification" Width="25" Height="25">
<Ellipse Fill="Red" Width="25" Height="25"/>
<ViewBox Width="25" Height="25">
<TextBlock Text="{Binding Itmes.Count}" Foreground="White"/>
</Viewbox>
</Grid>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding Items.Count, Comparison="Equal"
Value="0">
<ei:ChangePropertyAction PropertyName="IsEnabled"
Value="True"
TargetName="UnreadNotification" />
</ei:DataTrigger>
</i:Interaction.Triggers>
来源:https://stackoverflow.com/questions/10343293/icon-badge-overlay-for-notifications-in-silverlight-xaml