初心 & 目标
个人 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();}}
