首页 开源 下载 教程 Xiao

开源代码 | XiaoPush3.0

极简设计 · 开源可定制

XiaoPush3_0.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <Nullable>enable</Nullable>
        <UseWPF>true</UseWPF>
        <ApplicationIcon>Assets\favicon.ico</ApplicationIcon>
    </PropertyGroup>
    <ItemGroup>
        <None Include="Assets\*.png" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
    <ItemGroup>
        <Resource Include="Assets\favicon.ico">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Resource>
        <Resource Include="Assets\logo.png">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Resource>
        <Resource Include="Assets\wechat.png">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Resource>
    </ItemGroup>
</Project>
                
MainWindow.xaml

<Window x:Class="XiaoPush3_0.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="XiaoPush3_0" 
    Height="Auto" 
    Width="300"
    AllowsTransparency="True" 
    Background="Transparent" 
    WindowStyle="None" 
    Topmost="True" 
    ShowInTaskbar="False"
    ResizeMode="NoResize" 
    WindowStartupLocation="Manual">
    <Grid>
        <Canvas x:Name="MessagesCanvas" 
        HorizontalAlignment="Right" 
        VerticalAlignment="Top" 
        Width="300" 
        Height="600" />
    </Grid>
</Window>
            
MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Xml.Linq;
using System.Windows.Threading;
using System.Security.Cryptography;
namespace XiaoPush3_0
{
    public partial class MainWindow : Window
    {

        private const string UserAuthFile = "user_auth.json";
        private const string IconMapFile = "icon_mapping.json";
        private string uid;
        private string regCode;
        private bool uid_read_type;
        private static readonly HttpClient httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        private readonly string baseUrl = "https://localhost/api/";
        private readonly List messagesList = new List();
        private readonly int keepTime = 8000; private Dictionary iconMap;
        private const string DefaultIconPath = "./Assets/logo.png";

        private class UserAuthData
        {
            public string Uid { get; set; }
            public string RegCode { get; set; }
        }
        private class IconMappingData : Dictionary { }

        public MainWindow()
        {
            InitializeComponent();
            Loaded += OnLoaded;
            Closed += OnClosed;
        }

