【问题标题】:Icon badge overlay for notifications in silverlight/xamlSilverlight/xaml 中通知的图标徽章覆盖
【发布时间】:2012-05-07 18:26:42
【问题描述】:

我的 silverlight 应用程序中有一个功能区栏,我希望在其中一个图标上有一个徽章图标,显示图标激活的视图中的项目数。
想象一下 OS X 中的 Mail 图标,显示未读消息的数量或 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>

外观如何:

在功能区按钮前面没有运气,但是哦。

【问题讨论】:

  • 将通知放在按钮本身之外示例:

标签: silverlight xaml notifications styles badge


【解决方案1】:

这是我的解决方案。默认情况下,徽章将显示在右上角。您可以通过设置“BadgeMarginOffset”属性来更改此设置。我附上了几张图片来展示它的外观。一,它显示了包装 Telerik RadRibbonButton 的徽章。您还可以更改徽章的背景和前景色(BadgeBackground、BadgeForeground)。默认值如下所示。

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

UserControl 背后的代码

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>

【讨论】:

  • 在 UserControl XAML 代码中,DataContext=...line 应该移到下面。例如在&lt;Grid HorizontalAlignment=Right DataContext=... 。如果您像现在这样保留它,当在Countproperty 分配中使用带有Binding 的控件(在您的示例中硬编码108)时,DataContext 不是正确的并且它会失败。不要强制您的 UserControl DataContext: 但如果需要,您可以在其中强制使用一些 Elements DataContext。
  • @Askolein 您能否详细说明一下...我按照您的建议将 DataContext 移到了网格中,但我仍然遇到绑定失败的问题。我应该注意,我将“Count”的类型更改为字符串而不是 int(并且在硬编码时有效),以便我可以显示 '9+' 而不是大数字,但除此之外我无法弄清楚为什么绑定失败
  • @Askolein 我在调试时在事件日志中看到这一点:Activated Event Time Duration Thread BindingExpression path error: 'Count' property not found on 'object' ''Grid' (Name='')' .绑定表达式:路径=计数;数据项='网格'(名称='');目标元素是 'Border' (Name='');目标属性是'NoTarget'(类型'Object')0.29s
  • 我已经更新了控件的 XAML 和代码。这是我们在生产环境中使用的。
【解决方案2】:

这可以通过一些绑定和一个可选的值转换器来完成。此示例假定您绑定到具有 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>

【讨论】:

  • 看起来不错,但仅适用于 Teleriks 功能区。你知道如何用微软的ribbonbar做同样的事情吗?
猜你喜欢
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2013-02-20
  • 1970-01-01
  • 1970-01-01
  • 2022-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多