- PR -

C# フォームを突き抜けてフォームの後ろ側をクリックできるようにしたい

1
投稿者投稿内容
ごろごろ
会議室デビュー日: 2006/07/16
投稿数: 5
投稿日時: 2006-07-16 19:08
C#でウィンドウズアプリケーションを作っています。

@デスクトップに常駐し、指定時間になると最前面にウィンドウを表示させる。
Aこのウィンドウは半透明でスプラッシュウィンドウのよう感じである。
Bただし、このウィンドウはクリックできず、ユーザーはこのウィンドウを突き抜けて作業中のウィンドウをクリックできる。

Bはどのようにすれば実現できるのでしょうか?
どこかのページへのリンクや、GOOGLEでの検索ワードでもかまいませんので、アドバイスをよろしくお願いします。
(私のプログラミングスキルはかなり低いので、簡単にはできないのようなら諦めますので、その旨指摘いただければ助かります)

Bの補足
Bの文章で何が言いたいか判らないと困るので他の表現で書きます。
例えば

TopMost = true; //最前面に
Opacity = 0.3; //30%の透明
FormBorderStyle = FormBorderStyle.None; //ボーダースタイル
WindowState = FormWindowState.Maximized; //最大化
timer1.Start; //タイマ(例えば3秒)

としたら、ユーザーは3秒間作業を中断せざるをえないと思います。
ですが、ユーザーに作業を中断させないように、透けて見える自分の作業領域を普通にクリックできるようにさせたいのです。
つまり、ユーザーには手前に出てきたこのウィンドウは見えるけど、ないもの同然として処理させたいのです。
R・田中一郎
ぬし
会議室デビュー日: 2005/11/03
投稿数: 979
投稿日時: 2006-07-17 01:08
難しいと思います。

スプラッシュウィンドウの目的としては、ユーザーがソフトウェアを実行した時に、とりあえず画面を表示させて、その間に、初期処理を済ませてしまおうということと、起動前にユーザーに何らかの情報(注意事項やソフトウェアの名前や著作者)を与えるということがあるのだと思います。

しかし半透明にするということを考えると、後者の目的では無いのでしょう。
であれば、スプラッシュウィンドウを常に最前面にせず、切り替えられるようにするとか、マウスが乗ったら表示を消すとか、クリックしたら最背面にするなどの方が簡単に実現できそうです。
かるあ
ぬし
会議室デビュー日: 2003/11/16
投稿数: 1190
お住まい・勤務地: センガワ→ムサシノ
投稿日時: 2006-07-17 01:29
いっそのこと壁紙にしてしまうとか・・・
YAS
ベテラン
会議室デビュー日: 2006/02/15
投稿数: 59
投稿日時: 2006-07-17 08:24
GetDC(0)でデスクトップのデバイスコンテキストを取得して,デスクトップにフォームを直接描画したらどうでしょう。こうすれば「フォームらしきもの」が表示されるだけで実際にはフォームはないので,マウスクリック等はフォームの表示を突き抜けます。時間が経過したらデスクトップを再描画すれば「フォームらしきもの」は消えます。
YAS
ベテラン
会議室デビュー日: 2006/02/15
投稿数: 59
投稿日時: 2006-07-17 10:28
先ほどのデスクトップへの描画ですが,面白そうなので試しに作ってみました。
VB2005ですが,参考になれば幸いです。

問題点はウィンドウの再描画のタイミングで消えてしまうことがあることです。
ウィンドウ切り替え等のタイミングとぶつかってしまうと表示されないこともあります。
解決法としては,デスクトップをキャプチャしてフォームの画像とブレンドし,タイマーで0.1秒間隔くらいで再表示し続ける等が考えられます。

コード:
Option Strict On
Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging

Public Class Form1

    <DllImport("user32.dll")> _
    Private Shared Function GetDC( _
        ByVal hWnd As IntPtr) _
        As IntPtr
    End Function

    <DllImport("user32.dll")> _
    Private Shared Function ReleaseDC( _
        ByVal hWnd As IntPtr, _
        ByVal hDC As IntPtr) _
        As Integer
    End Function

    <DllImport("user32.dll")> _
    Private Shared Function UpdateWindow( _
        ByVal hWnd As IntPtr) _
        As Boolean
    End Function

    <DllImport("user32.dll")> _
    Private Shared Function RedrawWindow( _
        ByVal hWnd As IntPtr, _
        ByVal lprcUpdate As IntPtr, _
        ByVal hrgnUpdate As IntPtr, _
        ByVal flags As RDW) _
        As Boolean
    End Function

    'RedrawWindow() flags
    Private Enum RDW As Integer
        INVALIDATE = &H1
        INTERNALPAINT = &H2
        [ERASE] = &H4
        VALIDATE = &H8
        NOINTERNALPAINT = &H10
        NOERASE = &H20
        NOCHILDREN = &H40
        ALLCHILDREN = &H80
        UPDATENOW = &H100
        ERASENOW = &H200
        FRAME = &H400
        NOFRAME = &H800
    End Enum

    Private Sub RefreshDesktop()
        RedrawWindow(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, RDW.ALLCHILDREN Or RDW.UPDATENOW Or RDW.ERASE Or RDW.INVALIDATE Or RDW.INTERNALPAINT)
        UpdateWindow(IntPtr.Zero)
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Dim hDC As IntPtr = GetDC(IntPtr.Zero)
        Using g As Graphics = Graphics.FromHdc(hDC)
            Dim f As New Form
            Dim b As New Bitmap(f.Width, f.Height)
            f.DrawToBitmap(b, New Rectangle(0, 0, b.Width, b.Height))
            Dim a As New ImageAttributes
            Dim m As New ColorMatrix
            m.Matrix00 = 1
            m.Matrix11 = 1
            m.Matrix22 = 1
            m.Matrix33 = 0.5
            m.Matrix44 = 1
            a.SetColorMatrix(m)
            Dim s As Rectangle = Screen.AllScreens(0).Bounds
            g.DrawImage(b, New Rectangle(New Point(CInt((s.Width - b.Width) / 2), CInt((s.Height - b.Height) / 2)), b.Size), 0, 0, b.Width, b.Height, GraphicsUnit.Pixel, a)
        End Using
        ReleaseDC(IntPtr.Zero, hDC)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        RefreshDesktop()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim b1 As New Button
        b1.Text = "表示"
        Me.Controls.Add(b1)
        AddHandler b1.Click, AddressOf Button1_Click
        Dim b2 As New Button
        b2.Text = "消去"
        b2.Location = New Point(0, 30)
        Me.Controls.Add(b2)
        AddHandler b2.Click, AddressOf Button2_Click
    End Sub

