前言
前几天做了个桌面宠物,将效果和代码分享一下。
效果预览
贴图素材出自《侠客风云传》
1.拥有待机动画,呼吸效果,轮播台词。
2.可以拖动,拖动有交互反馈。
3.可以随时切换贴图,更改台词,切换呼吸频率。
实现技术与控件
- Winform窗体
- 文件流读写
- 多线程与定时器
- 鼠标绑定事件
- 坐标系更新与随机数生成
- PictureBox控件,contextMenuStrip控件,ToolTip控件
- 额外的小功能,锁屏,锁定键盘,用WindowsAPI实现
具体实现
窗体代码
using DeskPet.Properties;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DeskPet
{
public partial class Form1 : Form
{
//键盘操作类
KeyBroadClass KeyBroadBlock = new KeyBroadClass();
bool IsBlock = false;//锁定键盘标志位
//基本参数
private bool IsMouseDown;//是否点击鼠标
private Point MousePoint;//鼠标坐标
public int duration = 5000;//时间间隔
public int Opreation_flag=0;//操作动作标志
public int Duration
{
get
{
return duration;
}
set
{
duration = value;
toolTipTimer.Interval = duration;
}
}
public bool IsSettingOpen = false;//设置窗打开时不弹台词
public string charaterName = "东方未明";//角色名字
public string CharaterName
{
get
{
return charaterName;
}
set
{
charaterName = value;
textform.UserName = value;
}
}
//随机切换表情
private Random random = new Random();
private Image Curimage;//当前表情
private string Curimage_name;//当前表情文件名
private bool IsChanging = false;//防止冲突
//文字集合
public string[] tootipText = { "哎哟~你干嘛~~", "这是什么武功?", "痛啊,别拖我啦!" };
public string[] TimerText = { "真是无聊啊~", "努力!加油!终有一天,我也可以成为武林盟主!", "努力修行的一天!" };
public string[] StarText = { "少侠,好久不见!" };
private string[] TouchText = { "喂!别乱来啊!你在碰哪呢?", "你是在吃我豆腐吗?","拖就拖,别碰我那里!" };
string CurrentText = "";//当前文字
//定时器
private System.Windows.Forms.Timer toolTipTimer;
//判断窗体是否失去焦点
private bool IsDeactivate = false;
//对话窗
TextForm textform;
//贴图设置
public int Breath_Size_nums = 4; //呼吸幅度
public int Breath_Fre_nums = 200; //呼吸频率
Thread Pic_Bre;
public bool isBreath = true;//是否呼吸
bool IsFirstLoad = false;//第一次加载
public string imageFold = $@"{Environment.CurrentDirectory}/image/小师弟";//加载贴图的文件夹
public string ImageFold
{
get
{
return imageFold;
}
set
{
imageFold = value;
LoadNewImage(imageFold);
}
}
// 获取指定目录中所有的 PNG 图片文件路径
string[] imageFiles { get; set; }
public bool IsBreath
{
get
{
return isBreath;
}
set
{
isBreath = value;
if (isBreath && !IsFirstLoad && Pic_Bre == null)
{
Pic_Bre = new Thread(PicBreath);
Pic_Bre.IsBackground = true;
Pic_Bre.Start();
}
IsFirstLoad = false;
}
}
private int old_height;
private int old_top;
public Form1()
{
InitializeComponent();
// 创建计时器
toolTipTimer = new System.Windows.Forms.Timer();
toolTipTimer.Interval = duration; // 设置为5分钟的毫秒数
toolTipTimer.Tick += ToolTipTimer_Tick;
// 启动计时器
toolTipTimer.Start();
//保持前置
TopMost = true;
//隐藏任务栏
ShowInTaskbar = false;
//打开对话窗体
textform = new TextForm(2000, charaterName);
//textform.Visible = false;
textform.Show();
textform.Visible = false;
}
/// <summary>
/// 鼠标点下时切换表情,并且记录鼠标坐标
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MouseDown_Event(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
//随机切换表情
ChangePictureBoxImage();
IsMouseDown = true;
MousePoint = Cursor.Position;
//切换文字提示
int TextIndex = ChangeToopTipText(tootipText);
CurrentText = tootipText[TextIndex];
}
}
/// <summary>
/// 鼠标松开时的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MouseUp_Event(object sender, MouseEventArgs e)
{
IsMouseDown = false;
pictureBox1.Image = Curimage;
}
/// <summary>
/// 鼠标移动时的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MouseMove_Event(object sender, MouseEventArgs e)
{
if (IsMouseDown && e.Button == MouseButtons.Left)
{
Point snap = Cursor.Position;
this.Location = new Point(Location.X + (snap.X - MousePoint.X), Location.Y + (snap.Y - MousePoint.Y));
textform.Location = new Point(Location.X, Location.Y - 100);
MousePoint = Cursor.Position;
// 更新ToolTip的位置,使其跟随PictureBox的上方
Point toolTipLocation = new Point(pictureBox1.Location.X + 180, pictureBox1.Location.Y + 200);
toolTip1.Show(CurrentText, pictureBox1, toolTipLocation, 500);
}
}
/// <summary>
/// 窗口透明化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
//初始化贴图
LoadNewImage(imageFold);
this.BackColor = Color.FromArgb(0, 0, 1);
this.TransparencyKey = this.BackColor;
//初始化
Curimage_name = imageFiles[0];
Image randomImage = Image.FromFile(Curimage_name);
Curimage = randomImage;
// 获取程序执行的目录
string programDirectory = Environment.CurrentDirectory;
//拖动时的文字文件
string La_Text_configFilePath = Path.Combine(programDirectory, "La_Text_Config.txt");
//定时弹出的文字文件
string Timer_Text_configFilePath = Path.Combine(programDirectory, "Timer_Text_Config.txt");
//开始时的文字文件
string Start_Text_configFilePath = Path.Combine(programDirectory, "Start_Text_Config.txt");
//配置文件
string ConfigText_configFilePath = Path.Combine(programDirectory, "ConfigText.txt");
//解析文本
string[] res = ParseTextFile(La_Text_configFilePath);
if (res != null)
{
tootipText = res;
}
res = ParseTextFile(Timer_Text_configFilePath);
if (res != null)
{
TimerText = res;
}
res = ParseTextFile(Start_Text_configFilePath);
if (res != null)
{
StarText = res;
}
res = ParseTextFile(ConfigText_configFilePath);
if (res != null)
{
try
{
CharaterName = res[0]?.ToString();//角色名
int.TryParse(res[1]?.ToString(), out duration);
int.TryParse(res[2]?.ToString(), out Breath_Size_nums);
int.TryParse(res[3]?.ToString(), out Breath_Fre_nums);
IsFirstLoad = true;
bool _isBreath = res[4]?.ToString() == "1" ? true : false;
IsBreath = _isBreath;
ImageFold = res[5]?.ToString();//贴图默认路径
}
catch
{
}
//开机自启动
}
//放置在右下角
int screenWidth = Screen.PrimaryScreen.Bounds.Width;
int screenHeight = Screen.PrimaryScreen.Bounds.Height;
this.Location = new Point(screenWidth - this.Width - 70, screenHeight - this.Height - 40);
//第一次加载问候
ChangePictureBoxImage();
Point toolTipLocation = new Point(pictureBox1.Location.X + 180, pictureBox1.Location.Y + 200);
toolTip1.Show(StarText[ChangeToopTipText(StarText)], pictureBox1, toolTipLocation, 3000);
//赋值
old_height = Height;
old_top = Top;
//呼吸效果
if (Pic_Bre == null)
{
Pic_Bre = new Thread(PicBreath);
Pic_Bre.IsBackground = true;
Pic_Bre.Start();
}
}
/// <summary>
/// 随机切换表情
/// </summary>
private void ChangePictureBoxImage()
{
if (IsChanging)
{
return;
}
IsChanging = true;
// 如果没有找到任何图片文件,则直接返回
if (imageFiles.Length == 0)
{
IsChanging = false;
return;
}
// 生成一个随机数,范围为图片文件的数量
int randomImageIndex;
do
{
randomImageIndex = random.Next(imageFiles.Length);
} while (imageFiles[randomImageIndex] == Curimage_name);
// 获取随机选择的图片路径并读取图片数据
PutOnPic(randomImageIndex);
IsChanging = false;
}
/// <summary>
/// 改变文字提示
/// </summary>
private int ChangeToopTipText(string[] textList)
{
int TextCount = textList.Count();
// 生成一个随机数,范围为文字集合
int TextIndex = random.Next(TextCount);
return TextIndex;
}
/// <summary>
/// 定时器任务
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ToolTipTimer_Tick(object sender, EventArgs e)
{
// 在计时器触发时,显示ToolTip
if (!IsMouseDown && Visible)
{
Point toolTipLocation = new Point(pictureBox1.Location.X + 180, pictureBox1.Location.Y + 200);
int TextIndex = ChangeToopTipText(TimerText);
// 显示ToolTip
if (IsDeactivate && !IsSettingOpen)
{
textform.Location = new Point(Location.X, Location.Y - 100);
textform.CurrentText = TimerText[TextIndex];
}
else
{
toolTip1.Show(TimerText[TextIndex], pictureBox1, toolTipLocation, 2000);
}
ChangePictureBoxImage();
}
}
/// <summary>
/// 转换文本
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static string[] ParseTextFile(string filePath)
{
// 检查文件是否存在
if (!File.Exists(filePath))
{
return null;
}
try
{
// 从文本文件中读取所有行
string[] lines = File.ReadAllLines(filePath);
return lines;
}
catch
{
// 处理文件读取错误
//Console.WriteLine($"An error occurred while reading the file: {e.Message}");
return null; // 返回空数组或者抛出异常,具体取决于你的需求
}
}
/// <summary>
/// 失去焦点时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Deactivate(object sender, EventArgs e)
{
if (WindowState != FormWindowState.Minimized)
{
IsDeactivate = true;
}
}
/// <summary>
/// 激活窗体时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Activated(object sender, EventArgs e)
{
IsDeactivate = false;
}
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
ControlVisble();
}
/// <summary>
/// 控制显示隐藏
/// </summary>
private void ControlVisble()
{
if (Visible)
{
Visible = false;
}
else
{
Visible = true;
}
}
private void VisbleToolStripMenuItem_Click(object sender, EventArgs e)
{
ControlVisble();
}
/// <summary>
/// 退出
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
{
notifyIcon1.Dispose();
Environment.Exit(0);
}
/// <summary>
/// 打开设置页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SettingToolStripMenuItem_Click(object sender, EventArgs e)
{
IsSettingOpen = true;
SettingForm setting = new SettingForm(this);
setting.TopMost = true;
setting.StartPosition = FormStartPosition.CenterScreen;
setting.ShowDialog();
}
/// <summary>
/// 关于页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("关于页!");
}
/// <summary>
/// 呼吸效果
/// </summary>
private void PicBreath()
{
while (IsBreath)
{
if (!IsBreath)
{
break;
}
if (pictureBox1.Tag == null || pictureBox1.Tag.ToString() == "breathingIn")
{
pictureBox1.Tag = "breathingOut";
for (int i = 0; i < Breath_Size_nums; i++)
{
int newHeight = Height + i; // 修改为您想要的高度
int newTop = Top - i;
this.Invoke(new Action(() =>
{
Height = newHeight;
Top = newTop;
}));
Thread.Sleep(Breath_Fre_nums);
}
}
else
{
pictureBox1.Tag = "breathingIn";
for (int i = 0; i < Breath_Size_nums; i++)
{
int newHeight = Height - i; // 修改为您想要的高度
int newTop = Top + i;
this.Invoke(new Action(() =>
{
Height = newHeight;
Top = newTop;
}));
Thread.Sleep(Breath_Fre_nums);
}
}
Thread.Sleep(Breath_Fre_nums);
}
this.Invoke(new Action(() =>
{
Height = old_height;
Top = old_top;
}));
Pic_Bre = null;
}
/// <summary>
/// 读取新图片
/// </summary>
/// <param name="PathName"></param>
private void LoadNewImage(string PathName)
{
string imageDirectory = PathName;
try
{
imageFiles = Directory.GetFiles(imageDirectory, "*.*")
.Where(file => file.EndsWith(".jpg") || file.EndsWith(".jpeg") || file.EndsWith(".png") || file.EndsWith(".gif") || file.EndsWith(".bmp"))
.ToArray();
}
catch
{
string Dic = Environment.CurrentDirectory + @"/image";
List<string> Folder = new List<string>();
foreach (var item in Directory.GetDirectories(Dic))
{
Folder.Add(item);
}
if (Folder.Count == 0)
{
MessageBox.Show("无法获取图片文件夹,请检查配置文件及图片文件夹路径下的文件夹!", "菊菊拿不到图片");
Close();
}
imageFiles = Directory.GetFiles(Folder[0], "*.*")
.Where(file => file.EndsWith(".jpg") || file.EndsWith(".jpeg") || file.EndsWith(".png") || file.EndsWith(".gif") || file.EndsWith(".bmp"))
.ToArray();
}
//加载图片
PutOnPic(0);
}
/// <summary>
/// 加载图片
/// </summary>
private void PutOnPic(int nums)
{
try
{
string randomImagePath = imageFiles[nums];
Image randomImage = Image.FromFile(randomImagePath);
//randomImage = ProcessGifBackground(randomImagePath);
pictureBox1.Image = randomImage;
// 设置 PictureBox 的 Image 属性为随机选择的图片
string MorenPic = imageFiles[0];
randomImage = Image.FromFile(MorenPic);
//randomImage = ProcessGifBackground(randomImagePath);
Curimage = randomImage;
Curimage_name = imageFiles[0];
}
catch (Exception ex)
{
MessageBox.Show($"加载贴图失败!原因是{ex.ToString()}", "菊菊很无奈");
}
}
/// <summary>
/// 锁定键盘
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LockInputIBtn_Click(object sender, EventArgs e)
{
// 锁定键盘
if (!IsBlock)
{
if (KeyBroadBlock.LockKeyboard())
{
MessageBox.Show("咋瓦鲁多!键盘已锁定。", "菊菊提示");
IsBlock = true;
}
else
{
MessageBox.Show("键盘锁定失败。", "菊菊提示");
IsBlock = false;
}
}
else
{
if (KeyBroadBlock.UnlockKeyboard())
{
MessageBox.Show("时间开始流逝……键盘解锁。", "菊菊提示");
IsBlock = false;
}
else
{
MessageBox.Show("键盘解锁失败。", "菊菊提示");
IsBlock = true;
}
}
}
/// <summary>
/// 重写退出方法
/// </summary>
/// <param name="e"></param>
protected override void OnFormClosing(FormClosingEventArgs e)
{
// 在关闭窗体时解锁键盘以确保不会永久锁定
KeyBroadBlock.UnlockKeyboard();
base.OnFormClosing(e);
}
/// <summary>
/// 一键锁屏
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LockScreenBtn_Click(object sender, EventArgs e)
{
KeyBroadBlock.LockScreen();
}
/// <summary>
/// 双击时间
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (Opreation_flag==1)
{
//锁屏
KeyBroadBlock.LockScreen();
return;
}
if (Opreation_flag == 2)
{
//隐藏
Visible = !Visible;
return;
}
if (Opreation_flag == 3)
{
//锁定键盘
LockInputIBtn_Click(null, null);
return;
}
}
}
}
设置窗口代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DeskPet
{
public partial class SettingForm : Form
{
Form1 mainform;
string CurFolder;//当前文件夹
public SettingForm(Form1 MainForm)
{
InitializeComponent();
ShowInTaskbar = false;
//解析台词文本
PutOnText(MainForm.StarText, StartTextBox);
PutOnText(MainForm.tootipText, ToolTiptextBox);
PutOnText(MainForm.TimerText, TimertextBox);
//时间间隔
int num = MainForm.duration;
TimerSpan.Text = num.ToString();
//呼吸幅度和频率
string breathSizeText = "";
switch (MainForm.Breath_Size_nums)
{
case 2:
breathSizeText = "小";
break;
case 4:
breathSizeText = "中";
break;
case 5:
breathSizeText = "大";
break;
default:
// 如果 Breath_Size_nums 的值不是 2、4、6 中的任意一个,则给出错误提示
MessageBox.Show("无法获取呼吸幅度对应的值。你是不是改配置文件了?","菊菊抓住你了");
break;
}
Breath_Fudu.Text = breathSizeText;
string breathFreqText = "";
switch (MainForm.Breath_Fre_nums)
{
case 100:
breathFreqText = "快";
break;
case 200:
breathFreqText = "正常";
break;
case 400:
breathFreqText = "慢";
break;
default:
// 如果 Breath_Fre_nums 的值不是 100、200、400 中的任意一个,则给出错误提示
MessageBox.Show("无法获取呼吸频率对应的值。你是不是改配置文件了?","菊菊抓住你了");
break;
}
Breath_Frequence.Text = breathFreqText;
//是否呼吸
IsBreath.Checked = MainForm.IsBreath;
mainform = MainForm;
//装载贴图文件夹
CurFolder = mainform.imageFold;
LoadPofile();
//角色名
CharatertextBox.Text = ((KeyValuePair<string, string>)ImagecomboBox.SelectedItem).Key ;
//装载动作模式
LockScreencheckBox.Checked = MainForm.Opreation_flag == 1;
VisblecheckBox.Checked = MainForm.Opreation_flag == 2;
LockKeyBroadcheckBox.Checked = MainForm.Opreation_flag == 3;
MissQQWeChatcheckBox.Checked = MainForm.Opreation_flag == 4;
}
/// <summary>
/// 解析文本入TextBox
/// </summary>
/// <param name="StringArray"></param>
/// <param name="S_TextBox"></param>
private void PutOnText(string[] StringArray,TextBox S_TextBox)
{
foreach (string line in StringArray)
{
S_TextBox.Text += line + Environment.NewLine;
}
}
/// <summary>
/// 保存参数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
// 获取程序执行的目录
string programDirectory = Environment.CurrentDirectory;
//台词
mainform.StarText=StartTextBox.Text.Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
mainform.tootipText = ToolTiptextBox.Text.Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
mainform.TimerText = TimertextBox.Text.Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
//频率
int num = 5000;
if (int.TryParse(TimerSpan.Text, out num))
{
mainform.Duration = num;
}
else
{
MessageBox.Show("数字那里别乱填。。", "菊大侠的友情提示");
return;
}
//幅度
int Breath_Size_nums = Breath_Fudu.Text.Trim() == "小" ? 2 : Breath_Fudu.Text.Trim() == "中" ? 4 : 5;
mainform.Breath_Size_nums = Breath_Size_nums;
//呼吸频率
int Breath_Fre_nums = Breath_Frequence.Text.Trim() == "慢" ? 400 : Breath_Frequence.Text.Trim() == "正常" ? 200 : 100;
mainform.Breath_Fre_nums = Breath_Fre_nums;
//是否呼吸
mainform.IsBreath = IsBreath.Checked;
//贴图更改
mainform.ImageFold=ImagecomboBox.SelectedValue.ToString();
//双击模式选择
int mode = LockScreencheckBox.Checked ? 1 : 0;
mode = VisblecheckBox.Checked ? 2 : mode;
mode = LockKeyBroadcheckBox.Checked ? 3 : mode;
mode = MissQQWeChatcheckBox.Checked ? 4 : mode;
mainform.Opreation_flag = mode;
//保存配置
string[] _config = new string[] {$"{CharatertextBox.Text}",//角色名字
$"{num.ToString()}" ,//台词频率
$"{Breath_Size_nums.ToString()}",//呼吸幅度
$"{Breath_Fre_nums.ToString()}",//呼吸频率
$"{(IsBreath.Checked?1:0)}" ,//是否呼吸
$"{((KeyValuePair<string,string>)ImagecomboBox.SelectedItem).Value}",//默认贴图路径
$"{mode}"};//双击动作模式
SaveParametersToFile("La_Text_Config.txt", mainform.tootipText);
SaveParametersToFile("Timer_Text_Config.txt", mainform.TimerText);
SaveParametersToFile("Start_Text_Config.txt", mainform.StarText);
SaveParametersToFile("ConfigText.txt", _config);
mainform.CharaterName = CharatertextBox.Text;
mainform.IsSettingOpen = false;
Close();
}
/// <summary>
/// 关闭窗体时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SettingForm_FormClosing(object sender, FormClosingEventArgs e)
{
mainform.IsSettingOpen = false;
}
/// <summary>
/// 保存配置
/// </summary>
/// <param name="fileName"></param>
/// <param name="SaveText"></param>
private void SaveParametersToFile(string fileName, string[] SaveText)
{
// 获取程序执行的目录
string programDirectory = Environment.CurrentDirectory;
// 保存 Start 文件
string startFileName = $"{fileName}";
string startFilePath = Path.Combine(programDirectory, startFileName);
using (StreamWriter writer = new StreamWriter(startFilePath))
{
foreach (string line in SaveText)
{
writer.WriteLine(line);
}
}
}
/// <summary>
/// 装载文件
/// </summary>
public void LoadPofile()
{
try
{
string programDirectory = Environment.CurrentDirectory;
string path = $@"{programDirectory}/image";
Dictionary<string, string> folders = new Dictionary<string, string>();
// 获取所有文件夹
foreach (string folderPath in Directory.GetDirectories(path))
{
// 获取文件夹名
string folderName = Path.GetFileName(folderPath);
// 将文件夹名和地址添加到字典中
folders.Add(folderName, folderPath);
}
// 将字典中的文件夹添加到下拉框中
ImagecomboBox.DataSource = new BindingSource(folders, null);
ImagecomboBox.DisplayMember = "Key";
ImagecomboBox.ValueMember = "Value";
//当前文件夹名字
string _folderName = Path.GetFileNameWithoutExtension(CurFolder);
foreach (KeyValuePair<string,string> folderPath in ImagecomboBox.Items)
{
if(_folderName == folderPath.Key)
{
ImagecomboBox.Text = _folderName;
break;
}
}
}
catch(Exception ex )
{
MessageBox.Show("装载贴图失败!原因是" + ex.ToString(), "出错啦~");
}
}
/// <summary>
/// 更改名字
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ImagecomboBox_SelectedValueChanged(object sender, EventArgs e)
{
CharatertextBox.Text = ImagecomboBox.Text;
}
//窗体加载
private void SettingForm_Load(object sender, EventArgs e)
{
//焦点聚焦
button1.Focus();
}
private void CheckBox_CheckedChanged(object sender, EventArgs e)
{
// 获取当前触发事件的 CheckBox 控件
CheckBox currentCheckBox = (CheckBox)sender;
// 只有在当前 CheckBox 不被选中时才执行下面的操作
if (currentCheckBox.Checked)
{
// 遍历 GroupBox 中的所有 CheckBox 控件
foreach (Control control in groupBox3.Controls)
{
if (control is CheckBox)
{
CheckBox checkBox = control as CheckBox;
if (checkBox != currentCheckBox)
{
checkBox.Checked = false;
}
// 将其他 CheckBox 的 Checked 属性设置为 false
}
}
}
}
}
}
失去焦点后的显示轮播对话的窗体代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DeskPet
{
public partial class TextForm : Form
{
public string userName="东方未明";
public string UserName
{
get
{
return userName;
}
set
{
userName = value;
groupControl1.Text = value;
}
}
private Timer timer;//时间计时器
public string currentText;
public string CurrentText
{
get
{
return currentText;
}
set
{
currentText = value;
TalkText.Text = currentText;
Visible = true;
groupControl1.Focus();
timer.Start();
}
}
public TextForm(int duration,string name)
{
InitializeComponent();
// 初始化计时器
timer = new Timer();
timer.Interval = duration;
timer.Tick += Timer_Tick;
//隐藏任务栏
ShowInTaskbar = false;
//窗口前置
TopMost = true;
}
private void TextForm_Load(object sender, EventArgs e)
{
//透明化背景
this.BackColor = Color.FromArgb(0, 0, 1);
this.TransparencyKey = this.BackColor;
groupControl1.Text = UserName;
}
/// <summary>
/// 定时关掉
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer_Tick(object sender, EventArgs e)
{
// 关闭窗口
// 停止计时器
timer.Stop();
Visible = false;
}
private void TalkText_TextChanged(object sender, EventArgs e)
{
// 计算文本的行数,并根据行数自适应调整TextBox的高度
int lineCount = TalkText.GetLineFromCharIndex(TalkText.TextLength) + 1;
int lineHeight = TextRenderer.MeasureText("A", TalkText.Font).Height; // 假设每行的高度相同
int newHeight = Math.Max(lineCount * lineHeight, TalkText.MinimumSize.Height);
TalkText.Height = newHeight;
groupControl1.Height = TalkText.Bottom;
//int txtHeight = 22;//设置单行的行高
//int MaxLineCount = 20;//设置最大行数
//Size size = TextRenderer.MeasureText(TalkText.Text, TalkText.Font);
//int itxtLine = size.Width / TalkText.Width + TalkText.Lines.Count() + 1;
//if (itxtLine > MaxLineCount) { itxtLine = MaxLineCount; }
//TalkText.Height = txtHeight * itxtLine;
}
}
}
实现锁屏和锁定键盘类
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace DeskPet
{
public class KeyBroadClass
{
// 导入Windows API函数
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_MINIMIZE = 6;
private const int WH_KEYBOARD_LL = 13; // 定义键盘低级钩子常量
private const int WM_KEYDOWN = 0x0100; // 定义键盘按下消息常量
private IntPtr hookId = IntPtr.Zero; // 钩子句柄
private LowLevelKeyboardProc keyboardProc;
// 构造函数
public KeyBroadClass()
{
keyboardProc = HookCallback; // 设置钩子回调函数
}
// 声明钩子回调函数委托
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
// 导入Windows API函数,用于设置键盘钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
// 导入Windows API函数,用于卸载键盘钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
// 导入Windows API函数,用于传递钩子事件到下一个钩子处理程序
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
// 钩子回调函数,用于拦截和处理键盘事件
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
// 禁止键盘事件
return (IntPtr)1;
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
// 方法用于锁定键盘
public bool LockKeyboard()
{
// 安装键盘钩子
hookId = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardProc, IntPtr.Zero, 0);
return true;
}
// 方法用于解锁键盘
public bool UnlockKeyboard()
{
// 卸载键盘钩子
if (hookId != IntPtr.Zero)
{
UnhookWindowsHookEx(hookId);
hookId = IntPtr.Zero;
return true;
}
return false;
}
//锁屏
public void LockScreen()
{
// 启动锁屏命令
Process.Start("rundll32.exe", "user32.dll,LockWorkStation");
}
//最小化进程
public static void MinimizeProcess(string processNameOrPath)
{
// 寻找与指定进程名称或文件路径匹配的所有进程
Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(processNameOrPath));
foreach (Process process in processes)
{
// 最小化进程的主窗口
if (process.MainWindowHandle != IntPtr.Zero)
{
ShowWindow(process.MainWindowHandle, SW_MINIMIZE);
}
}
}
}
}
小结
- Tooltip在窗体失去激活时也会失去作用,如果想在窗体失去激活时依旧有对话弹窗,需要用额外的窗体打开显示。
- 呼吸效果参考了呼吸灯的做法,要注意必须要Top和Height同时变动,不然会造成底层伸缩。
- 在编写设置呼吸频率和呼吸启动的时候必须注意不要重复启动呼吸子线程。
- 拖拽事件在计算鼠标和图像位置时,必须同时重新计算ToolTip和失焦对话窗体的坐标才能及时刷新对话框位置。