WPF将窗口置于桌面下方(可用于动态桌面)

先来看一下效果:

界面元素很简单,就一个Button按钮,然后写个定时器,定时更新Button按钮中的内容为当前时间,下面来介绍下原理,和界面组成。

窗口介绍

Windows操作系统所有的地方都是窗口,可能这也是系统名字的由来吧,包括你看到的文件夹,桌面,右键菜单,这些都是由界面组成的, 这么多窗口需要有一个合理的显示,就需要用到我们的层级关系,比如两个窗体谁显示在前,谁显示在后。

VS给我们提供了一个查找和查看窗口信息的工具,叫做Spy++,在工具里面:

打开之后了,这里给我们展示了当前系统所有的窗口信息,你也可以点击红色框中的查找工具,来查看你想知道的窗口信息:

来演示一下如何查找窗口,点击上方红色框中的查找窗口按钮,两个随便选一个,会弹出如下窗口:

然后你点击红色区域中的这个控件拖动到你想获取的信息窗口,就能看到当前窗口的详细信息了,包括窗口的句柄、标题、类。

比如我直接将图标拖到桌面上,可以看到这是他显示桌面的信息:

这里我们关掉这个窗口, 回到Spy++的主界面,拖到最底部:

可以看到, Progman Manager是桌面窗口的父窗口,前面小窗口图标是灰色的表示的是此窗口是隐藏的(子窗口拥有和父窗口一致的显示层级)。

原理操作

现在,我们只需要把我们的界面,也就是放到 Program Manager下面,然后再适当调整它的显示顺序,就可以了,但是这一块我们不好操作。有一个其他路子就是给窗口发送一个特殊的消息,来让我们有操作的空间。

只需要给 Program Manager窗口发送一个消息0x52C,就可以将Program Manager拆分为多个窗口,分别是Program Manager窗口和两个WorkerW窗口。

下面是已经发送过此消息后的样子:

可以看到Program Manager下面已经什么都没有了,内容全都转移到第一个WokerW窗口下,这时候我们只需要将我们的窗口挂在到Program Manager窗口的下方就能又有和它一样的显示层级了(窗口从下到上依次显示,所以这里Program Manager显示在最底层),不过需要注意的是,在Program Manager和第一个WorkerW窗口之间,还存在另外一个WorkerW窗口,在我的系统中,它默认隐藏了,为了确保效果一致,我们需要手动将它隐藏起来。

具体操作

窗体的部分很简单,就创建个窗体,带一个按钮,设置一些窗口基本的信息,截个代码:

代码部分也贴一下吧(复制代码注意你的命名空间):

<Window
x:Class="BehindTheDesktop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BehindTheDesktop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="NoResize"
WindowStyle="None"
mc:Ignorable="d"> <Button
x:Name="btnTime"
Click="Button_Click"
Content="开始"
FontSize="300"
Foreground="WhiteSmoke" /> </Window>

然后呢就是后台的代码的部分,获取窗口信息,查找窗口和向桌面窗口发送消息 0x52C,需要用到一些Win32的API,下面直接列出来。

    //Win32方法
public static class Win32Func
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string className, string winName); [DllImport("user32.dll")]
public static extern IntPtr SendMessageTimeout(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam, uint fuFlage, uint timeout, IntPtr result); //查找窗口的委托 查找逻辑
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsProc proc, IntPtr lParam); [DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string winName); [DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hwnd, int nCmdShow); [DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hwnd, IntPtr parentHwnd);
}

下面的代码就是向窗口发送消息,并且隐藏中间WorkerW窗口的方法:

        /// <summary>
/// 向桌面发送消息
/// </summary>
public void SendMsgToProgman()
{
// 桌面窗口句柄,在外部定义,用于后面将我们自己的窗口作为子窗口放入
programHandle = Win32Func.FindWindow("Progman", null); IntPtr result = IntPtr.Zero;
// 向 Program Manager 窗口发送消息 0x52c 的一个消息,超时设置为2秒
Win32Func.SendMessageTimeout(programHandle, 0x52c, IntPtr.Zero, IntPtr.Zero, 0, 2, result); // 遍历顶级窗口
Win32Func.EnumWindows((hwnd, lParam) =>
{
// 找到第一个 WorkerW 窗口,此窗口中有子窗口 SHELLDLL_DefView,所以先找子窗口
if (Win32Func.FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null) != IntPtr.Zero)
{
// 找到当前第一个 WorkerW 窗口的,后一个窗口,及第二个 WorkerW 窗口。
IntPtr tempHwnd = Win32Func.FindWindowEx(IntPtr.Zero, hwnd, "WorkerW", null); // 隐藏第二个 WorkerW 窗口
Win32Func.ShowWindow(tempHwnd, 0);
}
return true;
}, IntPtr.Zero);
}

然后在MainWindow的构造函数中调用下就行了:

        public MainWindow()
{
InitializeComponent(); //向桌面发送消息
SendMsgToProgman();
}

最后贴一下页面Button的Click方法:

        private void Button_Click(object sender, RoutedEventArgs e)
{
// 设置当前窗口为 Program Manager的子窗口
Win32Func.SetParent(new WindowInteropHelper(this).Handle, programHandle); //设置button背景色为透明
btnTime.Background = new SolidColorBrush(Colors.Transparent);
//设置当前界面的宽高,因为我是双屏,所以不能设置全屏,就以这种方式来设置界面了
this.Width = 1920;
this.Height = 1080;
this.Left = 0;
this.Top = 0; //启动定时器,更新Button按钮中的内容
}