End Class

甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2006-07-17 12:00
引用:

ごろごろさんの書き込み (2006-07-16 19:08) より:
Bただし、このウィンドウはクリックできず、ユーザーはこのウィンドウを突き抜けて作業中のウィンドウをクリックできる。


Windows3.1の頃ですが、似たような事はやりましたよ。手前側のウィンドウでWM_LBUTTONDOWNを受けて、その後背面に隠れているウィンドウに対してWM_LBUTTON_DOWNをSendMessageすればよいのですよ。そうすれば操作的には透過するウィンドウになります。ウィンドウを列挙しつつ自分の背面にあるウィンドウを特定したりと、結構面倒な処理だったと記憶します。
ごろごろ
会議室デビュー日: 2006/07/16
投稿数: 5
投稿日時: 2006-07-17 15:19
いくつか回答いただけましたので、まずはお礼を。
ありがとうございました!!

簡単にできるのかと思っていたのですが、APIなどを駆使しなければいけないようで、私には敷居が高いようです。

せっかく、YASさんにご教授いただいたので、その内容をそのままC#に書き直したところ、YASさんの思惑通りに動きそうです。
ただ、今後の課題としてYASさんが上げられている
>デスクトップをキャプチャしてフォームの画像とブレンドし
というのだけでも私には敷居が高く、難しそうです。
>面白そうなので試しに作ってみました。
と言えるYASさんすごすぎです。

せっかく書き換えたので一応C#のソースも貼っておきます。

コード:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

public class Form1 : System.Windows.Forms.Form
{
	[DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hwnd);
	[DllImport("user32.dll")] private  static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);
	[DllImport("user32.dll")] private static extern bool UpdateWindow(IntPtr hWnd);
	[DllImport("user32.dll")] public static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);

	//RedrawWindow() flags
	private enum RDW : int 
	{ 
		INVALIDATE = 0x1,
		INTERNALPAINT = 0x2,
		ERASE = 0x4,
		VALIDATE = 0x8,
		NOINTERNALPAINT = 0x10,
		NOERASE = 0x20,
		NOCHILDREN = 0x40,
		ALLCHILDREN = 0x80,
		UPDATENOW = 0x100,
		ERASENOW = 0x200,
		FRAME = 0x400,
		NOFRAME = 0x800
	}

	private void RefreshDesktop() 
	{ 
		RedrawWindow(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 
			(uint)(RDW.ALLCHILDREN | RDW.UPDATENOW | RDW.ERASE | RDW.INVALIDATE | RDW.INTERNALPAINT)); 
		UpdateWindow(IntPtr.Zero); 
	}

	private void button1_Click(object sender, System.EventArgs e)
	{
		IntPtr hDC = GetDC(IntPtr.Zero);
		using (Graphics g = Graphics.FromHdc(hDC))
		{
			Form f = new Form(); 
			//Bitmap b = new Bitmap(f.Width, f.Height); 
			//f.DrawToBitmap(b, new Rectangle(0, 0, b.Width, b.Height)); 
			//DrawToBitmapはFramework2.0対応のようなので、代わりに画像の表示で試す
			Bitmap b = new Bitmap(@"c:\\pic.jpg"); 
			ImageAttributes a = new ImageAttributes(); 
			ColorMatrix m = new ColorMatrix(); 
			m.Matrix00 = 1; 
			m.Matrix11 = 1; 
			m.Matrix22 = 1; 
			m.Matrix33 = 0.5f; 
			m.Matrix44 = 1; 
			a.SetColorMatrix(m); 
			Rectangle s = Screen.PrimaryScreen.Bounds;
			g.DrawImage(
				b,
				new Rectangle(new Point(System.Convert.ToInt32((s.Width - b.Width) / 2),
				System.Convert.ToInt32((s.Height - b.Height) / 2)), b.Size),
				0,
				0,
				b.Width,
				b.Height, GraphicsUnit.Pixel, a);
		}
		ReleaseDC(IntPtr.Zero, hDC);
	}

	private void button2_Click(object sender, System.EventArgs e)
	{
		RefreshDesktop();
	}

	private void Form1_Load(object sender, System.EventArgs e) 
	{ 
		Button b1 = new Button(); 
		b1.Text = "表示"; 
		this.Controls.Add(b1); 
		b1.Click += new EventHandler(button1_Click); 
		Button b2 = new Button(); 
		b2.Text = "消去"; 
		b2.Location = new Point(0, 30); 
		this.Controls.Add(b2); 
		b2.Click += new EventHandler(button2_Click); 
	}

	public Form1()
	{
		Load += new EventHandler(Form1_Load);
	}

	[STAThread]
	static void Main() 
	{
		Application.Run(new Form1());
	}
}

1

スキルアップ/キャリアアップ(JOB@IT)