WPF中自制类似微信消息提示框Toast控件

因系统功能需要,需要那种像微信一样的Toast弹出框,可以有图标和文字提示,然后一定时间后可以自动消失

本文章主要讲如何通过自定义控件配合Popup实现个性化Toast提示框,使用Net4开发,支持XP。目前主要实现了如下几个功能:

  • 淡入淡出弹出Toast提示框
  • 显示固定时长后自动消失
  • 友好性图标
  • 可根据父容器定位
TopLeft-------TopCenter------TopRight
|                 |                 |
CenterLeft-----Center-----CenterRight
|                 |                 |
BottomLeft--BottomCenter--BottomRight
  • 可根据显示器定位,并去除任务栏高度和宽度(为了防止某些装逼人士,喜欢把默认的底部任务栏移至其它方向)
TopLeft-------TopCenter------TopRight
|                 |                 |
CenterLeft-----Center-----CenterRight
|                 |                 |
BottomLeft--BottomCenter--BottomRight
================任务栏================
  • 可跟随父容器一起移动
  • 可根据父容器大小改变自动计算新位置
  • 可添加提示框关闭事件和单击事件

NuGet包

Toast使用了Awesome字体图标库,可以通过以下命令实现安装:

Install-Package MahApps.Metro.IconPacks.FontAwesome -Version 2.3.0

实现效果预览

在这里插入图片描述

创建Toast

首先新建一个Toast控件并继承于UserControl
在这里插入图片描述

构建前端页面

<UserControl x:Class="WpfToast.Controls.Toast"
             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:local="clr-namespace:WpfToast.Controls"
             xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
             mc:Ignorable="d" Height="{Binding Height}" Width="{Binding Width}" MouseLeftButtonDown="UserControl_MouseLeftButtonDown"
             d:DesignHeight="48" d:DesignWidth="200" MinWidth="200" MaxWidth="500" Focusable="False">
    <UserControl.Resources>
        <local:ToastIconConverter x:Key="icon_converter"></local:ToastIconConverter>
    </UserControl.Resources>
    <Border CornerRadius="{Binding CornerRadius}" 
            BorderThickness="{Binding BorderThickness}" 
            Background="{Binding Background}" 
            BorderBrush="{Binding BorderBrush}">
        <Grid x:Name="grid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <iconPacks:PackIconFontAwesome x:Name="icon_toast" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" 
                                           Width="{Binding IconSize}" Height="{Binding IconSize}" Margin="10 0 10 0">
                <iconPacks:PackIconFontAwesome.Kind>
                    <MultiBinding Converter="{StaticResource icon_converter}">
                        <Binding Path="Icon"/>
                        <Binding ElementName="grid"/>
                        <Binding ElementName="txt_toast"/>
                    </MultiBinding>
                </iconPacks:PackIconFontAwesome.Kind>
            </iconPacks:PackIconFontAwesome>
            <TextBlock x:Name="txt_toast" Grid.Column="1" Text="{Binding Message}" 
                       Foreground="{Binding Foreground}" 
                       FontStyle="{Binding FontStyle}"
                       FontStretch="{Binding FontStretch}"
                       FontSize="{Binding FontSize}"
                       FontFamily="{Binding FontFamily}"
                       FontWeight="{Binding FontWeight}"
                       VerticalAlignment="{Binding VerticalContentAlignment}" 
                       HorizontalAlignment="{Binding HorizontalContentAlignment}" Padding="0 0 4 0">
            </TextBlock>
        </Grid>
    </Border>
</UserControl>

构建Options类

public class ToastOptions
{
    public double Width { get; set; } = 200;
    public double Height { get; set; } = 48;
    public int Time { get; set; } = 2000;
    public ToastIcons Icon { get; set; } = ToastIcons.None;
    public ToastLocation Location { get; set; } = ToastLocation.Default;
    public Brush Foreground { get; set; } = Brushes.White;
    public FontStyle FontStyle { get; set; } = SystemFonts.MessageFontStyle;
    public FontStretch FontStretch { get; set; } = FontStretches.Normal;
    public double FontSize { get; set; } = SystemFonts.MessageFontSize;
    public FontFamily FontFamily { get; set; } = SystemFonts.MessageFontFamily;
    public FontWeight FontWeight { get; set; } = SystemFonts.MenuFontWeight;
    public double IconSize { get; set; } = 26;
    public CornerRadius CornerRadius { get; set; } = new CornerRadius(5);
    public Brush BorderBrush { get; set; }
    public Thickness BorderThickness { get; set; } = new Thickness(1);
    public Brush Background { get; set; } = (Brush)new BrushConverter().ConvertFromString("#2E2929");
    public HorizontalAlignment HorizontalContentAlignment { get; set; } = HorizontalAlignment.Left;
    public VerticalAlignment VerticalContentAlignment { get; set; } = VerticalAlignment.Center;
    public EventHandler<EventArgs> Closed { get; internal set; }
    public EventHandler<EventArgs> Click { get; internal set; }
}

