WPF 实现 Windows 桌面动态壁纸

 WPF 实现 Windows  桌面动态壁纸

Nimble

作者:WPFDevelopersOrg - 驚鏵

原文链接[1]:https://github.com/WPFDevelopersOrg/Nimble

原文链接[2]:https://gitee.com/WPFDevelopersOrg/Nimble

  • 框架使用.NET40

  • Visual Studio 2019;

  • 项目使用 MIT 开源许可协议;

接着上一篇

gitee下载体验

8275cb57644d61c1ced5a79575072556.png 21ce544f85311835161f6e04061118d1.png

github下载体验

220590b6b3af5fe98f12e1890573da91.png 8be3acac7d350ef5ee2c7fae0e4a83dc.png

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值