最近公司需要做一个桌面程序,功能倒是简单,只不过是对特效要求比较特殊,要求窗体依附于桌面(WIN+D等直接显示桌面的时候不消失)、可以设置透明度、没有数据的地方可以实现鼠标穿透。
搞了半天,也没完全实现,用API可以分别实现 鼠标穿透、窗体透明(这个不用API也可以)。但是当这些特效一旦和依附桌面相结合的时候,通通达不到效果,要么窗体不显示,要么实现不了。而且窗体捕获不到显示桌面时候发送的的消息(估计当WIN+D等显示桌面的时候直接把桌面设置为topmost了,而不给窗体发送消息。)估计是我对桌面的hWnd捕获不对。现在先把代码分别贴出来。以后备用。
PS:我测试的系统是windows7,如果是XP 2000 2003下面代码可以正常实现,在VISA和win7下不行。
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Haedu_Job
{
public partial class mainfrm : Form
{
public mainfrm()
{
InitializeComponent();
this.CanPenetrate();
}
#region 创建依附于桌面的窗体
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern int FindWindow(string lpClassName, string lpWindowName);[DllImport("user32.dll", EntryPoint = "GetWindow")] //获取窗体句柄,hwnd为源窗口句柄 /*wCmd指定结果窗口与源窗口的关系,它们建立在下述常数基础上: GW_CHILD寻找源窗口的第一个子窗口 GW_HWNDFIRST为一个源子窗口寻找第一个兄弟(同级)窗口,或寻找第一个顶级窗口 GW_HWNDLAST为一个源子窗口寻找最后一个兄弟(同级)窗口,或寻找最后一个顶级窗口 GW_HWNDNEXT为源窗口寻找下一个兄弟窗口 GW_HWNDPREV为源窗口寻找前一个兄弟窗口 GW_OWNER寻找窗口的所有者 */
public static extern int GetWindow(int hwnd, int wCmd);
[DllImport("user32.dll", EntryPoint = "SetParent")]
public static extern int SetParent(int hWndChild, int hWndNewParent);
const int GW_HWNDFIRST = 0; //{同级别 Z 序最上}
const int GW_HWNDLAST = 1; //{同级别 Z 序最下}
const int GW_HWNDNEXT = 2;//{同级别 Z 序之下}
const int GW_HWNDPREV = 3; //{同级别 Z 序之上}
const int GW_OWNER = 4; //{属主窗口}
const int GW_CHILD = 5;//{子窗口中的最上} //
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] //
public static extern int GetDesktopWindow();
#endregion
#region 消息处理 //截取消息,进行处理 //
private const int WM_SIZE = 0x0005; //
const int SIZE_MAXIMIZED = 2; //
const int SIZE_MINIMIZED = 1; //
protected override void WndProc(ref Message m) //
{ //
switch (m.Msg) //
{ //
case WM_SIZE: //
if (m.WParam.ToInt32() == SIZE_MAXIMIZED) //
{ // // 窗体最大化 //
WindowState = FormWindowState.Normal; //
} //
else if (m.WParam.ToInt32() == SIZE_MINIMIZED) //
{ // // 窗体最小花 //
Console.WriteLine("Minimized"); // } //
else //
{ // // 其他 //
base.DefWndProc(ref m); //
Console.WriteLine(m.WParam.ToInt32().ToString()); //
} //
break; //
default: //
base.WndProc(ref m); // //
Console.WriteLine(m.LParam.ToString()); //
break; //
} //
}
#endregion
#region gridveiw边框样色重绘
private void dataGridView1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Peru, new Rectangle(0, 0, this.dataGridView1.Width - 1, this.dataGridView1.Height - 1));
}
#endregion
#region 窗体透明 API方式,备用 签入桌面此API无法工作
[DllImport("user32.dll")]
public extern static IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public extern static bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
public static uint LWA_COLORKEY = 0x1;
public static uint LWA_ALPHA = 0x2; // 0x00000002;
[DllImport("user32.dll")]
public extern static uint SetWindowLong(IntPtr hwnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll")]
public extern static uint GetWindowLong(IntPtr hwnd, int nIndex);
public enum WindowStyle :int { GWL_EXSTYLE = -20 }
public enum ExWindowStyle : uint { WS_EX_LAYERED = 0x00080000 }
private void SetWindowTransparent(byte bAlpha)
{
try
{
SetWindowLong(this.Handle, (int)WindowStyle.GWL_EXSTYLE, GetWindowLong(this.Handle, (int)WindowStyle.GWL_EXSTYLE) | (uint)ExWindowStyle.WS_EX_LAYERED);
SetLayeredWindowAttributes(this.Handle, 0x003F85CD, bAlpha, LWA_COLORKEY | LWA_ALPHA);
} catch { }
}
protected override CreateParams CreateParams {
get { CreateParams cp = base.CreateParams;
cp.Parent = GetDesktopWindow();
cp.ExStyle = 0x00000080 | 0x00000008;//WS_EX_TOOLWINDOW | WS_EX_TOPMOST
return cp;
}
}
#endregion
#region 窗体穿透
private const uint WS_EX_LAYERED = 0x80000;
private const int WS_EX_TRANSPARENT = 0x20;
private const int GWL_STYLE = (-16);
private const int GWL_EXSTYLE = (-20); //
private const int LWA_ALPHA = 0x2;
public void CanPenetrate()
{
uint intExTemp = GetWindowLong(this.Handle, GWL_EXSTYLE);
uint oldGWLEx = SetWindowLong(this.Handle, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_LAYERED);
SetLayeredWindowAttributes(this.Handle, 0x003F85CD, 100, LWA_ALPHA | LWA_COLORKEY);
}
#endregion
private void mainfrm_FormClosing(object sender, EventArgs e) { }
[DllImport("user32.dll")]
private static extern bool GetWindowText(int hWnd, StringBuilder title, int maxBufSize);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private extern static int GetWindowTextLength(IntPtr hWnd);
private void mainfrm_Load(object sender, EventArgs e)
{ //
MessageBox.Show(GetDesktopWindow().ToString());
int hDesktop = FindWindow("Progman", null); //
int length = GetWindowTextLength(new IntPtr(hDesktop)); //
StringBuilder stringBuilder = new StringBuilder(2 * length + 1); //
GetWindowText(hDesktop, stringBuilder, stringBuilder.Capacity); //
string strTitle = stringBuilder.ToString(); //
MessageBox.Show(strTitle); hDesktop = GetWindow(hDesktop, GW_CHILD); SetParent((int)this.Handle, hDesktop); //
this.SetWindowTransparent(50);
dataGridView1.Width = this.Width - 10;
DataTable dt = new DataTable();
dt.Columns.Add("任务名称");
dt.Columns.Add("当前状态");
dt.Columns.Add("开始日期");
dt.Columns.Add("添加人员");
DataRow dr = dt.NewRow();
dr["任务名称"] = "test";
dr["当前状态"] = "进行";
dr["开始日期"] = "test";
dr["添加人员"] = "test";
dt.Rows.Add(dr);
dataGridView1.DataSource = dt;
}
#region 移动窗体
private Point mouseOffset; //记录鼠标指针的坐标
private bool isMouseDown = false; //记录鼠标按键是否按下
//创建该窗体 MouseDown、MouseMove、MouseUp事件的相应处理程序
private void GB_Title_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
int xOffset;
int yOffset;
if (e.Button == MouseButtons.Left) {
xOffset = -e.X; yOffset = -e.Y;
mouseOffset = new Point(xOffset, yOffset);
isMouseDown = true;
}
}
private void GB_Title_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
if (isMouseDown) {
Point mousePos = Control.MousePosition; mousePos.Offset(mouseOffset.X, mouseOffset.Y);
Location = mousePos;
}
}
private void GB_Title_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
// 修改鼠标状态isMouseDown的值
// 确保只有鼠标左键按下并移动时,才移动窗体
if (e.Button == MouseButtons.Left) { isMouseDown = false; } }
#endregion
private void btn_quit_Click(object sender, EventArgs e) { Application.Exit(); } } }
最终选择的实现方式。设置窗体和要设置透明控件的BackColor,然后设置窗体的TransparencyKey的颜色和刚才设置BackColor相同
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Haedu_Job {
public partial class mainfrm : Form
{
public mainfrm()
{
InitializeComponent();
}
#region 创建依附于桌面的窗体
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern int FindWindow( string lpClassName, string lpWindowName );
[DllImport("user32.dll", EntryPoint = "GetWindow")] //获取窗体句柄,hwnd为源窗口句柄 /*wCmd指定结果窗口与源窗口的关系,它们建立在下述常数基础上: GW_CHILD寻找源窗口的第一个子窗口 GW_HWNDFIRST为一个源子窗口寻找第一个兄弟(同级)窗口,或寻找第一个顶级窗口 GW_HWNDLAST为一个源子窗口寻找最后一个兄弟(同级)窗口,或寻找最后一个顶级窗口 GW_HWNDNEXT为源窗口寻找下一个兄弟窗口 GW_HWNDPREV为源窗口寻找前一个兄弟窗口 GW_OWNER寻找窗口的所有者 */
public static extern int GetWindow( int hwnd, int wCmd ); [DllImport("user32.dll", EntryPoint = "SetParent")]
public static extern int SetParent( int hWndChild, int hWndNewParent ); //
const int GW_HWNDFIRST = 0; //{同级别 Z 序最上} //
const int GW_HWNDLAST = 1; //{同级别 Z 序最下} //
const int GW_HWNDNEXT = 2; //{同级别 Z 序之下} //
const int GW_HWNDPREV = 3; //{同级别 Z 序之上}
const int GW_OWNER = 4; //{属主窗口}
const int GW_CHILD = 5;//{子窗口中的最上} //
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] //
public static extern int GetDesktopWindow();
#endregion
private void mainfrm_FormClosing(object sender, EventArgs e) { }
private void mainfrm_Load(object sender, EventArgs e) {
DataTable dt = new DataTable();
dt.Columns.Add("任务名称");
dt.Columns.Add("当前状态");
dt.Columns.Add("开始日期");
dt.Columns.Add("添加人员");
DataRow dr = dt.NewRow();
dr["任务名称"] = "test";
dr["当前状态"] = "进行";
dr["开始日期"] = "test";
dr["添加人员"] = "test";
dt.Rows.Add(dr);
dataGridView1.DataSource = dt;
int hDesktop = FindWindow("Progman", null);
hDesktop = GetWindow(hDesktop, GW_CHILD);
SetParent((int)this.Handle, hDesktop);
this.TopMost = true;
}
#region 移动窗体
private Point mouseOffset; //记录鼠标指针的坐标
private bool isMouseDown = false; //记录鼠标按键是否按下 //创建该窗体 MouseDown、MouseMove、MouseUp事件的相应处理程序
private void GB_Title_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
int xOffset;
int yOffset;
if (e.Button == MouseButtons.Left) {
xOffset = -e.X;
yOffset = -e.Y;
mouseOffset = new Point(xOffset, yOffset);
isMouseDown = true;
}
}
private void GB_Title_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
if (isMouseDown) {
Point mousePos = Control.MousePosition; mousePos.Offset(mouseOffset.X, mouseOffset.Y);
Location = mousePos;
}
}
private void GB_Title_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { // 修改鼠标状态isMouseDown的值 // 确保只有鼠标左键按下并移动时,才移动窗体
if (e.Button == MouseButtons.Left) { isMouseDown = false; }
}
#endregion
private void btn_quit_Click(object sender, EventArgs e) {
Application.Exit();
}
private void button1_Click(object sender, EventArgs e) {
int hDesktop = FindWindow("Progman", null);
hDesktop = GetWindow(hDesktop, GW_CHILD);
SetParent((int)this.Handle, hDesktop);
this.TopMost = true;
}
}
}
不过唯一不好的地方就是这个依附于桌面,但是要比桌面图标高上一层,所以会遮挡掉桌面图标,我没有找到如何设置窗体为桌面图标的下一层的资料。所以这个问题没法解决,如果您有办法,请留言,非常感谢。