Guest User

FloatingTextNotifier.cs

a guest
Feb 2nd, 2026
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.13 KB | Source Code | 0 0
  1. // FloatingTextNotifier.cs
  2. // .NET Framework 4.x, WinForms, C# 5-compatible (no C# 6 syntax)
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.Text;
  8. using System.Windows.Forms;
  9.  
  10. namespace FloatingTextNotifier
  11. {
  12.     static class Program
  13.     {
  14.         [STAThread]
  15.         static void Main()
  16.         {
  17.             Application.EnableVisualStyles();
  18.             Application.SetCompatibleTextRenderingDefault(false);
  19.  
  20.             Application.Run(new TrayAppContext());
  21.         }
  22.     }
  23.  
  24.     internal sealed class TrayAppContext : ApplicationContext
  25.     {
  26.         private const string TextFilePath = @"C:\texts.txt";
  27.  
  28.         private readonly NotifyIcon _notifyIcon;
  29.         private readonly ContextMenuStrip _menu;
  30.         private readonly ToolStripMenuItem _showItem;
  31.         private readonly ToolStripMenuItem _exitItem;
  32.  
  33.         private readonly FloatingTextForm _form;
  34.         private readonly Timer _hourTimer;
  35.  
  36.         private readonly Random _rng = new Random();
  37.  
  38.         public TrayAppContext()
  39.         {
  40.             _form = new FloatingTextForm();
  41.             _form.RequestHide += (s, e) => _form.Hide();
  42.  
  43.             _showItem = new ToolStripMenuItem("Показать текст", null, (s, e) => ShowNewText());
  44.             _exitItem = new ToolStripMenuItem("Выход", null, (s, e) => ExitApp());
  45.  
  46.             _menu = new ContextMenuStrip();
  47.             _menu.Items.AddRange(new ToolStripItem[] { _showItem, new ToolStripSeparator(), _exitItem });
  48.  
  49.             _notifyIcon = new NotifyIcon
  50.             {
  51.                 Text = "FloatingTextNotifier",
  52.                 Icon = SystemIcons.Information,
  53.                 Visible = true,
  54.                 ContextMenuStrip = _menu
  55.             };
  56.             _notifyIcon.MouseUp += NotifyIcon_MouseUp;
  57.  
  58.             _hourTimer = new Timer();
  59.             _hourTimer.Tick += (s, e) =>
  60.             {
  61.                 _hourTimer.Stop();
  62.                 ShowNewText();
  63.                 ScheduleNextHour();
  64.             };
  65.  
  66.             // Требование: сразу при запуске показать текст
  67.             ShowNewText();
  68.  
  69.             // Требование: далее ровно в начале каждого часа
  70.             ScheduleNextHour();
  71.         }
  72.  
  73.         private void NotifyIcon_MouseUp(object sender, MouseEventArgs e)
  74.         {
  75.             if (e.Button == MouseButtons.Left)
  76.             {
  77.                 ShowNewText();
  78.             }
  79.             else if (e.Button == MouseButtons.Right)
  80.             {
  81.                 _menu.Show(Cursor.Position);
  82.             }
  83.         }
  84.  
  85.         private void ScheduleNextHour()
  86.         {
  87.             DateTime now = DateTime.Now;
  88.             DateTime nextHour = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0).AddHours(1);
  89.             TimeSpan due = nextHour - now;
  90.  
  91.             int ms = Math.Max(1, (int)due.TotalMilliseconds);
  92.             _hourTimer.Interval = ms;
  93.             _hourTimer.Start();
  94.         }
  95.  
  96.         private void ShowNewText()
  97.         {
  98.             string text = LoadRandomLineOrError(TextFilePath);
  99.             _form.SetText(text);
  100.             _form.ShowInTopRight();
  101.         }
  102.  
  103.         private string LoadRandomLineOrError(string path)
  104.         {
  105.             try
  106.             {
  107.                 // Требование: читать файл полностью перед каждым показом, UTF-8
  108.                 string[] lines = File.ReadAllLines(path, Encoding.UTF8);
  109.  
  110.                 List<string> candidates = new List<string>(lines.Length);
  111.                 for (int i = 0; i < lines.Length; i++)
  112.                 {
  113.                     string line = lines[i];
  114.                     if (!string.IsNullOrWhiteSpace(line))
  115.                         candidates.Add(line.Trim());
  116.                 }
  117.  
  118.                 if (candidates.Count == 0)
  119.                     return "Ошибка: файл пуст или нет непустых строк.";
  120.  
  121.                 int idx = _rng.Next(candidates.Count);
  122.                 return candidates[idx];
  123.             }
  124.             catch (Exception ex)
  125.             {
  126.                 string msg = ex.Message ?? "Неизвестная ошибка.";
  127.                 if (msg.Length > 220) msg = msg.Substring(0, 220) + "...";
  128.                 return "Ошибка: " + msg;
  129.             }
  130.         }
  131.  
  132.         private void ExitApp()
  133.         {
  134.             _hourTimer.Stop();
  135.             _hourTimer.Dispose();
  136.  
  137.             _notifyIcon.Visible = false;
  138.             _notifyIcon.Dispose();
  139.  
  140.             if (!_form.IsDisposed)
  141.             {
  142.                 _form.Hide();
  143.                 _form.Dispose();
  144.             }
  145.  
  146.             ExitThread();
  147.         }
  148.     }
  149.  
  150.     internal sealed class FloatingTextForm : Form
  151.     {
  152.         public event EventHandler RequestHide;
  153.  
  154.         private readonly TextSurface _surface;
  155.  
  156.         public FloatingTextForm()
  157.         {
  158.             AutoScaleMode = AutoScaleMode.Dpi;
  159.             Font = SystemFonts.MessageBoxFont;
  160.  
  161.             // Стиль окна: стандартное системное окно текущей темы, без кнопок
  162.             FormBorderStyle = FormBorderStyle.FixedToolWindow;
  163.             ControlBox = false;
  164.             MinimizeBox = false;
  165.             MaximizeBox = false;
  166.             ShowIcon = false;
  167.             ShowInTaskbar = false;
  168.  
  169.             TopMost = true;
  170.  
  171.             // Фиксированный размер
  172.             ClientSize = new Size(360, 72);
  173.  
  174.             _surface = new TextSurface
  175.             {
  176.                 Dock = DockStyle.Fill,
  177.                 Padding = new Padding(10),
  178.                 BackColor = SystemColors.Window,
  179.                 ForeColor = SystemColors.WindowText,
  180.                 Font = SystemFonts.MessageBoxFont
  181.             };
  182.  
  183.             Controls.Add(_surface);
  184.  
  185.             // Требование: при клике по окну — скрыть
  186.             this.Click += (s, e) => RaiseRequestHide();
  187.             _surface.Click += (s, e) => RaiseRequestHide();
  188.         }
  189.  
  190.         private void RaiseRequestHide()
  191.         {
  192.             EventHandler handler = RequestHide;
  193.             if (handler != null) handler(this, EventArgs.Empty);
  194.         }
  195.  
  196.         // (C# 5) вместо "=> true"
  197.         protected override bool ShowWithoutActivation
  198.         {
  199.             get { return true; }
  200.         }
  201.  
  202.         protected override CreateParams CreateParams
  203.         {
  204.             get
  205.             {
  206.                 const int WS_EX_TOPMOST = 0x00000008;
  207.                 const int WS_EX_TOOLWINDOW = 0x00000080;
  208.                 const int WS_EX_NOACTIVATE = 0x08000000;
  209.  
  210.                 CreateParams cp = base.CreateParams;
  211.                 cp.ExStyle |= WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE;
  212.                 return cp;
  213.             }
  214.         }
  215.  
  216.         public void SetText(string text)
  217.         {
  218.             _surface.TextToDraw = text ?? "";
  219.             _surface.Invalidate();
  220.         }
  221.  
  222.         public void ShowInTopRight()
  223.         {
  224.             PositionTopRight();
  225.             if (!Visible) Show();
  226.             else TopMost = true;
  227.         }
  228.  
  229.         private void PositionTopRight()
  230.         {
  231.             Rectangle wa = Screen.PrimaryScreen.WorkingArea;
  232.  
  233.             int margin = ScaleByDpi(10);
  234.             int x = wa.Right - Width - margin;
  235.             int y = wa.Top + margin;
  236.  
  237.             Location = new Point(Math.Max(wa.Left, x), Math.Max(wa.Top, y));
  238.         }
  239.  
  240.         private int ScaleByDpi(int px)
  241.         {
  242.             using (Graphics g = CreateGraphics())
  243.             {
  244.                 return (int)Math.Round(px * (g.DpiX / 96.0));
  245.             }
  246.         }
  247.     }
  248.  
  249.     internal sealed class TextSurface : Control
  250.     {
  251.         private string _textToDraw;
  252.  
  253.         // (C# 5) вместо "auto-property initializer"
  254.         public string TextToDraw
  255.         {
  256.             get { return _textToDraw; }
  257.             set { _textToDraw = value ?? ""; }
  258.         }
  259.  
  260.         public TextSurface()
  261.         {
  262.             _textToDraw = "";
  263.  
  264.             SetStyle(ControlStyles.AllPaintingInWmPaint |
  265.                      ControlStyles.OptimizedDoubleBuffer |
  266.                      ControlStyles.ResizeRedraw |
  267.                      ControlStyles.UserPaint, true);
  268.             TabStop = false;
  269.         }
  270.  
  271.         protected override void OnPaint(PaintEventArgs e)
  272.         {
  273.             base.OnPaint(e);
  274.  
  275.             e.Graphics.Clear(BackColor);
  276.  
  277.             Rectangle rect = new Rectangle(
  278.                 Padding.Left,
  279.                 Padding.Top,
  280.                 Math.Max(0, ClientSize.Width - Padding.Horizontal),
  281.                 Math.Max(0, ClientSize.Height - Padding.Vertical)
  282.             );
  283.  
  284.             TextRenderer.DrawText(
  285.                 e.Graphics,
  286.                 TextToDraw ?? "",
  287.                 Font,
  288.                 rect,
  289.                 ForeColor,
  290.                 TextFormatFlags.WordBreak |
  291.                 TextFormatFlags.EndEllipsis |
  292.                 TextFormatFlags.NoPrefix |
  293.                 TextFormatFlags.Left |
  294.                 TextFormatFlags.VerticalCenter
  295.             );
  296.         }
  297.     }
  298. }
Advertisement
Add Comment
Please, Sign In to add comment