WPFでモーダルダイアログを書いています。閉じるボタンがないように WPF ウィンドウを設定するにはどうすればよいですか? WindowState
通常のタイトルバーが欲しいのですが。
ResizeMode
、WindowState
、およびを見つけましWindowStyle
たが、モーダル ダイアログのように、閉じるボタンを非表示にしてタイトル バーを表示できるプロパティはありません。
WPF には、タイトル バーの [閉じる] ボタンを非表示にする組み込みのプロパティはありませんが、数行の P/Invoke でそれを行うことができます。
まず、これらの宣言を Window クラスに追加します。
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x80000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
次に、このコードを Window のLoaded
イベントに入れます。
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
これで、閉じるボタンがなくなりました。また、タイトル バーの左側にウィンドウ アイコンが表示されません。つまり、タイトル バーを右クリックしても、システム メニューが表示されません。これらはすべて一緒に表示されます。
重要な注意:これはボタンを非表示にするだけです。ユーザーは引き続きウィンドウを閉じることができます。Altユーザーが+を押すF4か、タスクバーからアプリを閉じると、ウィンドウは閉じられます。
バックグラウンド スレッドが完了する前にウィンドウを閉じたくない場合は、Gabe が提案したようにオーバーライドして true にOnClosing
設定することもできます。Cancel
私はちょうど同様の問題に直面しました、そして、ジョーホワイトの解決策は私には単純できれいに見えます。再利用して、Windowの添付プロパティとして定義しました
public class WindowBehavior
{
private static readonly Type OwnerType = typeof (WindowBehavior);
#region HideCloseButton (attached property)
public static readonly DependencyProperty HideCloseButtonProperty =
DependencyProperty.RegisterAttached(
"HideCloseButton",
typeof (bool),
OwnerType,
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(HideCloseButtonChangedCallback)));
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHideCloseButton(Window obj) {
return (bool)obj.GetValue(HideCloseButtonProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHideCloseButton(Window obj, bool value) {
obj.SetValue(HideCloseButtonProperty, value);
}
private static void HideCloseButtonChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window == null) return;
var hideCloseButton = (bool)e.NewValue;
if (hideCloseButton && !GetIsHiddenCloseButton(window)) {
if (!window.IsLoaded) {
window.Loaded += HideWhenLoadedDelegate;
}
else {
HideCloseButton(window);
}
SetIsHiddenCloseButton(window, true);
}
else if (!hideCloseButton && GetIsHiddenCloseButton(window)) {
if (!window.IsLoaded) {
window.Loaded -= ShowWhenLoadedDelegate;
}
else {
ShowCloseButton(window);
}
SetIsHiddenCloseButton(window, false);
}
}
#region Win32 imports
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x80000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
#endregion
private static readonly RoutedEventHandler HideWhenLoadedDelegate = (sender, args) => {
if (sender is Window == false) return;
var w = (Window)sender;
HideCloseButton(w);
w.Loaded -= HideWhenLoadedDelegate;
};
private static readonly RoutedEventHandler ShowWhenLoadedDelegate = (sender, args) => {
if (sender is Window == false) return;
var w = (Window)sender;
ShowCloseButton(w);
w.Loaded -= ShowWhenLoadedDelegate;
};
private static void HideCloseButton(Window w) {
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
private static void ShowCloseButton(Window w) {
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
#endregion
#region IsHiddenCloseButton (readonly attached property)
private static readonly DependencyPropertyKey IsHiddenCloseButtonKey =
DependencyProperty.RegisterAttachedReadOnly(
"IsHiddenCloseButton",
typeof (bool),
OwnerType,
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsHiddenCloseButtonProperty =
IsHiddenCloseButtonKey.DependencyProperty;
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetIsHiddenCloseButton(Window obj) {
return (bool)obj.GetValue(IsHiddenCloseButtonProperty);
}
private static void SetIsHiddenCloseButton(Window obj, bool value) {
obj.SetValue(IsHiddenCloseButtonKey, value);
}
#endregion
}
次に、XAMLでは次のように設定します。
<Window
x:Class="WafClient.Presentation.Views.SampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:WafClient.Presentation.Behaviors"
ResizeMode="NoResize"
u:WindowBehavior.HideCloseButton="True">
...
</Window>
WindowStyle
プロパティを None に設定すると、コントロール ボックスとタイトル バーが非表示になります。呼び出しをカーネル化する必要はありません。
これで閉じるボタンがなくなるわけではありませんが、誰かがウィンドウを閉じるのを止めることができます。
これをコード ビハインド ファイルに入れます。
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
}
閉じるボタンを無効にするには、次のコードをWindowクラスに追加する必要があります(コードはここから取得され、少し編集および再フォーマットされています)。
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwndSource.AddHook(HwndSourceHook);
}
}
private bool allowClosing = false;
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
private const int WM_SHOWWINDOW = 0x00000018;
private const int WM_CLOSE = 0x10;
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_SHOWWINDOW:
{
IntPtr hMenu = GetSystemMenu(hwnd, false);
if (hMenu != IntPtr.Zero)
{
EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
}
}
break;
case WM_CLOSE:
if (!allowClosing)
{
handled = true;
}
break;
}
return IntPtr.Zero;
}
このコードは、システムメニューの閉じる項目も無効にし、Alt+F4を使用してダイアログを閉じることを禁止します。
プログラムでウィンドウを閉じたいと思うかもしれません。電話をかけるだけでClose()
はうまくいきません。このようなことをします:
allowClosing = true;
Close();
ボタンを削除せずに無効にするというアイデアが好きなので、Vichaslauの答えを試していましたが、何らかの理由で常に機能するとは限りませんでした.閉じるボタンはまだ有効になっていますが、エラーはまったくありません.
一方、これは常に機能しました(エラーチェックは省略されました):
[DllImport( "user32.dll" )]
private static extern IntPtr GetSystemMenu( IntPtr hWnd, bool bRevert );
[DllImport( "user32.dll" )]
private static extern bool EnableMenuItem( IntPtr hMenu, uint uIDEnableItem, uint uEnable );
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
private const int WM_SHOWWINDOW = 0x00000018;
protected override void OnSourceInitialized( EventArgs e )
{
base.OnSourceInitialized( e );
var hWnd = new WindowInteropHelper( this );
var sysMenu = GetSystemMenu( hWnd.Handle, false );
EnableMenuItem( sysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED );
}
ユーザーがウィンドウを「閉じる」ようにしますが、実際には非表示にします。
ウィンドウの OnClosing イベントで、既に表示されている場合はウィンドウを非表示にします。
If Me.Visibility = Windows.Visibility.Visible Then
Me.Visibility = Windows.Visibility.Hidden
e.Cancel = True
End If
バックグラウンド スレッドが実行されるたびに、バックグラウンド UI ウィンドウを再表示します。
w.Visibility = Windows.Visibility.Visible
w.Show()
プログラムの実行を終了するときは、すべてのウィンドウを閉じる/閉じることができることを確認してください。
Private Sub CloseAll()
If w IsNot Nothing Then
w.Visibility = Windows.Visibility.Collapsed ' Tell OnClosing to really close
w.Close()
End If
End Sub
だから、ほとんどここにあなたの問題があります。ウィンドウ フレームの右上にある閉じるボタンは、WPF ウィンドウの一部ではありませんが、OS によって制御されるウィンドウ フレームの一部に属しています。これは、Win32 相互運用機能を使用してそれを行う必要があることを意味します。
または、noframe を使用して、独自の「フレーム」を提供するか、フレームをまったく持たないようにすることもできます。
以下は、閉じるボタンと最大化/最小化ボタンを無効にすることに関するもので、実際にはボタンを削除しません(ただし、メニュー項目は削除されます!)。タイトル バーのボタンは、無効/灰色の状態で描画されます。(すべての機能を自分で引き継ぐ準備はまだできていません ^^)
これは、単にメニュー項目を無効にするのではなく、メニュー項目 (および必要に応じて末尾の区切り記号) を削除するという点で、Virgoss ソリューションとは少し異なります。システムメニュー全体を無効にするわけではないため、Joe Whites ソリューションとは異なります。したがって、私の場合は、最小化ボタンとアイコンを保持できます。
次のコードは、最大化/最小化ボタンの無効化もサポートします。これは、[閉じる] ボタンとは異なり、メニューからエントリを削除しても、メニュー エントリを削除するとボタンの機能が無効になりますが、システムはボタンを「無効」にしないためです。
わたしにはできる。YMMV。
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Window = System.Windows.Window;
using WindowInteropHelper = System.Windows.Interop.WindowInteropHelper;
using Win32Exception = System.ComponentModel.Win32Exception;
namespace Channelmatter.Guppy
{
public class WindowUtil
{
const int MF_BYCOMMAND = 0x0000;
const int MF_BYPOSITION = 0x0400;
const uint MFT_SEPARATOR = 0x0800;
const uint MIIM_FTYPE = 0x0100;
[DllImport("user32", SetLastError=true)]
private static extern uint RemoveMenu(IntPtr hMenu, uint nPosition, uint wFlags);
[DllImport("user32", SetLastError=true)]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32", SetLastError=true)]
private static extern int GetMenuItemCount(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
public struct MenuItemInfo {
public uint cbSize;
public uint fMask;
public uint fType;
public uint fState;
public uint wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public IntPtr dwItemData; // ULONG_PTR
public IntPtr dwTypeData;
public uint cch;
public IntPtr hbmpItem;
};
[DllImport("user32", SetLastError=true)]
private static extern int GetMenuItemInfo(
IntPtr hMenu, uint uItem,
bool fByPosition, ref MenuItemInfo itemInfo);
public enum MenuCommand : uint
{
SC_CLOSE = 0xF060,
SC_MAXIMIZE = 0xF030,
}
public static void WithSystemMenu (Window win, Action<IntPtr> action) {
var interop = new WindowInteropHelper(win);
IntPtr hMenu = GetSystemMenu(interop.Handle, false);
if (hMenu == IntPtr.Zero) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get system menu");
} else {
action(hMenu);
}
}
// Removes the menu item for the specific command.
// This will disable and gray the Close button and disable the
// functionality behind the Maximize/Minimuze buttons, but it won't
// gray out the Maximize/Minimize buttons. It will also not stop
// the default Alt+F4 behavior.
public static void RemoveMenuItem (Window win, MenuCommand command) {
WithSystemMenu(win, (hMenu) => {
if (RemoveMenu(hMenu, (uint)command, MF_BYCOMMAND) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to remove menu item");
}
});
}
public static bool RemoveTrailingSeparator (Window win) {
bool result = false; // Func<...> not in .NET3 :-/
WithSystemMenu(win, (hMenu) => {
result = RemoveTrailingSeparator(hMenu);
});
return result;
}
// Removes the final trailing separator of a menu if it exists.
// Returns true if a separator is removed.
public static bool RemoveTrailingSeparator (IntPtr hMenu) {
int menuItemCount = GetMenuItemCount(hMenu);
if (menuItemCount < 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get menu item count");
}
if (menuItemCount == 0) {
return false;
} else {
uint index = (uint)(menuItemCount - 1);
MenuItemInfo itemInfo = new MenuItemInfo {
cbSize = (uint)Marshal.SizeOf(typeof(MenuItemInfo)),
fMask = MIIM_FTYPE,
};
if (GetMenuItemInfo(hMenu, index, true, ref itemInfo) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get menu item info");
}
if (itemInfo.fType == MFT_SEPARATOR) {
if (RemoveMenu(hMenu, index, MF_BYPOSITION) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to remove menu item");
}
return true;
} else {
return false;
}
}
}
private const int GWL_STYLE = -16;
[Flags]
public enum WindowStyle : int
{
WS_MINIMIZEBOX = 0x00020000,
WS_MAXIMIZEBOX = 0x00010000,
}
// Don't use this version for dealing with pointers
[DllImport("user32", SetLastError=true)]
private static extern int SetWindowLong (IntPtr hWnd, int nIndex, int dwNewLong);
// Don't use this version for dealing with pointers
[DllImport("user32", SetLastError=true)]
private static extern int GetWindowLong (IntPtr hWnd, int nIndex);
public static int AlterWindowStyle (Window win,
WindowStyle orFlags, WindowStyle andNotFlags)
{
var interop = new WindowInteropHelper(win);
int prevStyle = GetWindowLong(interop.Handle, GWL_STYLE);
if (prevStyle == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to get window style");
}
int newStyle = (prevStyle | (int)orFlags) & ~((int)andNotFlags);
if (SetWindowLong(interop.Handle, GWL_STYLE, newStyle) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
"Failed to set window style");
}
return prevStyle;
}
public static int DisableMaximizeButton (Window win) {
return AlterWindowStyle(win, 0, WindowStyle.WS_MAXIMIZEBOX);
}
}
}
使用法: これは、ソースが初期化された後に行う必要があります。適切な場所は、Window の SourceInitialized イベントを使用することです。
Window win = ...; /* the Window :-) */
WindowUtil.DisableMaximizeButton(win);
WindowUtil.RemoveMenuItem(win, WindowUtil.MenuCommand.SC_MAXIMIZE);
WindowUtil.RemoveMenuItem(win, WindowUtil.MenuCommand.SC_CLOSE);
while (WindowUtil.RemoveTrailingSeparator(win))
{
//do it here
}
Alt+F4 機能を無効にする簡単な方法は、キャンセル イベントを接続し、本当にウィンドウを閉じたいときにフラグを設定することです。
ウィンドウに Closing イベントを追加してみてください。このコードをイベント ハンドラーに追加します。
e.Cancel = true;
これにより、ウィンドウが閉じなくなります。これは、閉じるボタンを非表示にするのと同じ効果があります。
他の回答で述べたようにWindowStyle="None"
、タイトルバーを完全に削除するために使用できます。
また、他の回答へのコメントに記載されているように、これによりウィンドウをドラッグできなくなり、初期位置から移動するのが難しくなります。
ただし、ウィンドウのコード ビハインド ファイルのコンストラクターに 1 行のコードを追加することで、これを克服できます。
MouseDown += delegate { DragMove(); };
または、Lambda 構文を好む場合:
MouseDown += (sender, args) => DragMove();
これにより、ウィンドウ全体がドラッグ可能になります。ボタンなど、ウィンドウに存在する対話型コントロールは通常どおり機能し、ウィンドウのドラッグ ハンドルとしては機能しません。
XAML コード
<Button Command="Open" Content="_Open">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
動作するはずです
編集- このスレッドは、それがどのように行われるかを示していますが、ウィンドウには通常のタイトルバーを失うことなく必要なものを取得するプロパティがあるとは思いません。
編集2 このスレッドはそれを行う方法を示していますが、独自のスタイルをシステムメニューに適用する必要があり、それを行う方法を示しています。
goto ウィンドウ プロパティ セット
window style = none;
閉じるボタンがつかない…
これに対する答えをよく探した後、他の人に役立つことを願ってここで共有するこの簡単な解決策を考え出しました.
設定しWindowStyle=0x10000000
ました。
これにより、ウィンドウ スタイルのWS_VISIBLE (0x10000000)
との値が設定されます。WS_OVERLAPPED (0x0)
"Overlapped" は、タイトル バーとウィンドウの境界線を表示するために必要な値です。スタイルの値からWS_MINIMIZEBOX (0x20000)
、WS_MAXIMIZEBOX (0x10000)
、およびの値を削除することで、[閉じる] ボタンを含むすべてのボタンがタイトル バーから削除されました。WS_SYSMENU (0x80000)
を使用するWindowStyle="SingleBorderWindow"
と、WPF ウィンドウから最大ボタンと最小ボタンが非表示になります。