WPF 实现 Windows 桌面动态壁纸
Nimble
作者:WPFDevelopersOrg - 驚鏵
原文链接[1]:https://github.com/WPFDevelopersOrg/Nimble
原文链接[2]:https://gitee.com/WPFDevelopersOrg/Nimble
框架使用
.NET40
;Visual Studio 2019
;项目使用 MIT 开源许可协议;
接着上一篇
gitee下载体验


github下载体验


1)基于原有功能增加代码如下:
在样式的触发器中,如果VideoPath属性为空,则禁用了菜单项的点击操作。
<MenuItem
DisplayMemberPath="ItemName"
Header="动态壁纸"
ItemsSource="{Binding Path=WallpaperArray}"
ToolTip="更多壁纸请加QQ群:929469013">
<MenuItem.ItemContainerStyle>
<Style BasedOn="{StaticResource MenuItemSub}" TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.WallpaperSelectedCommand, RelativeSource={RelativeSource AncestorType=MenuItem}}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.VideoPath, RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter Property="IsHitTestVisible" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
2)WallpaperItem
代码如下:
定义了一个名为 WallpaperItem 的类,该类继承自 ItemBase 类。WallpaperItem 类有两个属性:ItemName 表示动态壁纸的名称,VideoPath 表示动态壁纸的视频文件路径
namespace Nimble.Models
{
public class WallpaperItem : ItemBase
{
public string ItemName { get; set; }
public string VideoPath { get; set; }
}
}
3)切换动态背景视频代码如下:
当选择一个壁纸项时,首先判断该壁纸项是否有视频路径,如果没有视频路径则直接返回,否则调用 ShowWallpaper 方法来显示该壁纸。
在 ShowWallpaper 方法中,首先判断壁纸路径是否为空或不存在相应文件,如果是,则直接返回。然后调用 StopFFplayProcess 方法停止之前的播放进程。接着将除当前选中壁纸之外的其他壁纸设置为未选中状态。然后调用 StartFFplayProcess 方法启动新的播放进程。最后,如果能够获取到 ffplay 的窗口句柄,就发送消息给桌面管理器,将 ffplay 窗口设置为桌面的子窗口,并保存壁纸路径。
SendMsgToProgman 方法用于向桌面管理器发送消息。在其中通过 FindWindow 函数找到桌面管理器的窗口句柄,然后使用 SendMessageTimeout 函数发送消息,再调用 EnumWindows 函数枚举窗口,通过回调函数 EnumWindowsCallback 来隐藏桌面下的 "WorkerW" 窗口。
StartFFplayProcess 方法用于启动 ffplay 进程播放视频文件。首先构造启动进程的参数和信息,然后创建一个新的进程,并等待其主窗口句柄获取到。最后将 ffplay 进程的主窗口句柄保存到 ffplayWindowHandle 变量。
StopFFplayProcess 方法用于停止 ffplay 进程,如果当前存在 ffplay 进程且未退出,则终止该进程,并释放资源。
public ICommand WallpaperSelectedCommand => new RelayCommand(obj =>
{
if (obj is WallpaperItem wallpaper)
{
if (string.IsNullOrWhiteSpace(wallpaper.VideoPath)) return;
ShowWallpaper(wallpaper.VideoPath);
}
});
void ShowWallpaper(string wallpaperPath)
{
if (string.IsNullOrWhiteSpace(wallpaperPath) || !File.Exists(wallpaperPath)) return;
StopFFplayProcess();
WallpaperArray.Where(x => x.VideoPath != wallpaperPath).ToList().ForEach(x =>
{
x.IsSelected = false;
});
StartFFplayProcess(wallpaperPath);
if (ffplayWindowHandle != IntPtr.Zero)
{
SendMsgToProgman();
Win32Api.SetParent(ffplayWindowHandle, desktopHandle);
if (ConfigHelper.WallpaperPath != wallpaperPath)
ConfigHelper.SaveWallpaperPath(wallpaperPath);
}
}
void SendMsgToProgman()
{
desktopHandle = Win32.FindWindow("Progman", null);
IntPtr result = IntPtr.Zero;
Win32Api.SendMessageTimeout(desktopHandle, 0x52c, IntPtr.Zero, IntPtr.Zero, 0, 2, result);
Win32Api.EnumWindows(EnumWindowsCallback, IntPtr.Zero);
}
bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam)
{
if (Win32Api.FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null) != IntPtr.Zero)
{
IntPtr workerW = Win32Api.FindWindowEx(IntPtr.Zero, hwnd, "WorkerW", null);
Win32Api.ShowWindow(workerW, 0);
}
return true;
}
void StartFFplayProcess(string videoFilePath)
{
var ffplayPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DLL", "ffplay.exe");
var startInfo = new ProcessStartInfo();
startInfo.FileName = ffplayPath;
//startInfo.WindowStyle = ProcessWindowStyle.Maximized;
startInfo.Arguments = $"-loop 0 -fs \"{videoFilePath}\" ";
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
try
{
ffplayProcess = new Process();
ffplayProcess.StartInfo = startInfo;
ffplayProcess.Start();
var startTime = DateTime.Now;
var timeout = TimeSpan.FromSeconds(10);
while (ffplayProcess.MainWindowHandle == IntPtr.Zero)
{
if (DateTime.Now - startTime > timeout)
{
throw new TimeoutException("无法获取到 MainWindowHandle。");
}
Thread.Sleep(500);
}
ffplayWindowHandle = ffplayProcess.MainWindowHandle;
}
catch (Exception ex)
{
Log.Error($"Error: StartFFplayProcess {ex.Message}");
}
}
void StopFFplayProcess()
{
try
{
if (ffplayProcess != null && !ffplayProcess.HasExited)
{
ffplayProcess.Kill();
ffplayProcess.Dispose();
ffplayProcess = null;
ffplayWindowHandle = IntPtr.Zero;
}
}
catch (Exception ex)
{
Log.Error($"Error: StopFFplayProcess {ex.Message}");
}
}
更多代码请查看源码
参考资料
[1]
原文链接: https://github.com/WPFDevelopersOrg/Nimble
[2]原文链接: https://gitee.com/WPFDevelopersOrg/Nimble