C# 桌面时钟(透明窗体、定时提醒、开机启动)
项目结构
DesktopClock/
├── Properties/
│ ├── AssemblyInfo.cs
│ └── Resources.resx
├── Forms/
│ ├── MainForm.cs
│ ├── MainForm.Designer.cs
│ ├── SettingsForm.cs
│ └── AlarmForm.cs
├── Classes/
│ ├── AlarmManager.cs
│ ├── RegistryHelper.cs
│ └── Win32API.cs
├── Icons/
│ ├── clock.ico
│ └── alarm.ico
└── Program.cs
代码实现
1. 主程序入口(Program.cs)
using System;
using System.Windows.Forms;
using DesktopClock.Properties;
namespace DesktopClock
{
internal static class Program
{
/// <summary>
/// 应用程序的主入口点
/// </summary>
[STAThread]
static void Main()
{
// 处理高DPI设置
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 检查是否已经运行
bool createdNew;
using (var mutex = new System.Threading.Mutex(true, "DesktopClock_UniqueMutex", out createdNew))
{
if (createdNew)
{
// 首次运行
LoadSettings();
// 启动主窗体
Application.Run(new MainForm());
}
else
{
// 程序已经在运行
MessageBox.Show("桌面时钟已经在运行中!", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
private static void LoadSettings()
{
// 加载用户设置
if (Settings.Default.UpgradeRequired)
{
Settings.Default.Upgrade();
Settings.Default.UpgradeRequired = false;
Settings.Default.Save();
}
}
}
}
2. 主窗体(MainForm.cs)
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
using DesktopClock.Classes;
using DesktopClock.Properties;
using System.Drawing.Drawing2D;
using System.Media;
namespace DesktopClock
{
public partial class MainForm : Form
{
// 私有字段
private bool isDragging = false;
private Point dragStartPoint;
private AlarmManager alarmManager;
private System.Windows.Forms.Timer updateTimer;
private System.Windows.Forms.Timer secondTimer;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;
// 窗体透明度级别
private float[] opacityLevels = { 0.3f, 0.5f, 0.7f, 0.9f, 1.0f };
private int currentOpacityIndex = 3; // 默认0.9
// 时钟样式
private ClockStyle currentStyle = ClockStyle.Digital;
private Font clockFont;
private Color clockColor = Color.Lime;
private Color shadowColor = Color.Black;
private bool showShadow = true;
private bool showSeconds = true;
private bool showDate = true;
private bool showWeek = true;
// 时钟位置
private FormPosition savedPosition = new FormPosition();
public MainForm()
{
InitializeComponent();
InitializeComponents();
LoadSettings();
InitializeTrayIcon();
// 启动时钟
StartClocks();
}
private void InitializeComponents()
{
// 设置窗体属性
this.FormBorderStyle = FormBorderStyle.None;
this.BackColor = Color.Black;
this.TransparencyKey = Color.Black;
this.TopMost = true;
this.ShowInTaskbar = false;
this.StartPosition = FormStartPosition.Manual;
// 设置窗体大小
this.ClientSize = new Size(300, 150);
// 创建定时器
updateTimer = new System.Windows.Forms.Timer();
updateTimer.Interval = 1000; // 1秒更新
updateTimer.Tick += UpdateTimer_Tick;
secondTimer = new System.Windows.Forms.Timer();
secondTimer.Interval = 100; // 0.1秒更新(用于秒动画)
secondTimer.Tick += SecondTimer_Tick;
// 创建闹钟管理器
alarmManager = new AlarmManager();
alarmManager.AlarmTriggered += AlarmManager_AlarmTriggered;
}
private void LoadSettings()
{
// 加载窗体位置
savedPosition = Settings.Default.FormPosition;
if (savedPosition != null && savedPosition.IsValid())
{
this.Location = savedPosition.Location;
this.Size = savedPosition.Size;
}
else
{
// 默认位置:屏幕右上角
this.Location = new Point(
Screen.PrimaryScreen.WorkingArea.Right - this.Width - 20,
20
);
}
// 加载透明度设置
currentOpacityIndex = Settings.Default.OpacityLevel;
if (currentOpacityIndex >= 0 && currentOpacityIndex < opacityLevels.Length)
{
this.Opacity = opacityLevels[currentOpacityIndex];
}
else
{
this.Opacity = 0.9f;
}
// 加载时钟样式
currentStyle = (ClockStyle)Settings.Default.ClockStyle;
showSeconds = Settings.Default.ShowSeconds;
showDate = Settings.Default.ShowDate;
showWeek = Settings.Default.ShowWeek;
clockColor = Settings.Default.ClockColor;
// 加载字体
try
{
clockFont = new Font(Settings.Default.FontName, Settings.Default.FontSize);
}
catch
{
clockFont = new Font("Arial", 24);
}
// 加载开机启动设置
UpdateStartupMenuItem();
}
private void SaveSettings()
{
Settings.Default.FormPosition = new FormPosition(this.Location, this.Size);
Settings.Default.OpacityLevel = currentOpacityIndex;
Settings.Default.ClockStyle = (int)currentStyle;
Settings.Default.ShowSeconds = showSeconds;
Settings.Default.ShowDate = showDate;
Settings.Default.ShowWeek = showWeek;
Settings.Default.ClockColor = clockColor;
Settings.Default.FontName = clockFont.Name;
Settings.Default.FontSize = clockFont.Size;
Settings.Default.Save();
}
private void InitializeTrayIcon()
{
// 创建托盘图标
trayIcon = new NotifyIcon();
trayIcon.Icon = Resources.clock_icon;
trayIcon.Text = "桌面时钟";
trayIcon.Visible = true;
trayIcon.MouseClick += TrayIcon_MouseClick;
// 创建托盘菜单
trayMenu = new ContextMenuStrip();
// 添加菜单项
var styleMenu = new ToolStripMenuItem("时钟样式");
styleMenu.DropDownItems.Add("数字时钟", null, (s, e) => ChangeClockStyle(ClockStyle.Digital));
styleMenu.DropDownItems.Add("模拟时钟", null, (s, e) => ChangeClockStyle(ClockStyle.Analog));
styleMenu.DropDownItems.Add("简约时钟", null, (s, e) => ChangeClockStyle(ClockStyle.Minimal));
trayMenu.Items.Add(styleMenu);
trayMenu.Items.Add(new ToolStripSeparator());
trayMenu.Items.Add("设置透明度", null, (s, e) => ChangeOpacity());
trayMenu.Items.Add("更改颜色...", null, (s, e) => ChangeColor());
trayMenu.Items.Add(new ToolStripSeparator());
trayMenu.Items.Add("显示/隐藏秒针", null, (s, e) => ToggleShowSeconds());
trayMenu.Items.Add("显示/隐藏日期", null, (s, e) => ToggleShowDate());
trayMenu.Items.Add("显示/隐藏星期", null, (s, e) => ToggleShowWeek());
trayMenu.Items.Add(new ToolStripSeparator());
// 闹钟管理
var alarmMenu = new ToolStripMenuItem("闹钟管理");
alarmMenu.DropDownItems.Add("添加闹钟...", null, (s, e) => AddAlarm());
alarmMenu.DropDownItems.Add("查看闹钟列表", null, (s, e) => ShowAlarms());
trayMenu.Items.Add(alarmMenu);
trayMenu.Items.Add(new ToolStripSeparator());
// 开机启动
startupMenuItem = new ToolStripMenuItem("开机启动");
startupMenuItem.Click += StartupMenuItem_Click;
trayMenu.Items.Add(startupMenuItem);
trayMenu.Items.Add(new ToolStripSeparator());
trayMenu.Items.Add("退出", null, (s, e) => ExitApplication());
trayIcon.ContextMenuStrip = trayMenu;
}
private ToolStripMenuItem startupMenuItem;
private void UpdateStartupMenuItem()
{
bool isStartup = RegistryHelper.IsAppSetToStartup("DesktopClock");
startupMenuItem.Checked = isStartup;
startupMenuItem.Text = isStartup ? "开机启动 ✓" : "开机启动";
}
private void StartClocks()
{
updateTimer.Start();
secondTimer.Start();
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
// 检查闹钟
alarmManager.CheckAlarms();
// 强制重绘
this.Invalidate();
}
private void SecondTimer_Tick(object sender, EventArgs e)
{
// 只用于模拟时钟的秒针动画
if (currentStyle == ClockStyle.Analog)
{
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
// 根据当前样式绘制时钟
switch (currentStyle)
{
case ClockStyle.Digital:
DrawDigitalClock(g);
break;
case ClockStyle.Analog:
DrawAnalogClock(g);
break;
case ClockStyle.Minimal:
DrawMinimalClock(g);
break;
}
}
private void DrawDigitalClock(Graphics g)
{
DateTime now = DateTime.Now;
// 准备文本
string timeText = now.ToString(showSeconds ? "HH:mm:ss" : "HH:mm");
string dateText = now.ToString("yyyy-MM-dd");
string weekText = GetChineseWeekDay(now.DayOfWeek);
// 计算文本大小
SizeF timeSize = g.MeasureString(timeText, clockFont);
SizeF dateSize = showDate ? g.MeasureString(dateText, clockFont) : SizeF.Empty;
SizeF weekSize = showWeek ? g.MeasureString(weekText, clockFont) : SizeF.Empty;
// 计算总高度
float totalHeight = timeSize.Height;
if (showDate) totalHeight += dateSize.Height + 5;
if (showWeek) totalHeight += weekSize.Height + 5;
// 计算开始位置(垂直居中)
float startY = (this.ClientSize.Height - totalHeight) / 2;
float currentY = startY;
// 绘制时间(带阴影效果)
if (showShadow)
{
DrawTextWithShadow(g, timeText, clockFont,
new PointF((this.ClientSize.Width - timeSize.Width) / 2, currentY),
clockColor, shadowColor, 2);
}
else
{
using (Brush brush = new SolidBrush(clockColor))
{
g.DrawString(timeText, clockFont, brush,
(this.ClientSize.Width - timeSize.Width) / 2, currentY);
}
}
currentY += timeSize.Height + 5;
// 绘制日期
if (showDate)
{
if (showShadow)
{
DrawTextWithShadow(g, dateText, clockFont,
new PointF((this.ClientSize.Width - dateSize.Width) / 2, currentY),
clockColor, shadowColor, 2);
}
else
{
using (Brush brush = new SolidBrush(clockColor))
{
g.DrawString(dateText, clockFont, brush,
(this.ClientSize.Width - dateSize.Width) / 2, currentY);
}
}
currentY += dateSize.Height + 5;
}
// 绘制星期
if (showWeek)
{
if (showShadow)
{
DrawTextWithShadow(g, weekText, clockFont,
new PointF((this.ClientSize.Width - weekSize.Width) / 2, currentY),
clockColor, shadowColor, 2);
}
else
{
using (Brush brush = new SolidBrush(clockColor))
{
g.DrawString(weekText, clockFont, brush,
(this.ClientSize.Width - weekSize.Width) / 2, currentY);
}
}
}
}
private void DrawAnalogClock(Graphics g)
{
DateTime now = DateTime.Now;
int centerX = this.ClientSize.Width / 2;
int centerY = this.ClientSize.Height / 2;
int radius = Math.Min(centerX, centerY) - 20;
// 绘制表盘
using (Pen pen = new Pen(clockColor, 2))
{
g.DrawEllipse(pen, centerX - radius, centerY - radius,
radius * 2, radius * 2);
}
// 绘制刻度
for (int i = 0; i < 60; i++)
{
double angle = i * 6 * Math.PI / 180;
int length = (i % 5 == 0) ? 10 : 5;
int width = (i % 5 == 0) ? 2 : 1;
int x1 = (int)(centerX + (radius - 5) * Math.Sin(angle));
int y1 = (int)(centerY - (radius - 5) * Math.Cos(angle));
int x2 = (int)(centerX + (radius - 5 - length) * Math.Sin(angle));
int y2 = (int)(centerY - (radius - 5 - length) * Math.Cos(angle));
using (Pen pen = new Pen(clockColor, width))
{
g.DrawLine(pen, x1, y1, x2, y2);
}
}
// 绘制数字
for (int i = 1; i <= 12; i++)
{
double angle = (i * 30 - 90) * Math.PI / 180;
int numberRadius = radius - 25;
int x = (int)(centerX + numberRadius * Math.Cos(angle));
int y = (int)(centerY + numberRadius * Math.Sin(angle));
string num = i.ToString();
SizeF size = g.MeasureString(num, new Font(clockFont.FontFamily, 12));
using (Brush brush = new SolidBrush(clockColor))
{
g.DrawString(num, new Font(clockFont.FontFamily, 12), brush,
x - size.Width / 2, y - size.Height / 2);
}
}
// 绘制时针
double hourAngle = (now.Hour % 12 + now.Minute / 60.0) * 30 * Math.PI / 180;
int hourHandLength = (int)(radius * 0.5);
int hourX = (int)(centerX + hourHandLength * Math.Sin(hourAngle));
int hourY = (int)(centerY - hourHandLength * Math.Cos(hourAngle));
using (Pen pen = new Pen(clockColor, 4))
{
g.DrawLine(pen, centerX, centerY, hourX, hourY);
}
// 绘制分针
double minuteAngle = (now.Minute + now.Second / 60.0) * 6 * Math.PI / 180;
int minuteHandLength = (int)(radius * 0.7);
int minuteX = (int)(centerX + minuteHandLength * Math.Sin(minuteAngle));
int minuteY = (int)(centerY - minuteHandLength * Math.Cos(minuteAngle));
using (Pen pen = new Pen(clockColor, 3))
{
g.DrawLine(pen, centerX, centerY, minuteX, minuteY);
}
// 绘制秒针(如果显示)
if (showSeconds)
{
double secondAngle = (now.Second + now.Millisecond / 1000.0) * 6 * Math.PI / 180;
int secondHandLength = (int)(radius * 0.8);
int secondX = (int)(centerX + secondHandLength * Math.Sin(secondAngle));
int secondY = (int)(centerY - secondHandLength * Math.Cos(secondAngle));
using (Pen pen = new Pen(Color.Red, 1))
{
g.DrawLine(pen, centerX, centerY, secondX, secondY);
}
// 绘制秒针尖端
using (Brush brush = new SolidBrush(Color.Red))
{
g.FillEllipse(brush, secondX - 3, secondY - 3, 6, 6);
}
}
// 绘制中心点
using (Brush brush = new SolidBrush(clockColor))
{
g.FillEllipse(brush, centerX - 4, centerY - 4, 8, 8);
}
// 绘制日期(如果显示)
if (showDate)
{
string dateText = now.ToString("MM/dd");
SizeF size = g.MeasureString(dateText, new Font(clockFont.FontFamily, 10));
using (Brush brush = new SolidBrush(clockColor))
{
g.DrawString(dateText, new Font(clockFont.FontFamily, 10), brush,
centerX - size.Width / 2, centerY + radius / 2);
}
}
}
private void DrawMinimalClock(Graphics g)
{
DateTime now = DateTime.Now;
// 只显示小时和分钟,使用大字体
string timeText = now.ToString("HH:mm");
using (Font largeFont = new Font(clockFont.FontFamily, 36, FontStyle.Bold))
{
SizeF timeSize = g.MeasureString(timeText, largeFont);
if (showShadow)
{
DrawTextWithShadow(g, timeText, largeFont,
new PointF((this.ClientSize.Width - timeSize.Width) / 2, 20),
clockColor, shadowColor, 3);
}
else
{
using (Brush brush = new SolidBrush(clockColor))
{
g.DrawString(timeText, largeFont, brush,
(this.ClientSize.Width - timeSize.Width) / 2, 20);
}
}
}
}
private void DrawTextWithShadow(Graphics g, string text, Font font,
PointF location, Color textColor, Color shadowColor, int shadowOffset)
{
// 绘制阴影
using (Brush shadowBrush = new SolidBrush(shadowColor))
{
g.DrawString(text, font, shadowBrush,
location.X + shadowOffset, location.Y + shadowOffset);
}
// 绘制文本
using (Brush textBrush = new SolidBrush(textColor))
{
g.DrawString(text, font, textBrush, location);
}
}
private string GetChineseWeekDay(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Sunday: return "星期日";
case DayOfWeek.Monday: return "星期一";
case DayOfWeek.Tuesday: return "星期二";
case DayOfWeek.Wednesday: return "星期三";
case DayOfWeek.Thursday: return "星期四";
case DayOfWeek.Friday: return "星期五";
case DayOfWeek.Saturday: return "星期六";
default: return "";
}
}
// 鼠标事件处理
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
isDragging = true;
dragStartPoint = new Point(e.X, e.Y);
this.Cursor = Cursors.SizeAll;
}
else if (e.Button == MouseButtons.Right)
{
// 显示上下文菜单
trayMenu.Show(this, e.Location);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (isDragging)
{
Point newLocation = this.Location;
newLocation.Offset(e.X - dragStartPoint.X, e.Y - dragStartPoint.Y);
this.Location = newLocation;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Left)
{
isDragging = false;
this.Cursor = Cursors.Default;
// 保存新位置
SaveSettings();
}
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
if (e.Button == MouseButtons.Left)
{
// 双击切换样式
currentStyle = (ClockStyle)(((int)currentStyle + 1) % 3);
this.Invalidate();
SaveSettings();
}
}
// 闹钟事件
private void AlarmManager_AlarmTriggered(object sender, AlarmEventArgs e)
{
// 在主线程中显示提醒
this.Invoke(new Action(() =>
{
ShowAlarmNotification(e.Alarm);
}));
}
private void ShowAlarmNotification(Alarm alarm)
{
// 播放提示音
SystemSounds.Exclamation.Play();
// 显示通知
if (trayIcon.Visible)
{
trayIcon.ShowBalloonTip(5000, "闹钟提醒",
$"{alarm.Name}\n{alarm.Time:HH:mm}", ToolTipIcon.Info);
}
// 显示提醒窗口
using (var alarmForm = new AlarmForm(alarm))
{
alarmForm.ShowDialog();
}
}
// 菜单功能
private void ChangeClockStyle(ClockStyle style)
{
currentStyle = style;
this.Invalidate();
SaveSettings();
}
private void ChangeOpacity()
{
currentOpacityIndex = (currentOpacityIndex + 1) % opacityLevels.Length;
this.Opacity = opacityLevels[currentOpacityIndex];
SaveSettings();
}
private void ChangeColor()
{
using (ColorDialog dialog = new ColorDialog())
{
dialog.Color = clockColor;
dialog.FullOpen = true;
if (dialog.ShowDialog() == DialogResult.OK)
{
clockColor = dialog.Color;
this.Invalidate();
SaveSettings();
}
}
}
private void ToggleShowSeconds()
{
showSeconds = !showSeconds;
SaveSettings();
this.Invalidate();
}
private void ToggleShowDate()
{
showDate = !showDate;
SaveSettings();
this.Invalidate();
}
private void ToggleShowWeek()
{
showWeek = !showWeek;
SaveSettings();
this.Invalidate();
}
private void AddAlarm()
{
using (var alarmForm = new AlarmForm())
{
if (alarmForm.ShowDialog() == DialogResult.OK)
{
alarmManager.AddAlarm(alarmForm.Alarm);
SaveAlarms();
}
}
}
private void ShowAlarms()
{
using (var alarmsForm = new AlarmsForm(alarmManager))
{
alarmsForm.ShowDialog();
SaveAlarms();
}
}
private void StartupMenuItem_Click(object sender, EventArgs e)
{
bool isStartup = RegistryHelper.IsAppSetToStartup("DesktopClock");
if (isStartup)
{
RegistryHelper.RemoveFromStartup("DesktopClock");
}
else
{
RegistryHelper.SetAppToStartup("DesktopClock", Application.ExecutablePath);
}
UpdateStartupMenuItem();
}
private void SaveAlarms()
{
Settings.Default.Alarms = alarmManager.SerializeAlarms();
Settings.Default.Save();
}
private void TrayIcon_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Visible = !this.Visible;
}
}
private void ExitApplication()
{
SaveSettings();
SaveAlarms();
updateTimer.Stop();
secondTimer.Stop();
trayIcon.Visible = false;
Application.Exit();
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Hide();
trayIcon.ShowBalloonTip(1000, "桌面时钟", "时钟已隐藏到系统托盘", ToolTipIcon.Info);
}
base.OnFormClosing(e);
}
// 时钟样式枚举
private enum ClockStyle
{
Digital = 0,
Analog = 1,
Minimal = 2
}
}
// 窗体位置序列化类
[Serializable]
public class FormPosition
{
public Point Location { get; set; }
public Size Size { get; set; }
public FormPosition() { }
public FormPosition(Point location, Size size)
{
Location = location;
Size = size;
}
public bool IsValid()
{
return Location.X >= 0 && Location.Y >= 0 &&
Size.Width > 0 && Size.Height > 0;
}
}
}
3. 主窗体设计器(MainForm.Designer.cs)
namespace DesktopClock
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(300, 150);
this.Name = "MainForm";
this.Text = "桌面时钟";
}
#endregion
}
}
4. 闹钟管理器(AlarmManager.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using System.IO;
namespace DesktopClock.Classes
{
public class AlarmManager
{
private List<Alarm> alarms = new List<Alarm>();
private System.Windows.Forms.Timer checkTimer;
public event EventHandler<AlarmEventArgs> AlarmTriggered;
public AlarmManager()
{
checkTimer = new System.Windows.Forms.Timer();
checkTimer.Interval = 1000; // 每秒检查一次
checkTimer.Tick += CheckTimer_Tick;
checkTimer.Start();
}
public void AddAlarm(Alarm alarm)
{
alarms.Add(alarm);
}
public void RemoveAlarm(Alarm alarm)
{
alarms.Remove(alarm);
}
public List<Alarm> GetAlarms()
{
return new List<Alarm>(alarms);
}
public void CheckAlarms()
{
DateTime now = DateTime.Now;
foreach (var alarm in alarms.Where(a => a.IsEnabled))
{
if (alarm.ShouldTrigger(now))
{
OnAlarmTriggered(alarm);
if (!alarm.Repeat)
{
alarm.IsEnabled = false;
}
}
}
}
private void CheckTimer_Tick(object sender, EventArgs e)
{
CheckAlarms();
}
protected virtual void OnAlarmTriggered(Alarm alarm)
{
AlarmTriggered?.Invoke(this, new AlarmEventArgs(alarm));
}
public string SerializeAlarms()
{
try
{
var serializer = new XmlSerializer(typeof(List<Alarm>));
using (var writer = new StringWriter())
{
serializer.Serialize(writer, alarms);
return writer.ToString();
}
}
catch
{
return string.Empty;
}
}
public void DeserializeAlarms(string xml)
{
try
{
if (!string.IsNullOrEmpty(xml))
{
var serializer = new XmlSerializer(typeof(List<Alarm>));
using (var reader = new StringReader(xml))
{
var deserialized = (List<Alarm>)serializer.Deserialize(reader);
alarms = deserialized ?? new List<Alarm>();
}
}
}
catch
{
alarms = new List<Alarm>();
}
}
}
[Serializable]
public class Alarm
{
public string Name { get; set; }
public DateTime Time { get; set; }
public bool IsEnabled { get; set; }
public bool Repeat { get; set; }
public DayOfWeek[] RepeatDays { get; set; }
public DateTime LastTriggered { get; set; }
public Alarm()
{
Name = "新闹钟";
Time = DateTime.Now;
IsEnabled = true;
Repeat = false;
RepeatDays = new DayOfWeek[0];
}
public bool ShouldTrigger(DateTime currentTime)
{
if (!IsEnabled) return false;
// 检查是否在触发时间范围内(允许±1分钟)
if (Math.Abs((currentTime - Time).TotalMinutes) <= 1)
{
// 防止重复触发(同一分钟内只触发一次)
if ((currentTime - LastTriggered).TotalMinutes < 1)
return false;
if (Repeat)
{
if (RepeatDays.Length == 0 || RepeatDays.Contains(currentTime.DayOfWeek))
{
LastTriggered = currentTime;
return true;
}
}
else
{
LastTriggered = currentTime;
return true;
}
}
return false;
}
}
public class AlarmEventArgs : EventArgs
{
public Alarm Alarm { get; }
public AlarmEventArgs(Alarm alarm)
{
Alarm = alarm;
}
}
}
5. 闹钟设置窗体(AlarmForm.cs)
using System;
using System.Windows.Forms;
using DesktopClock.Classes;
namespace DesktopClock
{
public partial class AlarmForm : Form
{
public Alarm Alarm { get; private set; }
private CheckBox[] dayCheckboxes = new CheckBox[7];
private string[] dayNames = { "周一", "周二", "周三", "周四", "周五", "周六", "周日" };
public AlarmForm() : this(null) { }
public AlarmForm(Alarm existingAlarm)
{
InitializeComponent();
InitializeDayCheckboxes();
if (existingAlarm != null)
{
Alarm = existingAlarm;
LoadAlarmData();
}
else
{
Alarm = new Alarm();
timePicker.Value = DateTime.Now.AddMinutes(1);
}
}
private void InitializeComponent()
{
this.Text = "设置闹钟";
this.Size = new System.Drawing.Size(350, 300);
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// 名称标签
var nameLabel = new Label
{
Text = "闹钟名称:",
Location = new System.Drawing.Point(20, 20),
Size = new System.Drawing.Size(80, 20)
};
// 名称文本框
var nameTextBox = new TextBox
{
Location = new System.Drawing.Point(100, 20),
Size = new System.Drawing.Size(200, 20)
};
nameTextBox.TextChanged += (s, e) => Alarm.Name = nameTextBox.Text;
// 时间标签
var timeLabel = new Label
{
Text = "提醒时间:",
Location = new System.Drawing.Point(20, 50),
Size = new System.Drawing.Size(80, 20)
};
// 时间选择器
timePicker = new DateTimePicker
{
Location = new System.Drawing.Point(100, 50),
Size = new System.Drawing.Size(100, 20),
Format = DateTimePickerFormat.Time,
ShowUpDown = true
};
timePicker.ValueChanged += (s, e) => Alarm.Time = timePicker.Value;
// 重复复选框
var repeatCheckBox = new CheckBox
{
Text = "重复",
Location = new System.Drawing.Point(20, 80),
Size = new System.Drawing.Size(60, 20)
};
repeatCheckBox.CheckedChanged += (s, e) =>
{
Alarm.Repeat = repeatCheckBox.Checked;
panelDays.Enabled = repeatCheckBox.Checked;
};
// 重复日期面板
panelDays = new Panel
{
Location = new System.Drawing.Point(20, 110),
Size = new System.Drawing.Size(300, 60),
Enabled = false
};
// 启用复选框
var enabledCheckBox = new CheckBox
{
Text = "启用闹钟",
Location = new System.Drawing.Point(20, 180),
Size = new System.Drawing.Size(100, 20),
Checked = true
};
enabledCheckBox.CheckedChanged += (s, e) => Alarm.IsEnabled = enabledCheckBox.Checked;
// 按钮
var okButton = new Button
{
Text = "确定",
Location = new System.Drawing.Point(150, 220),
Size = new System.Drawing.Size(80, 30),
DialogResult = DialogResult.OK
};
var cancelButton = new Button
{
Text = "取消",
Location = new System.Drawing.Point(240, 220),
Size = new System.Drawing.Size(80, 30),
DialogResult = DialogResult.Cancel
};
// 添加控件
this.Controls.AddRange(new Control[]
{
nameLabel, nameTextBox, timeLabel, timePicker,
repeatCheckBox, panelDays, enabledCheckBox,
okButton, cancelButton
});
this.AcceptButton = okButton;
this.CancelButton = cancelButton;
}
private void InitializeDayCheckboxes()
{
for (int i = 0; i < 7; i++)
{
dayCheckboxes[i] = new CheckBox
{
Text = dayNames[i],
Location = new System.Drawing.Point(i * 40, 10),
Size = new System.Drawing.Size(40, 20),
Tag = (DayOfWeek)((i + 1) % 7) // 周日是0,调整为最后
};
int index = i; // 捕获变量
dayCheckboxes[i].CheckedChanged += (s, e) =>
{
UpdateRepeatDays();
};
panelDays.Controls.Add(dayCheckboxes[i]);
}
}
private void LoadAlarmData()
{
// 查找并设置控件
foreach (Control ctrl in this.Controls)
{
if (ctrl is TextBox && ctrl.Name.Contains("name"))
{
((TextBox)ctrl).Text = Alarm.Name;
}
else if (ctrl is DateTimePicker)
{
((DateTimePicker)ctrl).Value = Alarm.Time;
}
else if (ctrl is CheckBox checkBox)
{
if (checkBox.Text == "重复")
{
checkBox.Checked = Alarm.Repeat;
}
else if (checkBox.Text == "启用闹钟")
{
checkBox.Checked = Alarm.IsEnabled;
}
}
}
// 设置重复日期
if (Alarm.RepeatDays != null)
{
foreach (var checkbox in dayCheckboxes)
{
checkbox.Checked = Alarm.RepeatDays.Contains((DayOfWeek)checkbox.Tag);
}
}
}
private void UpdateRepeatDays()
{
var selectedDays = new List<DayOfWeek>();
foreach (var checkbox in dayCheckboxes)
{
if (checkbox.Checked)
{
selectedDays.Add((DayOfWeek)checkbox.Tag);
}
}
Alarm.RepeatDays = selectedDays.ToArray();
}
private DateTimePicker timePicker;
private Panel panelDays;
}
}
参考代码 C# 桌面时钟(透明窗体,定时提醒,开机启动) www.youwenfan.com/contentcnt/49380.html
6. 闹钟列表窗体(AlarmsForm.cs)
using System;
using System.Windows.Forms;
using DesktopClock.Classes;
using System.Drawing;
namespace DesktopClock
{
public partial class AlarmsForm : Form
{
private AlarmManager alarmManager;
private ListView listView;
public AlarmsForm(AlarmManager manager)
{
alarmManager = manager;
InitializeComponent();
LoadAlarms();
}
private void InitializeComponent()
{
this.Text = "闹钟列表";
this.Size = new Size(500, 300);
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// 列表视图
listView = new ListView
{
View = View.Details,
FullRowSelect = true,
GridLines = true,
Location = new Point(10, 10),
Size = new Size(460, 200)
};
// 添加列
listView.Columns.Add("名称", 150);
listView.Columns.Add("时间", 100);
listView.Columns.Add("重复", 80);
listView.Columns.Add("状态", 80);
listView.Columns.Add("重复日期", 150);
// 按钮
var addButton = new Button
{
Text = "添加",
Location = new Point(10, 220),
Size = new Size(80, 30)
};
addButton.Click += AddButton_Click;
var editButton = new Button
{
Text = "编辑",
Location = new Point(100, 220),
Size = new Size(80, 30)
};
editButton.Click += EditButton_Click;
var deleteButton = new Button
{
Text = "删除",
Location = new Point(190, 220),
Size = new Size(80, 30)
};
deleteButton.Click += DeleteButton_Click;
var toggleButton = new Button
{
Text = "启用/禁用",
Location = new Point(280, 220),
Size = new Size(80, 30)
};
toggleButton.Click += ToggleButton_Click;
var closeButton = new Button
{
Text = "关闭",
Location = new Point(370, 220),
Size = new Size(80, 30),
DialogResult = DialogResult.Cancel
};
this.Controls.AddRange(new Control[]
{
listView, addButton, editButton,
deleteButton, toggleButton, closeButton
});
this.CancelButton = closeButton;
}
private void LoadAlarms()
{
listView.Items.Clear();
foreach (var alarm in alarmManager.GetAlarms())
{
var item = new ListViewItem(alarm.Name);
item.SubItems.Add(alarm.Time.ToString("HH:mm"));
item.SubItems.Add(alarm.Repeat ? "是" : "否");
item.SubItems.Add(alarm.IsEnabled ? "启用" : "禁用");
string daysText = "";
if (alarm.RepeatDays != null && alarm.RepeatDays.Length > 0)
{
foreach (var day in alarm.RepeatDays)
{
daysText += GetDayAbbreviation(day) + " ";
}
}
item.SubItems.Add(daysText);
item.Tag = alarm;
listView.Items.Add(item);
}
}
private string GetDayAbbreviation(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Sunday: return "日";
case DayOfWeek.Monday: return "一";
case DayOfWeek.Tuesday: return "二";
case DayOfWeek.Wednesday: return "三";
case DayOfWeek.Thursday: return "四";
case DayOfWeek.Friday: return "五";
case DayOfWeek.Saturday: return "六";
default: return "";
}
}
private void AddButton_Click(object sender, EventArgs e)
{
using (var alarmForm = new AlarmForm())
{
if (alarmForm.ShowDialog() == DialogResult.OK)
{
alarmManager.AddAlarm(alarmForm.Alarm);
LoadAlarms();
}
}
}
private void EditButton_Click(object sender, EventArgs e)
{
if (listView.SelectedItems.Count > 0)
{
var alarm = (Alarm)listView.SelectedItems[0].Tag;
using (var alarmForm = new AlarmForm(alarm))
{
if (alarmForm.ShowDialog() == DialogResult.OK)
{
LoadAlarms();
}
}
}
}
private void DeleteButton_Click(object sender, EventArgs e)
{
if (listView.SelectedItems.Count > 0)
{
if (MessageBox.Show("确定要删除这个闹钟吗?", "确认删除",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
var alarm = (Alarm)listView.SelectedItems[0].Tag;
alarmManager.RemoveAlarm(alarm);
LoadAlarms();
}
}
}
private void ToggleButton_Click(object sender, EventArgs e)
{
if (listView.SelectedItems.Count > 0)
{
var alarm = (Alarm)listView.SelectedItems[0].Tag;
alarm.IsEnabled = !alarm.IsEnabled;
LoadAlarms();
}
}
}
}
7. 注册表助手(RegistryHelper.cs)
using System;
using Microsoft.Win32;
using System.Security;
using System.Windows.Forms;
namespace DesktopClock.Classes
{
public static class RegistryHelper
{
private const string RUN_REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
public static bool SetAppToStartup(string appName, string appPath)
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RUN_REGISTRY_KEY, true))
{
if (key != null)
{
key.SetValue(appName, $"\"{appPath}\"");
return true;
}
}
}
catch (SecurityException ex)
{
MessageBox.Show($"权限不足: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show($"访问被拒绝: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show($"设置失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return false;
}
public static bool RemoveFromStartup(string appName)
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RUN_REGISTRY_KEY, true))
{
if (key != null)
{
key.DeleteValue(appName, false);
return true;
}
}
}
catch (Exception ex)
{
MessageBox.Show($"删除失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return false;
}
public static bool IsAppSetToStartup(string appName)
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RUN_REGISTRY_KEY, false))
{
if (key != null)
{
return key.GetValue(appName) != null;
}
}
}
catch
{
// 忽略错误
}
return false;
}
public static string GetStartupAppPath(string appName)
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RUN_REGISTRY_KEY, false))
{
if (key != null)
{
return key.GetValue(appName)?.ToString();
}
}
}
catch
{
// 忽略错误
}
return null;
}
}
}
8. 设置类(Settings.cs)
using System;
using System.Drawing;
using System.Configuration;
namespace DesktopClock.Properties
{
internal sealed partial class Settings : ApplicationSettingsBase
{
private static Settings defaultInstance = (Settings)Synchronized(new Settings());
public static Settings Default
{
get { return defaultInstance; }
}
[UserScopedSetting]
[DefaultSettingValue("true")]
public bool UpgradeRequired
{
get { return (bool)this["UpgradeRequired"]; }
set { this["UpgradeRequired"] = value; }
}
[UserScopedSetting]
public FormPosition FormPosition
{
get
{
var value = this["FormPosition"];
return value as FormPosition ?? new FormPosition();
}
set { this["FormPosition"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("3")]
public int OpacityLevel
{
get { return (int)this["OpacityLevel"]; }
set { this["OpacityLevel"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("0")]
public int ClockStyle
{
get { return (int)this["ClockStyle"]; }
set { this["ClockStyle"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("true")]
public bool ShowSeconds
{
get { return (bool)this["ShowSeconds"]; }
set { this["ShowSeconds"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("true")]
public bool ShowDate
{
get { return (bool)this["ShowDate"]; }
set { this["ShowDate"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("true")]
public bool ShowWeek
{
get { return (bool)this["ShowWeek"]; }
set { this["ShowWeek"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("Lime")]
public Color ClockColor
{
get { return (Color)this["ClockColor"]; }
set { this["ClockColor"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("Arial")]
public string FontName
{
get { return (string)this["FontName"]; }
set { this["FontName"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("24")]
public float FontSize
{
get { return (float)this["FontSize"]; }
set { this["FontSize"] = value; }
}
[UserScopedSetting]
[DefaultSettingValue("")]
public string Alarms
{
get { return (string)this["Alarms"]; }
set { this["Alarms"] = value; }
}
}
}
9. 项目配置文件(App.config)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings"
type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="DesktopClock.Properties.Settings"
type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<userSettings>
<DesktopClock.Properties.Settings>
<setting name="UpgradeRequired" serializeAs="String">
<value>True</value>
</setting>
<setting name="FormPosition" serializeAs="String">
<value />
</setting>
<setting name="OpacityLevel" serializeAs="String">
<value>3</value>
</setting>
<setting name="ClockStyle" serializeAs="String">
<value>0</value>
</setting>
<setting name="ShowSeconds" serializeAs="String">
<value>True</value>
</setting>
<setting name="ShowDate" serializeAs="String">
<value>True</value>
</setting>
<setting name="ShowWeek" serializeAs="String">
<value>True</value>
</setting>
<setting name="ClockColor" serializeAs="String">
<value>Lime</value>
</setting>
<setting name="FontName" serializeAs="String">
<value>Arial</value>
</setting>
<setting name="FontSize" serializeAs="String">
<value>24</value>
</setting>
<setting name="Alarms" serializeAs="String">
<value />
</setting>
</DesktopClock.Properties.Settings>
</userSettings>
</configuration>
使用说明
1. 编译和运行
# 使用Visual Studio
1. 打开DesktopClock.sln
2. 编译项目 (F6)
3. 运行 (F5)
# 使用命令行
msbuild DesktopClock.csproj
DesktopClock.exe
2. 功能操作
| 操作 | 功能 |
|---|---|
| 左键拖拽 | 移动时钟位置 |
| 右键单击 | 显示设置菜单 |
| 双击时钟 | 切换时钟样式 |
| 托盘图标右键 | 显示完整菜单 |
| 托盘图标左键 | 显示/隐藏时钟 |
3. 时钟样式
- 数字时钟:显示时间、日期、星期
- 模拟时钟:传统表盘样式
- 简约时钟:只显示小时和分钟
4. 闹钟功能
- 支持单次和重复闹钟
- 可设置重复日期
- 闹钟触发时显示提示和播放声音
- 支持系统托盘通知
配置说明
1. 透明度级别
// 5个级别:30%, 50%, 70%, 90%, 100%
private float[] opacityLevels = { 0.3f, 0.5f, 0.7f, 0.9f, 1.0f };
2. 开机启动
- 通过修改注册表实现
- 路径:
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run - 键名:
DesktopClock
3. 数据存储
- 设置存储在:
%LOCALAPPDATA%\DesktopClock - 闹钟数据序列化为XML格式
扩展功能建议
- 天气显示:集成天气API显示实时天气
- 倒计时:添加倒计时功能
- 世界时钟:显示多个时区时间
- 屏幕边缘吸附:自动吸附到屏幕边缘
- 主题皮肤:支持多种主题颜色
- 语音报时:整点语音播报
- 快捷键:自定义快捷键操作