关于改变Button按钮中内容的定时器,这里就不贴了,随意发挥自己的想象吧!

简单心得

既然可以把当前的界面放置到桌面图标的下方, 那么在界面中可以设置一些更加好玩的效果,比如播放一个视频,这就是我们的动态桌面了,要是再有点奇思妙想,弄个科技桌面,或者简单粗暴做个俄罗斯方块,也不是不可以! 自由发挥!

又水一篇!

哈哈!

WPF将窗口置于桌面下方(可用于动态桌面)的更多相关文章

  1. WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)

    原文 WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口) WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验.如果希望做不同线程 ...

  2. 2000条你应知的WPF小姿势 基础篇<74-77 WPF 多窗口Tips>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000ThingsYou Should Know About C# 和 2,00 ...

  3. WPF自定义窗口基类

    WPF自定义窗口基类时,窗口基类只定义.cs文件,xaml文件不定义.继承自定义窗口的类xaml文件的根节点就不再是<Window>,而是自定义窗口类名(若自定义窗口与继承者不在同一个命名 ...

  4. wpf中子窗口的几个问题

    今天研究了一下wpf中的窗口,写这篇文章来总结一下今天的收获.(转载请注明出处~) 总所周知,窗口是windows系统中十分重要的一个元素(从名字上就能体现出来),而一个应用程序总是包含很多窗口(主窗 ...

  5. WPF自学入门(九)WPF自定义窗口基类

    今天简单记录一个知识点:WPF自定义窗口基类,常用winform的人知道,winform的窗体继承是很好用的,写一个基础窗体,直接在后台代码改写继承窗体名.但如果是WPF要继承窗体,我个人感觉没有理解 ...

  6. VC.【转】窗口置于前台并激活的方法

    1.VC 窗口置于前台并激活的方法 - CSDN博客.html https://blog.csdn.net/oXunFeng/article/details/52681279 2.(http://ww ...

  7. wpf 自定义窗口,最大化时覆盖任务栏解决方案

    原文:wpf 自定义窗口,最大化时覆盖任务栏解决方案 相信很多人使用wpf时会选择自定义美观的窗口,因此会设置WindowStyle="None" 取消自带的标题栏.但这样使用 W ...

  8. WPF自定义窗口最大化显示任务栏

    原文:WPF自定义窗口最大化显示任务栏 当我们要自定义WPF窗口样式时,通常是采用设计窗口的属性 WindowStyle="None" ,然后为窗口自定义放大,缩小,关闭按钮的样式 ...

  9. C# 编写一个小巧快速的 Windows 动态桌面软件

    开源自己前段时间使用 C# 编写的 Windows 动态桌面软件,在接下来的博客我将描写一些技术细节和遇到的一些坑.这个软件可以把视频设置成桌面背景播放,不仅如此而且还可以把网页或一个网页文件设置成桌 ...

随机推荐

  1. 如何隐藏shell脚本内容

    从事 Linux 开发的同学,经常需要编写 shell 脚本,有时脚本中会涉及到一些敏感内容,比如一些 IP 地址,用户名以及密码等,或者脚本中有一些关键的代码, 所有这些内容你都不想别人阅读或者修改 ...

  2. KNN算法实现对iris数据集的预测

    KNN算法的实现 import pandas as pd from math import dist k = int(input("请输入k值:")) dataTest = pd. ...

  3. bilibili动画下载视频批量改名(python)

    bilib应用 在微软商店中下载哔哩哔哩动画,虽然软件UI古老,但是贵在稳定和支持下载 安装以后搜索自己想要的视频,然后缓存下载 下载后进入下载的路径 视频文件重命名 打开自动命令的程序或者py脚本, ...

  4. 快速入门上手JavaScript中的Promise

    当我还是一个小白的时候,我翻了很多关于Promise介绍的文档,我一直没能理解所谓解决异步操作的痛点是什么意思 直到我翻了谷歌第一页的所有中文文档我才有所顿悟,其实从他的英文字面意思理解最为简单粗暴 ...

  5. CF1559D2 Mocha and Diana (Hard Version)

    考虑到加树边每次最多只导致一对联通块之间的状态. 所以我们以任意顺序加入当前的合法边. 我们考虑先加入所有可加的\((1,a)\) 然后统计只在\(A\)中与1连的点,\(B\)中与2连的点. 则他们 ...

  6. DP 做题记录 II.

    里面会有一些数据结构优化 DP 的题目(如 XI.),以及普通 DP. *I. P3643 [APIO2016]划艇 题意简述:给出序列 \(a_i,b_i\),求出有多少序列 \(c_i\) 满足 ...

  7. python判断字符串是否为空和null

    1.使用字符串长度判断 len(s==0)则字符串为空 test1 = '' if len(test1) == 0: print('test1为空串') else: print('test非空串,te ...

  8. Nginx编译安装相关参数

    Nginx编译安装相关参数 Nginx插件安装 ------------------pcre------------------ cd /usr/local/source wget http://ww ...

  9. rust shadow

    1 fn main() { 2 let mut demo = 12; 3 println!("{}",demo); 4 demo = 21; // 值可变,数据类型不可变 5 pr ...

  10. 数据分析体系 — 用户粘性的两个计算指标(DAU/MAU和月人均活跃天数)

    很多运营都了解DAU(日活跃用户数)和MAU(月活跃用户数)的重要性,但在某些情况下这两个数值本身并不能反映出太多问题,这个时候就要引用到[DAU/MAU]的概念,即[日活/月活] 用户粘性的两个计算 ...