概要
画面上に情報を被せて表示する必要があったので、色々調べた。
開発・検証環境
- Microsoft Visual Studio 2017
- .Net Framework 4.7
- Windows 10 Pro [1709] Build:16299.192
Frameworkは、4.7を使用していますが以前のバージョンでも動作するとは思います。
要件
- 透過背景
- 全画面表示
- 最前面表示
- クリック・スルー
- タスクバーに表示しない
- システムメニュー非表示
- Alt+F4で閉じない
1. 透過背景
Window のPropertyを以下の様に設定することで、透過することができます。
WindowStyle = WindowStyle.None
AllowsTransparency = true
Background = new SolidColorBrush( Colors.Transparent )
Xaml で書くとこんな感じ
<Window x:Class="hogehoge.Views.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="OverlayWindow"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent">
<Grid>
</Grid>
</Window>
2. 全画面表示
Window のPropertyを以下の様に設定することで、全画面表示にできます。
WindowState = WindowState.Maximized
Xaml で書くとこんな感じ
<Window x:Class="hogehoge.Views.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="OverlayWindow"
WindowState="Maximized">
<Grid>
</Grid>
</Window>
3. 最前面表示
Window のPropertyを以下の様に設定することで、最前面固定表示にできます。
Topmost = true
Xaml で書くとこんな感じ
<Window x:Class="hogehoge.Views.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="OverlayWindow"
Topmost="True">
<Grid>
</Grid>
</Window>
4. クリック・スルー
Windowのクリックをスルーするには、拡張Windowスタイルを変更する必要があります。
拡張Windowスタイルの取得と設定に P/Invoke を使います。
ひとまず、OverlayWindow
の分離コードに記述していきます。
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace hogehoge.Views {
/// <summary>
/// OverlayWindow.xaml の相互作用ロジック
/// </summary>
public partial class OverlayWindow : Window {
protected const int GWL_EXSTYLE = ( -20 );
protected const int WS_EX_TRANSPARENT = 0x00000020;
[DllImport( "user32" )]
protected static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport( "user32" )]
protected static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwLong);
public OverlayWindow() {
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized( e );
//WindowHandle(Win32) を取得
var handle = new WindowInteropHelper( this ).Handle;
//クリックをスルー
int extendStyle = GetWindowLong( handle, GWL_EXSTYLE );
extendStyle |= WS_EX_TRANSPARENT; //フラグの追加
SetWindowLong( handle, GWL_EXSTYLE, extendStyle );
}
}
}
演算子に関しては以下を参照
さて、ここまですればオーバーレイ用のWindowとして使用できます。
ですが、Alt+Space でシステムメニューが開けてしまったりと、残念なポイントがあるので修正していきます。
5. タスクバーに表示しない
Window のPropertyを以下の様に設定することで、タスクバーに表示されないようにできます。
ShowInTaskbar = false
Xaml で書くとこんな感じ
<Window x:Class="hogehoge.Views.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="OverlayWindow"
ShowInTaskbar="False">
<Grid>
</Grid>
</Window>
6. システムメニュー非表示
システムメニューが表示されないようにするには、 Windowスタイルを変更する必要があります。
Window
クラスのWindowStyle
プロパティではなく、NativeなWindowスタイルの設定です。
なので、これまた P/Invoke で対応していきます。
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace hogehoge.Views {
/// <summary>
/// OverlayWindow.xaml の相互作用ロジック
/// </summary>
public partial class OverlayWindow : Window {
protected const int GWL_STYLE = ( -16 );
protected const int WS_SYSMENU = 0x00080000;
[DllImport( "user32" )]
protected static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport( "user32" )]
protected static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwLong);
public OverlayWindow() {
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized( e );
//WindowHandle(Win32) を取得
var handle = new WindowInteropHelper( this ).Handle;
//システムメニュを非表示
int windowStyle = GetWindowLong( handle, GWL_STYLE );
windowStyle &= ~WS_SYSMENU; //フラグを消す
SetWindowLong( handle, GWL_STYLE, windowStyle );
}
}
}
演算子に関しては以下を参照
7. Alt+F4で閉じない
Windowプロシージャをフックして、Alt+F4が入力された時に処理済みとしてマークします。
using System;
using System.Windows;
using System.Windows.Interop;
namespace hogehoge.Views {
/// <summary>
/// OverlayWindow.xaml の相互作用ロジック
/// </summary>
public partial class OverlayWindow : Window {
protected const int WM_SYSKEYDOWN = 0x0104;
protected const int VK_F4 = 0x73;
public OverlayWindow() {
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized( e );
//WindowHandle(Win32) を取得
var handle = new WindowInteropHelper( this ).Handle;
//Alt + F4 を無効化
var hwndSource = HwndSource.FromHwnd( handle );
hwndSource.AddHook( WndProc );
}
protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr IParam, ref bool handled) {
//Alt + F4 が入力されたら
if ( msg == WM_SYSKEYDOWN && wParam.ToInt32() == VK_F4 ) {
//処理済みにセットする
//(Windowは閉じられなくなる)
handled = true;
}
return IntPtr.Zero;
}
}
}
ビヘイビアにまとめる
上で紹介したコード片をまとめて、ビヘイビアにします。
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Interop;
using System.Windows.Media;
namespace hogehoge.Behaviors {
public class OverlayWindowSettingBehavior : Behavior<Window> {
public const int GWL_STYLE = ( -16 ); // ウィンドウスタイル
public const int GWL_EXSTYLE = ( -20 ); // 拡張ウィンドウスタイル
public const int WS_SYSMENU = 0x00080000; // システムメニュを表示する
public const int WS_EX_TRANSPARENT = 0x00000020; // 透過ウィンドウスタイル
public const int WM_SYSKEYDOWN = 0x0104; // Alt + 任意のキー の入力
public const int VK_F4 = 0x73;
[DllImport( "user32" )]
protected static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport( "user32" )]
protected static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwLong);
protected override void OnAttached() {
base.OnAttached();
// 透過背景
this.AssociatedObject.WindowStyle = WindowStyle.None;
this.AssociatedObject.AllowsTransparency = true;
this.AssociatedObject.Background = new SolidColorBrush( Colors.Transparent );
// 全画面表示
this.AssociatedObject.WindowState = WindowState.Maximized;
// 最前面表示
this.AssociatedObject.Topmost = true;
//タスクバーに表示しない
this.AssociatedObject.ShowInTaskbar = false;
this.AssociatedObject.SourceInitialized += (sender, eventArgs) => {
//WindowHandle(Win32) を取得
var handle = new WindowInteropHelper( this.AssociatedObject ).Handle;
//システムメニュを非表示
int windowStyle = GetWindowLong( handle, GWL_STYLE );
windowStyle &= ~WS_SYSMENU; //フラグを消す
SetWindowLong( handle, GWL_STYLE, windowStyle );
//クリックをスルー
int extendStyle = GetWindowLong( handle, GWL_EXSTYLE );
extendStyle |= WS_EX_TRANSPARENT; //フラグの追加
SetWindowLong( handle, GWL_EXSTYLE, extendStyle );
//Alt + F4 を無効化
var hwndSource = HwndSource.FromHwnd( handle );
hwndSource.AddHook( WndProc );
};
}
protected IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr IParam, ref bool handled ) {
//Alt + F4 が入力されたら
if ( msg == WM_SYSKEYDOWN && wParam.ToInt32() == VK_F4 ) {
//処理済みにセットする
//(Windowは閉じられなくなる)
handled = true;
}
return IntPtr.Zero;
}
}
}
これを、Windowに適用します。
<Window x:Class="hogehoge.Views.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:behavior="clr-namespace:hogehoge.Behaviors"
Title="OverlayWindow">
<i:Interaction.Behaviors>
<behavior:OverlayWindowSettingBehavior/>
</i:Interaction.Behaviors>
<Grid>
</Grid>
</Window>
ビヘイビアにまとめない
ビヘイビアにするより、継承元のクラスとした方が使い勝手が良かった(^^;)
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
namespace hogehoge
{
public class OverlayWindow : Window
{
#region DependencyProperties
#region AltF4Cancel
public bool AltF4Cancel {
get { return (bool)GetValue(AltF4CancelProperty); }
set { SetValue(AltF4CancelProperty, value); }
}
// Using a DependencyProperty as the backing store for AltF4Cancel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AltF4CancelProperty =
DependencyProperty.Register(
"AltF4Cancel",
typeof(bool),
typeof(OverlayWindow),
new PropertyMetadata(true));
#endregion
#region ShowSystemMenu
public bool ShowSystemMenu {
get { return (bool)GetValue(ShowSystemMenuProperty); }
set { SetValue(ShowSystemMenuProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowSystemMenu. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowSystemMenuProperty =
DependencyProperty.Register(
"ShowSystemMenu",
typeof(bool),
typeof(OverlayWindow),
new PropertyMetadata(
false,
ShowSystemMenuPropertyChanged));
private static void ShowSystemMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is OverlayWindow window)
{
window.SetShowSystemMenu( (bool)e.NewValue );
}
}
#endregion
#region ClickThrough
public bool ClickThrough {
get { return (bool)GetValue(ClickThroughProperty); }
set { SetValue(ClickThroughProperty, value); }
}
// Using a DependencyProperty as the backing store for ClickThrough. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickThroughProperty =
DependencyProperty.Register(
"ClickThrough",
typeof(bool),
typeof(OverlayWindow),
new PropertyMetadata(
true,
ClickThroughPropertyChanged));
private static void ClickThroughPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is OverlayWindow window)
{
window.SetClickThrough((bool)e.NewValue);
}
}
#endregion
#endregion
#region const values
private const int GWL_STYLE = (-16); // ウィンドウスタイル
private const int GWL_EXSTYLE = (-20); // 拡張ウィンドウスタイル
private const int WS_SYSMENU = 0x00080000; // システムメニュを表示する
private const int WS_EX_TRANSPARENT = 0x00000020; // 透過ウィンドウスタイル
private const int WM_SYSKEYDOWN = 0x0104; // Alt + 任意のキー の入力
private const int VK_F4 = 0x73;
#endregion
#region Win32Apis
[DllImport("user32")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwLong);
#endregion
public OverlayWindow()
{
// 透過背景
this.WindowStyle = WindowStyle.None;
this.AllowsTransparency = true;
this.Background = new SolidColorBrush(Colors.Transparent);
// 全画面表示
this.WindowState = WindowState.Maximized;
// 最前面表示
this.Topmost = true;
//タスクバーに表示しない
this.ShowInTaskbar = false;
}
protected override void OnSourceInitialized(EventArgs e)
{
//システムメニュを非表示
this.SetShowSystemMenu(this.ShowSystemMenu);
//クリックをスルー
this.SetClickThrough( this.ClickThrough );
//Alt + F4 を無効化
var handle = new WindowInteropHelper(this).Handle;
var hwndSource = HwndSource.FromHwnd(handle);
hwndSource.AddHook(WndProc);
base.OnSourceInitialized(e);
}
protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr IParam, ref bool handled)
{
//Alt + F4 が入力されたら
if (msg == WM_SYSKEYDOWN && wParam.ToInt32() == VK_F4)
{
if (this.AltF4Cancel)
{
//処理済みにセットする
//(Windowは閉じられなくなる)
handled = true;
}
}
return IntPtr.Zero;
}
/// <summary>
/// システムメニュの表示を切り替える
/// </summary>
/// <param name="value"><see langword="true"/> = 表示, <see langword="false"/> = 非表示</param>
protected void SetShowSystemMenu( bool value )
{
try
{
var handle = new WindowInteropHelper(this).Handle;
int windowStyle = GetWindowLong(handle, GWL_STYLE);
if (value)
{
windowStyle |= WS_SYSMENU; //フラグの追加
}
else
{
windowStyle &= ~WS_SYSMENU; //フラグを消す
}
SetWindowLong(handle, GWL_STYLE, windowStyle);
}
catch
{
}
}
/// <summary>
/// クリックスルーの設定
/// </summary>
/// <param name="value"><see langword="true"/> = クリックをスルー, <see langword="false"/>=クリックを捉える</param>
protected void SetClickThrough(bool value)
{
try
{
var handle = new WindowInteropHelper(this).Handle;
int extendStyle = GetWindowLong(handle, GWL_EXSTYLE);
if (value)
{
extendStyle |= WS_EX_TRANSPARENT; //フラグの追加
}
else
{
extendStyle &= ~WS_EX_TRANSPARENT; //フラグを消す
}
SetWindowLong(handle, GWL_EXSTYLE, extendStyle);
}
catch
{
}
}
}
}
参考文献
- [WPFアプリケーションでウィンドウプロシージャをフックする]
(https://qiita.com/tricogimmick/items/86141bc33c0e06e9d2e9)
- [WPF でシステムメニューのないウィンドウを作る]
(http://grabacr.net/archives/208)
- [WPFでフルスクリーン表示]
(http://sourcechord.hatenablog.com/entry/20131117/1384697484)