初心 & 目标

个人 WPF 项目做得很少,想了解一下简单的 WPF 项目结构怎么搭,有什么需要注意的。

预设目标:

原项目只是个控制台应用,现在升级为了 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 创建桌面快捷方式:

    1. /// <summary>
    2. /// 创建桌面快捷方式
    3. /// </summary>
    4. /// <param name="name">目标名称</param>
    5. /// <param name="description">目标描述</param>
    6. /// <param name="targetFilePath">源文件路径</param>
    7. /// <param name="logoName">logo名称</param>
    8. /// <returns></returns>
    9. public bool CreateDeskTopLik(string name, string description, string targetFilePath, string logoName)
    10. {
    11. var deskTop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
    12. var dirPath = CoreEngine.Current.AppRootDirection;
    13. if (System.IO.File.Exists($@"{deskTop}\{name}.lnk")) return false;
    14. var shell = new WshShell();
    15. var shortcut = (IWshShortcut)shell.CreateShortcut(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\" + $"{name}.lnk");
    16. shortcut.TargetPath = targetFilePath;
    17. shortcut.Description = description;
    18. // 目标文件夹
    19. shortcut.WorkingDirectory = dirPath;
    20. // 目标应用程序的窗口状态分为普通、最大化、最小化 [1,3,7]
    21. shortcut.WindowStyle = 1;
    22. // 快捷方式图标
    23. shortcut.IconLocation = $@"{dirPath}\Assets\{logoName}.ico";
    24. shortcut.Arguments = "";
    25. // 快捷键
    26. shortcut.Hotkey = "ALT+X";
    27. shortcut.Save();
    28. return true;
    29. }

    实现开机自动设置壁纸

  1. BingWallpaperAuto 项目输出为一个没有界面的客户端应用程序(AutoRunning.exe)
    1. 该程序内调用 BingWallpaper.Core 的 SetWallpaper 方法
  2. 通过注册表将 AutoRunning.exe 注册为开机启动

BingWallpaperAuto 核心代码:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. try
  6. {
  7. CoreEngine.Current.SetWallpaper();
  8. }
  9. catch (Exception e)
  10. {
  11. CoreEngine.Current.Logger.Error(e, "开机自动设置壁纸:失败");
  12. Alert.Show("设置失败", AlertTheme.Error);
  13. }
  14. CoreEngine.Current.Logger.Info("开机自动设置壁纸:结束");
  15. }
  16. }

注册表实现开机自启,参考 C# 设置程序开机自启

编码技巧

异步的 BackgroundWorker

本项目中的异步很多都是通过 BackgroundWorker 实现的,个人用的更多是 await + async,所以学习下。

CoreEngine.cs 中的 SetWallpaperAsync 方法为例:

  1. /// <summary>
  2. /// 异步设置壁纸
  3. /// </summary>
  4. /// <param name="forceFromWeb">强制从网络获取</param>
  5. public void SetWallpaperAsync(bool forceFromWeb = false)
  6. {
  7. Current.Logger.Info("设置桌面壁纸(异步)");
  8. var locker = new object();
  9. var isSuccess = false;
  10. using (var work = new BackgroundWorker())
  11. {
  12. work.DoWork += (work_sender, work_e) =>
  13. {
  14. lock (locker)
  15. {
  16. isSuccess = new WallpaperManager().SetWallpaper(forceFromWeb);
  17. }
  18. };
  19. work.RunWorkerCompleted += (work_sender, work_e) =>
  20. {
  21. var alertConfig = new AlertConfig
  22. {
  23. OnlyOneWindowAllowed = true
  24. };
  25. if (isSuccess)
  26. {
  27. Alert.Show("", "设置成功", AlertTheme.Success, alertConfig);
  28. Current.Logger.Info("设置桌面壁纸(异步)成功");
  29. }
  30. else
  31. {
  32. Alert.Show("", "设置失败", AlertTheme.Error, alertConfig);
  33. Current.Logger.Info("设置桌面壁纸(异步)失败");
  34. }
  35. };
  36. work.RunWorkerAsync();
  37. }
  38. }

再放一个 BackgroundWorker 内部还用到了异步的例子:

  1. /// <summary>
  2. /// 检查更新
  3. /// </summary>
  4. private void CheckUpdate()
  5. {
  6. string updatePath = null;
  7. string updateVersion = null;
  8. using (var work = new BackgroundWorker())
  9. {
  10. work.DoWork += async (work_sender, work_e) =>
  11. {
  12. var update = await new UpdateUtil().FindNewUpdate();
  13. updatePath = update.path;
  14. updateVersion = update.version;
  15. };
  16. work.RunWorkerCompleted += (work_sender, work_e) =>
  17. {
  18. if (null != updatePath)
  19. {
  20. ThereHasUpdate(updatePath, updateVersion);
  21. }
  22. else
  23. {
  24. ThereNoUpdate();
  25. }
  26. };
  27. work.RunWorkerAsync();
  28. }
  29. }