初心 & 目标
个人 WPF 项目做得很少,想了解一下简单的 WPF 项目结构怎么搭,有什么需要注意的。
预设目标:
- WPF 项目的结构?
- 相对原项目 BingWallpaperTest 有哪些升级?
原项目只是个控制台应用,现在升级为了 WPF 应用,但核心的获取壁纸代码基本没动。
项目结构
BingWallpaper2 是由 Dustray 开发的必应壁纸获取软件。
项目地址:https://github.com/Dustray/BingWallpaper2
项目结构:
- BingWallpaper.Core(.NET Framework 类库):核心引擎库
- BingWallpaper2(WPF):UI + 工具类
- BingWallpaperAuto(.NET Framework 类库):设置软件的开机自启
- UnitTest:针对 BingWallpaper.Core 的单元测试
- Update(.NET Framework 类库):通过调用 7Zip DLL 解压更新软件
- Update 最后没用上
项目结构比较简单,核心就是:
- BingWallpaper2:UI + 工具代码
BingWallpaper.Core:壁纸下载和设置 + 日志,作为核心引擎库被多个项目引用
有亮点的代码片段
创建桌面快捷方式
LikUtil.cs 中通过 WshShell 创建桌面快捷方式:
/// <summary>
/// 创建桌面快捷方式
/// </summary>
/// <param name="name">目标名称</param>
/// <param name="description">目标描述</param>
/// <param name="targetFilePath">源文件路径</param>
/// <param name="logoName">logo名称</param>
/// <returns></returns>
public bool CreateDeskTopLik(string name, string description, string targetFilePath, string logoName)
{
var deskTop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var dirPath = CoreEngine.Current.AppRootDirection;
if (System.IO.File.Exists($@"{deskTop}\{name}.lnk")) return false;
var shell = new WshShell();
var shortcut = (IWshShortcut)shell.CreateShortcut(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\" + $"{name}.lnk");
shortcut.TargetPath = targetFilePath;
shortcut.Description = description;
// 目标文件夹
shortcut.WorkingDirectory = dirPath;
// 目标应用程序的窗口状态分为普通、最大化、最小化 [1,3,7]
shortcut.WindowStyle = 1;
// 快捷方式图标
shortcut.IconLocation = $@"{dirPath}\Assets\{logoName}.ico";
shortcut.Arguments = "";
// 快捷键
shortcut.Hotkey = "ALT+X";
shortcut.Save();
return true;
}
实现开机自动设置壁纸
- BingWallpaperAuto 项目输出为一个没有界面的客户端应用程序(AutoRunning.exe)
- 该程序内调用 BingWallpaper.Core 的 SetWallpaper 方法
- 通过注册表将 AutoRunning.exe 注册为开机启动
BingWallpaperAuto 核心代码:
class Program
{
static void Main(string[] args)
{
try
{
CoreEngine.Current.SetWallpaper();
}
catch (Exception e)
{
CoreEngine.Current.Logger.Error(e, "开机自动设置壁纸:失败");
Alert.Show("设置失败", AlertTheme.Error);
}
CoreEngine.Current.Logger.Info("开机自动设置壁纸:结束");
}
}
注册表实现开机自启,参考 C# 设置程序开机自启。
编码技巧
异步的 BackgroundWorker
本项目中的异步很多都是通过 BackgroundWorker 实现的,个人用的更多是 await + async,所以学习下。
以 CoreEngine.cs 中的 SetWallpaperAsync 方法为例:
/// <summary>
/// 异步设置壁纸
/// </summary>
/// <param name="forceFromWeb">强制从网络获取</param>
public void SetWallpaperAsync(bool forceFromWeb = false)
{
Current.Logger.Info("设置桌面壁纸(异步)");
var locker = new object();
var isSuccess = false;
using (var work = new BackgroundWorker())
{
work.DoWork += (work_sender, work_e) =>
{
lock (locker)
{
isSuccess = new WallpaperManager().SetWallpaper(forceFromWeb);
}
};
work.RunWorkerCompleted += (work_sender, work_e) =>
{
var alertConfig = new AlertConfig
{
OnlyOneWindowAllowed = true
};
if (isSuccess)
{
Alert.Show("", "设置成功", AlertTheme.Success, alertConfig);
Current.Logger.Info("设置桌面壁纸(异步)成功");
}
else
{
Alert.Show("", "设置失败", AlertTheme.Error, alertConfig);
Current.Logger.Info("设置桌面壁纸(异步)失败");
}
};
work.RunWorkerAsync();
}
}
再放一个 BackgroundWorker 内部还用到了异步的例子:
/// <summary>
/// 检查更新
/// </summary>
private void CheckUpdate()
{
string updatePath = null;
string updateVersion = null;
using (var work = new BackgroundWorker())
{
work.DoWork += async (work_sender, work_e) =>
{
var update = await new UpdateUtil().FindNewUpdate();
updatePath = update.path;
updateVersion = update.version;
};
work.RunWorkerCompleted += (work_sender, work_e) =>
{
if (null != updatePath)
{
ThereHasUpdate(updatePath, updateVersion);
}
else
{
ThereNoUpdate();
}
};
work.RunWorkerAsync();
}
}