1.框架使用.NET4 至 .NET6;
2.IDE工具用Visual Studio 2022;
3.实现方式通过获取当前焦点的 Window 然后对它添加装饰器,将 Message 内部添加 ListBox 用作记录显示消息。
4..新建装饰器 MessageAdorner.cs 提供一个公共方法 PushMessage 提供可传参数
4.1 message 消息文本,居中显示从 Y 轴 0 方向屏幕下添加。
4.2 MessageBoxImage 消息类型枚举
4.3消息添加到 ListBox 集合中后,使用 DispatcherTimer 用来记时关闭 MessageItem
5.新建 MessageItem.cs 基础 ListBoxItem 用来展示每条 Message 消息
5.1 添加一个依赖属性 MessageType 枚举,用来区分显示的 Icon 与文本字体颜色
6.新建 MessageItem.xaml 的模板
6.1 对 EventTrigger Loaded 添加动画 ScaleY 从 0 到 1
7新建 Message.cs 帮助类实现全局方便使用
7.1 添加静态方法 PushMessage 判断装饰器是否为空,如果为空则创建装饰,如果不为空则调用装饰器内部 PushMessage 方法
此项目使用了 WPFDevelopers 。
1)新增 MessageItem.cs 代码如下:
using System.Windows;
using System.Windows.Controls;
namespace MessageSample
public class MessageItem : ListBoxItem
public MessageBoxImage MessageType
// 堆代码 duidaima.com
get { return (MessageBoxImage)GetValue(MessageTypeProperty); }
set { SetValue(MessageTypeProperty, value); }
public static readonly DependencyProperty MessageTypeProperty =
DependencyProperty.Register("MessageType", typeof(MessageBoxImage), typeof(MessageItem), new PropertyMetadata(MessageBoxImage.Information));
2)新增 MessageAdorner.cs 代码如下:
public class MessageAdorner : Adorner
private ListBox listBox;
private UIElement _child;
private FrameworkElement adornedElement;
public MessageAdorner(UIElement adornedElement) : base(adornedElement)
this.adornedElement = adornedElement as FrameworkElement;
public void PushMessage(string message, MessageBoxImage type = MessageBoxImage.Information)
if (listBox == null)
listBox = new ListBox() { Style = null ,BorderThickness = new Thickness(0) ,Background = Brushes.Transparent};
Child = listBox;
var item = new MessageItem{ Content = message , MessageType = type};
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += (sender, e) =>
listBox.Items.Remove(item);
timer.Stop();
listBox.Items.Insert(0,item);
timer.Start();
public UIElement Child
get => _child;
if (value == null)
RemoveVisualChild(_child);
_child = value;
return;
AddVisualChild(value);
_child = value;
protected override int VisualChildrenCount
return _child != null ? 1 : 0;
protected override Size ArrangeOverride(Size finalSize)
var x = (adornedElement.ActualWidth - _child.DesiredSize.Width) / 2;
_child.Arrange(new Rect(new Point(x, 0), _child.DesiredSize));
return finalSize;
protected override Visual GetVisualChild(int index)
if (index == 0 && _child != null) return _child;
return base.GetVisualChild(index);
3)新增 Message.cs 代码如下:
public static class Message
// 堆代码 duidaima.com
private static MessageAdorner messageAdorner;
public static void PushMessage( string message, MessageBoxImage type = MessageBoxImage.Information)
if (messageAdorner != null)
messageAdorner.PushMessage(message, type);
return;
Window win = null;
if (Application.Current.Windows.Count > 0)
win = Application.Current.Windows.OfType<Window>().FirstOrDefault(o => o.IsActive);
if (win == null)
win = Application.Current.Windows.OfType<Window>().First(o => o.IsActive);
var layer = GetAdornerLayer(win);
if (layer == null)
throw new Exception("not AdornerLayer is null");
messageAdorner = new MessageAdorner(layer);
layer.Add(messageAdorner);
messageAdorner.PushMessage(message, type);
static AdornerLayer GetAdornerLayer(Visual visual)
var decorator = visual as AdornerDecorator;
if (decorator != null)
return decorator.AdornerLayer;
var presenter = visual as ScrollContentPresenter;
if (presenter != null)
return presenter.AdornerLayer;
var visualContent = (visual as Window)?.Content as Visual;
return AdornerLayer.GetAdornerLayer(visualContent ?? visual);
4)新增 MessageItem.xaml 代码如下:
<Style BasedOn="{StaticResource WD.ControlBasicStyle}" TargetType="{x:Type local:MessageItem}">
<Setter Property="Background" Value="{DynamicResource WD.BackgroundSolidColorBrush}" />
<Setter Property="Width" Value="300" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MessageItem}">
<wd:SmallPanel
Name="PART_SmallPanel"
Margin="4"
RenderTransformOrigin=".5,0">
<wd:SmallPanel.RenderTransform>
<ScaleTransform />
</wd:SmallPanel.RenderTransform>
<Border
Name="PART_Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=(wd:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
Effect="{StaticResource WD.NormalShadowDepth}"
SnapsToDevicePixels="True"
UseLayoutRounding="True" />
<Border Padding="10">
<DockPanel>
x:Name="PART_Path"
Width="15"
Height="15"
Data="{StaticResource WD.InformationGeometry}"
Fill="{DynamicResource WD.PrimaryNormalSolidColorBrush}"
Stretch="Fill" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
Margin="5,0"
VerticalAlignment="Center"
FontSize="{DynamicResource WD.NormalFontSize}"
Foreground="{TemplateBinding Foreground}"
Text="{TemplateBinding Content}"
TextWrapping="Wrap" />
</DockPanel>
</Border>
</wd:SmallPanel>
<ControlTemplate.Triggers>
<Trigger Property="MessageType" Value="Warning">
<Setter TargetName="PART_Path" Property="Data" Value="{StaticResource WD.WarningGeometry}" />
<Setter TargetName="PART_Path" Property="Fill" Value="{StaticResource WD.WarningSolidColorBrush}" />
<Setter Property="Foreground" Value="{StaticResource WD.WarningSolidColorBrush}" />
</Trigger>
<Trigger Property="MessageType" Value="Error">
<Setter TargetName="PART_Path" Property="Data" Value="{StaticResource WD.ErrorGeometry}" />
<Setter TargetName="PART_Path" Property="Fill" Value="{StaticResource WD.DangerSolidColorBrush}" />
<Setter Property="Foreground" Value="{StaticResource WD.DangerSolidColorBrush}" />
</Trigger>
<Trigger Property="MessageType" Value="Information">
<Setter TargetName="PART_Path" Property="Data" Value="{StaticResource WD.InformationGeometry}" />
<Setter TargetName="PART_Path" Property="Fill" Value="{StaticResource WD.SuccessSolidColorBrush}" />
<Setter Property="Foreground" Value="{StaticResource WD.SuccessSolidColorBrush}" />
</Trigger>
<Trigger Property="MessageType" Value="Question">
<Setter TargetName="PART_Path" Property="Data" Value="{StaticResource WD.QuestionGeometry}" />
<Setter TargetName="PART_Path" Property="Fill" Value="{StaticResource WD.NormalSolidColorBrush}" />
<Setter Property="Foreground" Value="{StaticResource WD.NormalSolidColorBrush}" />
</Trigger>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_SmallPanel"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
From="0"
To="1"
Duration="0:0:0.2" />
<DoubleAnimation
Storyboard.TargetName="PART_SmallPanel"
Storyboard.TargetProperty="Opacity"
From="0.01"
To="1"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger SourceName="PART_SmallPanel" Property="Opacity" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
5)新增 示例 代码如下:
<wd:Window
x:Class="MessageSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MessageSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
Title="WPFDevelopers - Message"
Width="800"
Height="450"
mc:Ignorable="d">
<StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<Button
Click="AddButton_Click"
Content="Info Message"
Style="{StaticResource WD.SuccessPrimaryButton}"
Tag="Info" />
<Button
Click="AddButton_Click"
Content="Error Message"
Style="{StaticResource WD.DangerPrimaryButton}"
Tag="Error" />
<Button
Click="AddButton_Click"
Content="Warning Message"
Style="{StaticResource WD.WarningPrimaryButton}"
Tag="Warning" />
<Button
Click="AddButton_Click"
Content="Question Message"
Style="{StaticResource WD.PrimaryButton}"
Tag="Question" />
<Button
Click="AddButton_Click"
Content="Very Long Message"
Style="{StaticResource WD.SuccessPrimaryButton}"
Tag="Long" />
</StackPanel>
</Grid>
</wd:Window>
6) 示例 代码如下:
using System.Windows;
using System.Windows.Controls;
namespace MessageSample
/// <summary>
/// 堆代码 duidaima.com
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
public MainWindow()
InitializeComponent();
private void AddButton_Click(object sender, RoutedEventArgs e)
var btn = sender as Button;
switch (btn.Tag)
case "Info":
Message.PushMessage("这是一条成功消息", MessageBoxImage.Information);
break;
case "Error":
Message.PushMessage("这是一条错误消息", MessageBoxImage.Error);
break;
case "Warning":
Message.PushMessage("这是一条警告消息", MessageBoxImage.Warning);
break;
case "Question":
Message.PushMessage("这是一条询问消息", MessageBoxImage.Question);
break;
default:
Message.PushMessage("这是一条很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长消息", MessageBoxImage.Information);
break;