构件图标和位置枚举类

public enum ToastIcons
{
    None,
    Information,//CheckSolid
    Error,//TimesSolid
    Warning,//ExclamationSolid
    Busy//ClockSolid
}

public enum ToastLocation
{
    OwnerCenter,
    OwnerLeft,
    OwnerRight,
    OwnerTopLeft,
    OwnerTopCenter,
    OwnerTopRight,
    OwnerBottomLeft,
    OwnerBottomCenter,
    OwnerBottomRight,
    ScreenCenter,
    ScreenLeft,
    ScreenRight,
    ScreenTopLeft,
    ScreenTopCenter,
    ScreenTopRight,
    ScreenBottomLeft,
    ScreenBottomCenter,
    ScreenBottomRight,
    Default//OwnerCenter
}

构建转换器

public class ToastIconConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        object value = values[0];
        object grid = values[1];
        object txt = values[2];
        Grid _grid = grid as Grid;
        TextBlock _txt = txt as TextBlock;
        if (value == null)
        {
            if (_grid != null)
            {
                _grid.ColumnDefinitions.RemoveAt(0);
            }
            if (_txt != null)
            {
                _txt.HorizontalAlignment = HorizontalAlignment.Center;
            }
            return PackIconFontAwesomeKind.None;
        }
        ToastIcons _value;
        try
        {
            _value = (ToastIcons)value;
        }
        catch
        {
            if (_grid != null)
            {
                _grid.ColumnDefinitions.RemoveAt(0);
            }
            if (_txt != null)
            {
                _txt.HorizontalAlignment = HorizontalAlignment.Center;
            }
            return PackIconFontAwesomeKind.None;
        }
        switch (_value)
        {
            case ToastIcons.Information:
                return PackIconFontAwesomeKind.CheckSolid;
            case ToastIcons.Error:
                return PackIconFontAwesomeKind.TimesSolid;
            case ToastIcons.Warning:
                return PackIconFontAwesomeKind.ExclamationSolid;
            case ToastIcons.Busy:
                return PackIconFontAwesomeKind.ClockSolid;
        }
        if (_grid != null)
        {
            _grid.ColumnDefinitions.RemoveAt(0);
        }
        if (_txt != null)
        {
            _txt.HorizontalAlignment = HorizontalAlignment.Center;
        }
        return PackIconFontAwesomeKind.None;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

构造方法

private Toast()
{
    InitializeComponent();
    this.DataContext = this;
}

private Toast(Window owner, string message, ToastOptions options = null)
{
    Message = message;
    InitializeComponent();
    if (options != null)
    {
        Width = options.Width;
        Height = options.Height;
        Icon = options.Icon;
        Location = options.Location;
        Time = options.Time;
        Closed += options.Closed;
        Click += options.Click;
        Background = options.Background;
        Foreground = options.Foreground;
        FontStyle = options.FontStyle;
        FontStretch = options.FontStretch;
        FontSize = options.FontSize;
        FontFamily = options.FontFamily;
        FontWeight = options.FontWeight;
        IconSize = options.IconSize;
        BorderBrush = options.BorderBrush;
        BorderThickness = options.BorderThickness;
        HorizontalContentAlignment = options.HorizontalContentAlignment;
        VerticalContentAlignment = options.VerticalContentAlignment;
        CornerRadius = options.CornerRadius;
    }
    this.DataContext = this;
    if (owner == null)
    {
        this.owner = Application.Current.MainWindow;
    }
    else
    {
        this.owner = owner;
    }
    this.owner.Closed += Owner_Closed;
}
private void Owner_Closed(object sender, EventArgs e)
{
    this.Close();
}

注册单击和关闭事件

private event EventHandler<EventArgs> Closed;
private void RaiseClosed(EventArgs e)
{
    Closed?.Invoke(this, e);
}

private event EventHandler<EventArgs> Click;
private void RaiseClick(EventArgs e)
{
    Click?.Invoke(this, e);
}

注册控件属性

private string Message
{
    get { return (string)GetValue(MessageProperty); }
    set { SetValue(MessageProperty, value); }
}

private static readonly DependencyProperty MessageProperty =
    DependencyProperty.Register("Message", typeof(string), typeof(Toast), new PropertyMetadata(string.Empty));

private CornerRadius CornerRadius
{
    get { return (CornerRadius)GetValue(CornerRadiusProperty); }
    set { SetValue(CornerRadiusProperty, value); }
}

private static readonly DependencyProperty CornerRadiusProperty =
    DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(Toast), new PropertyMetadata(new CornerRadius(5)));

private double IconSize
{
    get { return (double)GetValue(IconSizeProperty); }
    set { SetValue(IconSizeProperty, value); }
}