        private async void OnLoaded(object sender, RoutedEventArgs e)
        {
            try
            {
                Left = SystemParameters.PrimaryScreenWidth - Width;
                Top = 0;
                var hwnd = new WindowInteropHelper(this).Handle;
                SetWindowExTransparent(hwnd);
                LoadIconMapFromJson();
                AddMessage("XiaoPush", "Nice To Meet You");
                await StartProcess();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"启动失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void OnClosed(object sender, EventArgs e)
        {
            foreach (var message in messagesList)
            {
                CleanupMessage(message);
            }
            messagesList.Clear();
            MessagesCanvas.Children.Clear();
        }
        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
        [DllImport("user32.dll")]
        static extern int GetWindowLong(IntPtr hwnd, int index);
        private void SetWindowExTransparent(IntPtr hwnd)
        {
            const int WS_EX_TRANSPARENT = 0x00000020;
            const int WS_EX_TOOLWINDOW = 0x00000080;
            const int GWL_EXSTYLE = -20;
            try
            {
                var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
                SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"设置窗口样式失败: {ex.Message}");
            }
        }


        private void LoadIconMapFromJson()
        {
            try
            {
                var jsonOptions = new JsonSerializerOptions { WriteIndented = true }; if (!File.Exists(IconMapFile))
                {
                    iconMap = new IconMappingData
                    {
                        { "com.tencent.mm", "./Assets/wechat.png" },
                        { "com.tencent.mobileqq", "./Assets/qq.png" }
                    };
                    string defaultJson = JsonSerializer.Serialize(iconMap, jsonOptions);
                    File.WriteAllText(IconMapFile, defaultJson);
                    return;
                }
                string iconJson = File.ReadAllText(IconMapFile);
                iconMap = JsonSerializer.Deserialize(iconJson) ?? new IconMappingData();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"加载图标映射失败: {ex.Message},使用默认配置");
                iconMap = new Dictionary
                {
                    { "com.tencent.mm", "./Assets/wechat.png" },
                    { "com.tencent.qq", "./Assets/logo.png" }
                };
            }
        }
        private (string uid, bool type) LoadOrGenerateUid()
        {
            try
            {
                if (File.Exists(UserAuthFile))
                {
                    string authJson = File.ReadAllText(UserAuthFile);
                    var authData = JsonSerializer.Deserialize(authJson); if (authData != null && !string.IsNullOrEmpty(authData.Uid))
                    {
                        uid = authData.Uid.Trim();
                        regCode = authData.RegCode ?? string.Empty; return (uid, true);
                    }
                }
                string newUid = GenerateUuidV7();
                regCode = string.Empty;
                SaveUserAuthData(newUid, regCode);
                return (newUid, false);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"UID加载失败: {ex.Message},生成新UID");
                string newUid = GenerateUuidV7();
                regCode = string.Empty;
                SaveUserAuthData(newUid, regCode);
                return (newUid, false);
            }
        }
        private void SaveUserAuthData(string uid, string regCode)
        {
            try
            {
                var authData = new UserAuthData
                {
                    Uid = uid,
                    RegCode = regCode ?? string.Empty
                };
                var jsonOptions = new JsonSerializerOptions { WriteIndented = true };
                string authJson = JsonSerializer.Serialize(authData, jsonOptions);
                File.WriteAllText(UserAuthFile, authJson);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"保存用户认证数据失败: {ex.Message}");
            }
        }
        private void SaveUid(string uid)
        {
            SaveUserAuthData(uid, regCode);
        }

        private async Task StartProcess()
        {
            try
            {
                (uid, uid_read_type) = LoadOrGenerateUid(); if (!uid_read_type)
                {
                    string newRegCode = await RegisterUid(uid); if (newRegCode == "re-register")
                    {
                        uid = GenerateUuidV7();
                        newRegCode = await RegisterUid(uid);
                    }
                    if (!string.IsNullOrEmpty(newRegCode) && newRegCode != "re-register")
                    {
                        regCode = newRegCode;
                        SaveUserAuthData(uid, regCode); AddMessage("注册码", regCode); await PollLogin();
                    }
                    else
                    {
                        AddMessage("注册失败", "无法连接到服务器,请检查网络");
                    }
                }
                else
                {
                    await PollLogin();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"启动流程失败: {ex.Message}");
                AddMessage("启动失败", ex.Message);
            }
        }
        private string GenerateUuidV7()
        {
            try
            {
                long unixTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); byte[] uuidBytes = new byte[16]; uuidBytes[0] = (byte)(unixTimeMs >> 40);
                uuidBytes[1] = (byte)(unixTimeMs >> 32);
                uuidBytes[2] = (byte)(unixTimeMs >> 24);
                uuidBytes[3] = (byte)(unixTimeMs >> 16);
                uuidBytes[4] = (byte)(unixTimeMs >> 8);
                uuidBytes[5] = (byte)unixTimeMs; uuidBytes[6] = 0x70; uuidBytes[8] = 0x80; using (var rng = RandomNumberGenerator.Create())
                {
                    byte[] randomByte = new byte[1];
                    rng.GetBytes(randomByte);
                    uuidBytes[6] |= (byte)(randomByte[0] & 0x0F); rng.GetBytes(uuidBytes.AsSpan(7, 1));
                    rng.GetBytes(uuidBytes.AsSpan(9, 7));
                }
                return string.Format("{0:X2}{1:X2}{2:X2}{3:X2}-{4:X2}{5:X2}-{6:X2}{7:X2}-{8:X2}{9:X2}-{10:X2}{11:X2}{12:X2}{13:X2}{14:X2}{15:X2}",
                    uuidBytes[0], uuidBytes[1], uuidBytes[2], uuidBytes[3],
                    uuidBytes[4], uuidBytes[5],
                    uuidBytes[6], uuidBytes[7],
                    uuidBytes[8], uuidBytes[9],
                    uuidBytes[10], uuidBytes[11], uuidBytes[12], uuidBytes[13], uuidBytes[14], uuidBytes[15]).ToLower();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"UUID v7生成失败: {ex.Message},使用后备GUID");
                return Guid.NewGuid().ToString().ToLower();
            }
        }
        private async Task RegisterUid(string uid)
        {
            try
            {
                var content = new FormUrlEncodedContent(new[] { new KeyValuePair("uid", uid) });
                var response = await httpClient.PostAsync(baseUrl + "register.php", content);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
            catch (HttpRequestException ex)
            {
                Console.WriteLine($"注册请求失败: {ex.Message}");
                return null;
            }
            catch (TaskCanceledException)
            {
                Console.WriteLine("注册请求超时");
                return null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"注册错误: {ex.Message}");
                return null;
            }
        }
        private async Task PollLogin()
        {
            int maxRetries = 30;
            int retryCount = 0;
            while (retryCount < maxRetries)
            {
                try
                {
                    AddMessage("XiaoPush", "身份认证中!");
                    string result = await LoginUid(uid); if (result == "online")
                    {
                        AddMessage("XiaoPush", "身份认证已通过!");
                        await StartLongPolling();
                        return;
                    }
                    else if (result == "not-whitelisted")
                    {
                        if (!string.IsNullOrEmpty(regCode))
                        {
                            AddMessage("注册码", regCode);
                        }
                        Console.WriteLine($"未在白名单中,重试中... ({retryCount + 1}/{maxRetries})");
                    }
                    else
                    {
                        AddMessage("注册码?", result);
                    }
                    retryCount++;
                }
                catch (Exception ex)
                {
                    AddMessage("XiaoPush", "身份认证失败");
                    Console.WriteLine($"登录轮询错误: {ex.Message}");
                    retryCount++;
                }
                await Task.Delay(10000);
            }
            AddMessage("登录失败", "无法连接到服务,请检查网络或联系管理员");
        }
        private async Task LoginUid(string uid)
        {
            try
            {
                var content = new FormUrlEncodedContent(new[] { new KeyValuePair("uid", uid) });
                var response = await httpClient.PostAsync(baseUrl + "login.php", content);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
            catch (HttpRequestException ex)
            {
                Console.WriteLine($"登录请求失败: {ex.Message}");
                return null;
            }
            catch (TaskCanceledException)
            {
                Console.WriteLine("登录请求超时");
                return null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"登录错误: {ex.Message}");
                return null;
            }
        }
        private async Task StartLongPolling()
        {
            Dispatcher.Invoke(() =>
            {
                messagesList.Clear();
                MessagesCanvas.Children.Clear();
            });
            AddMessage("XiaoPush", "Xiao与您同行..."); int failCount = 0;
            while (true)
            {
                try
                {
                    if (!this.IsLoaded) break;
                    string jsonStr = await LongPollUid(uid); if (!string.IsNullOrEmpty(jsonStr))
                    {
                        try
                        {
                            var msg = JsonSerializer.Deserialize(jsonStr);
                            if (msg != null && !string.IsNullOrEmpty(msg.message_title))
                            {
                                AddMessage(msg.message_title, msg.message_content, msg.package_name);
                            }
                        }
                        catch (JsonException ex)
                        {
                            Console.WriteLine($"JSON解析失败: {ex.Message}");
                        }
                    }
                    failCount = 0;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"长轮询错误: {ex.Message}");
                    failCount++; if (failCount >= 3)
                    {
                        string attackResult = await CheckAttack(uid);
                        if (!string.IsNullOrEmpty(attackResult) && attackResult != "normal")
                        {
                            Console.WriteLine($"黑名单解封时间: {attackResult}");
                            AddMessage("网络异常", "检测到异常请求,等待恢复...");
                            await Task.Delay(60000);
                            failCount = 0;
                        }
                    }
                    await Task.Delay(5000);
                }
            }
        }
        private async Task LongPollUid(string uid)
        {
            try
            {
                var content = new FormUrlEncodedContent(new[] { new KeyValuePair("uid", uid) });
                var response = await httpClient.PostAsync(baseUrl + "longpolling.php", content);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
            catch (HttpRequestException ex)
            {
                Console.WriteLine($"长轮询请求失败: {ex.Message}");
                return null;
            }
            catch (TaskCanceledException)
            {
                return string.Empty;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"长轮询错误: {ex.Message}");
                return null;
            }
        }
        private async Task CheckAttack(string uid)
        {
            try
            {
                var content = new FormUrlEncodedContent(new[] { new KeyValuePair("uid", uid) });
                var response = await httpClient.PostAsync(baseUrl + "attacktest.php", content);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"攻击检测请求失败: {ex.Message}");
                return null;
            }
        }

        private void AddMessage(string title, string content, string packageName = null)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(() => AddMessage(title, content, packageName));
                return;
            }
            var message = new Border
            {
                Width = 280,
                Height = 70,
                Background = new SolidColorBrush(Color.FromArgb(192, 255, 255, 255)),
                BorderBrush = Brushes.White,
                BorderThickness = new Thickness(2),
                CornerRadius = new CornerRadius(18),
                Margin = new Thickness(10, 0, 10, 0),
                Opacity = 0.1,
                RenderTransform = new ScaleTransform(0.1, 0.1),
                Effect = new DropShadowEffect
                {
                    Color = Colors.Black,
                    Direction = 320,
                    ShadowDepth = 5,
                    Opacity = 0.3,
                    BlurRadius = 10
                }
            }; var grid = new Grid();
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(70) });
            grid.ColumnDefinitions.Add(new ColumnDefinition()); var img = new Image
            {
                Source = LoadBitmapImage(GetIconPath(packageName)),
                Height = 44,
                Width = 44,
                Margin = new Thickness(10)
            };
            Grid.SetColumn(img, 0); var stack = new StackPanel
            {
                Orientation = Orientation.Vertical,
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Center
            }; var titleText = new TextBlock
            {
                Text = title,
                FontSize = 16,
                FontWeight = FontWeights.Bold,
                FontFamily = new FontFamily("Segoe UI"),
                MaxWidth = 180,
                TextTrimming = TextTrimming.CharacterEllipsis,
                Foreground = Brushes.Black,
                TextAlignment = TextAlignment.Left
            }; var contentText = new TextBlock
            {
                Text = content,
                FontSize = 14,
                MaxWidth = 180,
                TextTrimming = TextTrimming.CharacterEllipsis,
                Foreground = Brushes.Gray,
                FontFamily = new FontFamily("Segoe UI"),
            }; stack.Children.Add(titleText);
            stack.Children.Add(contentText);
            Grid.SetColumn(stack, 1); grid.Children.Add(img);
            grid.Children.Add(stack);
            message.Child = grid; Canvas.SetTop(message, -110);
            Canvas.SetRight(message, 0); MessagesCanvas.Children.Add(message);
            messagesList.Add(message); AnimateMessageIn(message);
            RefreshMessages();
            RecycleMessage(message);
        }
        private void AnimateMessageIn(FrameworkElement message)
        {
            var config_duation_time = 1;
            if (!(message.RenderTransform is ScaleTransform))
            {
                message.RenderTransform = new ScaleTransform();
                message.RenderTransformOrigin = new Point(0.5, 0.5);
            }
            var enterStoryboard = new Storyboard(); var slideIn = new DoubleAnimation
            {
                From = -100,
                To = 10,
                Duration = TimeSpan.FromSeconds(config_duation_time),
                EasingFunction = new ElasticEase
                {
                    Oscillations = 2,
                    Springiness = 5,
                    EasingMode = EasingMode.EaseOut
                }
            };
            Storyboard.SetTarget(slideIn, message);
            Storyboard.SetTargetProperty(slideIn, new PropertyPath(Canvas.TopProperty)); var fadeIn = new DoubleAnimation
            {
                From = 0.1,
                To = 1,
                Duration = TimeSpan.FromSeconds(config_duation_time),
                EasingFunction = new QuarticEase { EasingMode = EasingMode.EaseOut }
            };
            Storyboard.SetTarget(fadeIn, message);
            Storyboard.SetTargetProperty(fadeIn, new PropertyPath(OpacityProperty)); var scaleXAnim = new DoubleAnimation
            {
                From = 0.1,
                To = 1,
                Duration = TimeSpan.FromSeconds(config_duation_time),
                EasingFunction = new ElasticEase { Oscillations = 2, Springiness = 5, EasingMode = EasingMode.EaseOut }
            };
            var scaleYAnim = new DoubleAnimation
            {
                From = 0.1,
                To = 1,
                Duration = TimeSpan.FromSeconds(config_duation_time),
                EasingFunction = new ElasticEase { Oscillations = 2, Springiness = 5, EasingMode = EasingMode.EaseOut }
            }; Storyboard.SetTarget(scaleXAnim, message);
            Storyboard.SetTargetProperty(scaleXAnim, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
            Storyboard.SetTarget(scaleYAnim, message);
            Storyboard.SetTargetProperty(scaleYAnim, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); enterStoryboard.Children.Add(slideIn);
            enterStoryboard.Children.Add(fadeIn);
            enterStoryboard.Children.Add(scaleXAnim);
            enterStoryboard.Children.Add(scaleYAnim); enterStoryboard.Begin();
        }
        private string GetIconPath(string packageName)
        {
            if (!string.IsNullOrEmpty(packageName) && iconMap.TryGetValue(packageName, out var path))
            {
                return path;
            }
            return DefaultIconPath;
        }
        private BitmapImage LoadBitmapImage(string imagePath)
        {
            try
            {
                if (string.IsNullOrEmpty(imagePath) || !File.Exists(imagePath))
                {
                    imagePath = DefaultIconPath;
                }
                var bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.UriSource = new Uri(imagePath, UriKind.RelativeOrAbsolute);
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.EndInit();
                bitmap.Freeze();
                return bitmap;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"加载图片失败 '{imagePath}': {ex.Message}");
                return new BitmapImage();
            }
        }
        private void RefreshMessages()
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(RefreshMessages);
                return;
            }
            var RefreshMessagesConfigDurationTime = 0.3;
            for (int i = 0; i < Math.Min(5, messagesList.Count); i++)
            {
                var element = messagesList[i];
                var storyboard = new Storyboard(); double targetTop = i * 75;
                var marginAnimation = new ThicknessAnimation
                {
                    To = new Thickness(0, targetTop, 10, 0),
                    Duration = TimeSpan.FromSeconds(RefreshMessagesConfigDurationTime),
                    EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut }
                };
                Storyboard.SetTarget(marginAnimation, element);
                Storyboard.SetTargetProperty(marginAnimation, new PropertyPath(FrameworkElement.MarginProperty));
                storyboard.Children.Add(marginAnimation); double targetOpacity = Math.Max(0, 1.0 - i * 0.01);
                var opacityAnimation = new DoubleAnimation
                {
                    To = targetOpacity,
                    Duration = TimeSpan.FromSeconds(RefreshMessagesConfigDurationTime),
                    EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
                };
                Storyboard.SetTarget(opacityAnimation, element);
                Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath(UIElement.OpacityProperty));
                storyboard.Children.Add(opacityAnimation); double targetScale = Math.Max(0, 1.0 - i * 0.01);
                if (!(element.RenderTransform is ScaleTransform))
                {
                    element.RenderTransform = new ScaleTransform(1, 1);
                    element.RenderTransformOrigin = new Point(0.5, 0.5);
                }
                var scaleXAnimation = new DoubleAnimation
                {
                    To = targetScale,
                    Duration = TimeSpan.FromSeconds(RefreshMessagesConfigDurationTime),
                    EasingFunction = new BackEase { Amplitude = 0.1, EasingMode = EasingMode.EaseOut }
                };
                Storyboard.SetTarget(scaleXAnimation, element);
                Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("RenderTransform.ScaleX"));
                storyboard.Children.Add(scaleXAnimation); var scaleYAnimation = new DoubleAnimation
                {
                    To = targetScale,
                    Duration = TimeSpan.FromSeconds(RefreshMessagesConfigDurationTime),
                    EasingFunction = new BackEase { Amplitude = 0.1, EasingMode = EasingMode.EaseOut }
                };
                Storyboard.SetTarget(scaleYAnimation, element);
                Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("RenderTransform.ScaleY"));
                storyboard.Children.Add(scaleYAnimation); element.SetValue(Panel.ZIndexProperty, 5 - i); var rightAnimation = new DoubleAnimation
                {
                    To = 0,
                    Duration = TimeSpan.FromSeconds(RefreshMessagesConfigDurationTime),
                    EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut }
                };
                Storyboard.SetTarget(rightAnimation, element);
                Storyboard.SetTargetProperty(rightAnimation, new PropertyPath(Canvas.RightProperty));
                storyboard.Children.Add(rightAnimation); storyboard.Begin();
            }
        }
        private async void RecycleMessage(FrameworkElement message)
        {
            try
            {
                await Task.Delay(keepTime);
                if (!this.IsLoaded) return; await Dispatcher.InvokeAsync(() =>
                {
                    if (!messagesList.Contains(message) || !MessagesCanvas.Children.Contains(message))
                        return; TransformGroup transformGroup = new TransformGroup();
                    ScaleTransform scaleTransform = (message.RenderTransform as ScaleTransform) ?? new ScaleTransform(1, 1);
                    TranslateTransform translateTransform = new TranslateTransform(0, 0);
                    transformGroup.Children.Add(scaleTransform);
                    transformGroup.Children.Add(translateTransform);
                    message.RenderTransform = transformGroup;
                    message.RenderTransformOrigin = new Point(0.5, 0); var fadeOut = new DoubleAnimation(message.Opacity, 0, TimeSpan.FromSeconds(0.5))
                    {
                        EasingFunction = new QuarticEase { EasingMode = EasingMode.EaseIn }
                    }; var currentScale = scaleTransform.ScaleX;
                    var scaleAnim = new DoubleAnimation(currentScale, 0, TimeSpan.FromSeconds(0.5))
                    {
                        EasingFunction = new QuarticEase { EasingMode = EasingMode.EaseIn }
                    }; var moveUpAnim = new DoubleAnimation(0, -20, TimeSpan.FromSeconds(0.5))
                    {
                        EasingFunction = new QuarticEase { EasingMode = EasingMode.EaseIn },
                        FillBehavior = FillBehavior.HoldEnd
                    }; message.BeginAnimation(OpacityProperty, fadeOut);
                    scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnim);
                    scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnim);
                    translateTransform.BeginAnimation(TranslateTransform.YProperty, moveUpAnim); messagesList.Remove(message); Task.Delay(500).ContinueWith(async _ =>
                    {
                        if (this.IsLoaded)
                        {
                            await Dispatcher.InvokeAsync(() =>
                            {
                                if (MessagesCanvas.Children.Contains(message))
                                {
                                    CleanupMessage(message);
                                    MessagesCanvas.Children.Remove(message);
                                }
                            });
                        }
                    }); RefreshMessages();
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"回收消息时出错: {ex.Message}");
            }
        }
        private void CleanupMessage(FrameworkElement message)
        {
            try
            {
                message.BeginAnimation(UIElement.OpacityProperty, null); if (message.RenderTransform is ScaleTransform scaleTransform)
                {
                    scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, null);
                    scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null);
                }
                message.RenderTransform = null; if (message is ContentControl contentControl)
                {
                    contentControl.Content = null;
                }
                else if (message is Panel panel)
                {
                    panel.Children.Clear();
                }
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"清理消息资源时出错: {ex.Message}");
            }
            if (message is Border border && border.Child is Grid grid)
            {
                foreach (var child in grid.Children)
                {
                    if (child is Image image)
                    {
                        image.Source = null;
                    }
                }
            }
        }

    }
    public class MessageItem
    {
        public string package_name { get; set; }
        public string message_title { get; set; }
        public string message_content { get; set; }
    }

}

                
App.xaml

<Application x:Class="XiaoPush3_0.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>

    </Application.Resources>
</Application>
            
App.xaml.cs

using System.Windows;

namespace XiaoPush3_0
{
public partial class App : Application
{
}
}