Icon badge overlay for notifications in silverlight/xaml

我的未来我决定 提交于 2019-12-22 14:56:33

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!