Накладываемый значок значка для уведомлений в silverlight / xaml

В моем приложении silverlight есть полоса ленты, и на одном из значков я бы хотел, чтобы значок значка отображал количество элементов в представлении, которое активируется значком.
Изобразите значок «Почта» в OS X отображение количества непрочитанных сообщений или счетчика уведомлений на значке приложения iOS.

Я мало знаю о стилях xaml, но мне кажется, что я мог бы продублировать стиль по умолчанию для кнопки панели ленты, а затем добавить к нему какой-то красный кружок и белый текст, значение которого было взято из нового свойство на кнопке панели ленты каким-то образом, чтобы я мог привязаться к нему.

Есть ли у кого-нибудь пример чего-то подобного, с которого я могу начать?


Спасибо, Шон, за ответ. Вот что я в итоге сделал:
В 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>

Как это выглядит:
введите описание изображения здесь

Не повезло поставить его перед кнопкой на ленте, ну да ладно.


person Coxy    schedule 27.04.2012    source источник
comment
Разместите уведомление за пределами самой кнопки, например: ‹Grid› ‹Button /› ‹Notification /› ‹/Grid›   -  person Shawn Kendrot    schedule 01.05.2012


Ответы (2)


Вот мое решение для этого. По умолчанию значок отображается в правом верхнем углу. Вы можете изменить это, установив свойство «BadgeMarginOffset». Я приложил пару изображений, чтобы показать, как это выглядит. Один, на котором изображен значок, оборачивающий Telerik RadRibbonButton. Вы также можете изменить цвет фона и переднего плана значка (BadgeBackground, BadgeForeground). Значения по умолчанию показаны ниже.

With BadgeMarginOffset

Нет BadgeMarginOffset

Telerik RadRibbonButton

XAML UserControl

<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
}

Пример кода для двух верхних изображений

<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>
person LawMan    schedule 03.05.2018
comment
В коде XAML UserControl строка DataContext=...line должна быть перемещена ниже. Например, в <Grid HorizontalAlignment=Right DataContext=... . Если вы оставите все так, как есть сейчас, при использовании элемента управления с Binding в Countproperty assignemnt (где вы жестко запрограммировали 108 в своем примере), DataContext не является правильным, и он не работает. Не заставляйте свой UserControl DataContext: но вы можете принудительно использовать некоторые элементы DataContext внутри него, если это необходимо. - person Askolein; 21.12.2018
comment
@Askolein, не могли бы вы немного уточнить ... Я переместил DataContext в сетку, как вы предложили, но у меня все еще есть проблемы с ошибкой привязки. Я должен отметить, что я изменил тип Count на строку вместо int (и он работал при жестком кодировании), чтобы я мог отображать '9+' вместо больших чисел, но кроме этого я не могу понять, почему привязка провал - person MegaMark; 30.05.2019
comment
@Askolein Я вижу это в журналах событий при отладке: Activated Event Time Duration Thread BindingExpression path error: свойство «Count» не найдено в «объекте» «Grid» (Name = '') '. BindingExpression: Путь = Счетчик; DataItem = 'Сетка' (Имя = ''); целевой элемент - «Граница» (Name = ''); целевое свойство - 'NoTarget' (тип 'Object') 0,29 с. - person MegaMark; 30.05.2019
comment
Я обновил XAML и код элемента управления. Это то, что мы используем в нашей производственной среде. - person LawMan; 31.05.2019

Этого можно добиться с помощью нескольких привязок и дополнительного преобразователя значений. В этих примерах предполагается, что вы привязаны к модели, которая имеет свойство Items и что это свойство имеет тип ObservableCollection, поэтому свойство Count коллекции будет активировать свойство, измененное при добавлении / удалении элементов.

<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>

И преобразователь значений:

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
}

Причина, по которой я увидел необязательный "конвертер", заключается в том, что вы также можете использовать такие триггеры данных взаимодействия.

    <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>
person Shawn Kendrot    schedule 27.04.2012
comment
Выглядит красиво, но работает только с ленточной панелью Teleriks. Вы знаете, как сделать то же самое с ленточной панелью Microsoft? - person Martin Andersen; 02.09.2014