private static readonly DependencyProperty IconSizeProperty =
    DependencyProperty.Register("IconSize", typeof(double), typeof(Toast), new PropertyMetadata(26.0));

private new Brush BorderBrush
{
    get { return (Brush)GetValue(BorderBrushProperty); }
    set { SetValue(BorderBrushProperty, value); }
}

private static new readonly DependencyProperty BorderBrushProperty =
    DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(Toast), new PropertyMetadata((Brush)new BrushConverter().ConvertFromString("#FFFFFF")));

private new Thickness BorderThickness
{
    get { return (Thickness)GetValue(BorderThicknessProperty); }
    set { SetValue(BorderThicknessProperty, value); }
}

private static new readonly DependencyProperty BorderThicknessProperty =
    DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(Toast), new PropertyMetadata(new Thickness(0)));

private new Brush Background
{
    get { return (Brush)GetValue(BackgroundProperty); }
    set { SetValue(BackgroundProperty, value); }
}

private static new readonly DependencyProperty BackgroundProperty =
    DependencyProperty.Register("Background", typeof(Brush), typeof(Toast), new PropertyMetadata((Brush)new BrushConverter().ConvertFromString("#2E2929")));

private new HorizontalAlignment HorizontalContentAlignment
{
    get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); }
    set { SetValue(HorizontalContentAlignmentProperty, value); }
}

private static new readonly DependencyProperty HorizontalContentAlignmentProperty =
    DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(Toast), new PropertyMetadata(HorizontalAlignment.Left));

private new VerticalAlignment VerticalContentAlignment
{
    get { return (VerticalAlignment)GetValue(VerticalContentAlignmentProperty); }
    set { SetValue(VerticalContentAlignmentProperty, value); }
}

private static new readonly DependencyProperty VerticalContentAlignmentProperty =
    DependencyProperty.Register("VerticalContentAlignment", typeof(VerticalAlignment), typeof(Toast), new PropertyMetadata(VerticalAlignment.Center));

private new double Width
{
    get { return (double)GetValue(WidthProperty); }
    set { SetValue(WidthProperty, value); }
}

private new static readonly DependencyProperty WidthProperty =
    DependencyProperty.Register("Width", typeof(double), typeof(Toast), new PropertyMetadata(200.0));

private new double Height
{
    get { return (double)GetValue(HeightProperty); }
    set { SetValue(HeightProperty, value); }
}

private new static readonly DependencyProperty HeightProperty =
    DependencyProperty.Register("Height", typeof(double), typeof(Toast), new PropertyMetadata(48.0));

private ToastIcons Icon
{
    get { return (ToastIcons)GetValue(IconProperty); }
    set { SetValue(IconProperty, value); }
}

private static readonly DependencyProperty IconProperty =
    DependencyProperty.Register("Icon", typeof(ToastIcons), typeof(Toast), new PropertyMetadata(ToastIcons.None));

private int Time
{
    get { return (int)GetValue(TimeProperty); }
    set { SetValue(TimeProperty, value); }
}

private static readonly DependencyProperty TimeProperty =
    DependencyProperty.Register("Time", typeof(int), typeof(Toast), new PropertyMetadata(2000));

private ToastLocation Location
{
    get { return (ToastLocation)GetValue(LocationProperty); }
    set { SetValue(LocationProperty, value); }
}

private static readonly DependencyProperty LocationProperty =
    DependencyProperty.Register("Location", typeof(ToastLocation), typeof(Toast), new PropertyMetadata(ToastLocation.Default));

暴露类方法Show和对象方法Close

public static void Show(string msg, ToastOptions options = null)
{
    var toast = new Toast(null, msg, options);
    int time = toast.Time;
    ShowToast(toast, time);
}

public static void Show(Window owner, string msg, ToastOptions options = null)
{
    var toast = new Toast(owner, msg, options);
    int time = toast.Time;
    ShowToast(toast, time);
}
public void Close()
{
    if (timer != null)
    {
        timer.Stop();
        timer = null;
    }
    popup.IsOpen = false;
    owner.LocationChanged -= UpdatePosition;
    owner.SizeChanged -= UpdatePosition;
}

重新计算位置

private void UpdatePosition(object sender, EventArgs e)
{
    var up = typeof(Popup).GetMethod("UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    if (up == null || popup == null)
    {
        return;
    }
    SetPopupOffset(popup, this);
    up.Invoke(popup, null);
}

XP下运行效果

中间有个消失的,是我用鼠标点击了,实现单击事件和关闭事件
在这里插入图片描述
在这里插入图片描述
再给你们来个那种某些装逼人士的任务栏
在这里插入图片描述

XP环境下底部始终有一个高度,暂不清楚是不是系统自身原因,win10无此BUG

相关代码

伸手党:下载地址

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0564丶Kang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值