WPF에서 모달 대화 상자를 작성 중입니다. 닫기 버튼이 없도록 WPF 창을 어떻게 설정합니까? 나는 여전히 WindowState
일반적인 제목 표시 줄을 갖고 싶습니다 .
내가 발견 ResizeMode
, WindowState
그리고 WindowStyle
하지만 이러한 속성 중 어느 것도 나를 닫기 버튼을 숨길 수 없지만 모달 대화 상자에서 같은 제목 표시 줄을 보여줍니다.
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가 제안한대로 재정의 OnClosing
하고 Cancel
true로 설정할 수도 있습니다 .
SetWindowLongPtr
대신 사용해야합니다 .
방금 비슷한 문제가 발생하여 Joe White의 솔루션 이 간단하고 깨끗해 보입니다. 재사용하고 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
속성을 없음으로 설정 하면 제목 표시 줄과 함께 컨트롤 상자가 숨겨집니다. 전화를 걸 필요가 없습니다.
this.DragMove();
창 MouseDown
이벤트 에 추가하여 창을 움직일 수 있습니다.
이것은 닫기 버튼을 제거하지는 않지만 누군가가 창을 닫는 것을 막을 것입니다.
이것을 코드 뒤에 파일 뒤에 넣으십시오.
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
}
Window
모달 대화 상자로 설정된 에서이 작업을 수행하면 속성 Window
설정 이 방해되어 DialogResult
사용할 수 없게 될 수 있습니다. stackoverflow.com/questions/898708/cant-set-dialogresult-in-wpf
닫기 버튼을 비활성화하려면 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();
나는 버튼을 제거하지 않고 비활성화하는 아이디어를 좋아했기 때문에 Viachaslau의 대답을 시도했지만 어떤 이유로 든 항상 작동하지는 않았습니다. 닫기 버튼은 여전히 활성화되었지만 오류는 없습니다.
반면에 이것은 항상 작동했습니다 (오류 검사 생략).
[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 );
}
Window
내 프로젝트에서 확장 방법 으로 추가되었습니다 .
설정할 속성은 =>입니다. WindowStyle="None"
<Window x:Class="mdaframework.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Start" Height="350" Width="525" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" WindowStyle="None">
Interactivity Behavior (System.Windows.Interactivity를 참조해야 함)를 사용하여 Joe White의 답변 구현을 추가 합니다.
암호:
public class HideCloseButtonOnWindow : Behavior<Window>
{
#region bunch of native methods
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
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= OnLoaded;
base.OnDetaching();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(AssociatedObject).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
용법:
<Window x:Class="WpfApplication2.MainWindow"
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:w="clr-namespace:WpfApplication2">
<i:Interaction.Behaviors>
<w:HideCloseButtonOnWindow />
</i:Interaction.Behaviors>
</Window>
사용자가 창을 "닫아"보도록하지만 실제로는 숨길 수 있습니다.
윈도우의 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
다음은 닫기 및 최대화 / 최소화 단추를 비활성화하는 방법에 대한 것이며 실제로 단추를 제거 하지는 않지만 메뉴 항목을 제거합니다! 제목 표시 줄의 버튼은 비활성화 / 회색 상태로 표시됩니다. (나는 모든 기능을 스스로 인수 할 준비가되어 있지 않습니다 ^^)
이는 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 기능을 비활성화하려면 쉬운 방법은 Canceling 이벤트를 연결하고 실제로 창을 닫고 싶을 때 플래그 설정을 사용하는 것입니다.
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>
작동해야합니다
편집 - 즉시이 스레드 는 수행 방법을 보여 주지만 Window에 일반 제목 표시 줄을 잃지 않고 원하는 것을 얻을 수있는 속성이 있다고 생각하지 않습니다.
편집 2 이 스레드 는 수행 방법을 보여 주지만 시스템 메뉴에 고유 한 스타일을 적용해야하며이를 수행하는 방법을 보여줍니다.
이를 https://stephenhaunts.com/2014/09/25/remove-the-close-button-from-a-wpf-window 에서 수정 하십시오 .
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
namespace Whatever
{
public partial class MainMenu : Window
{
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x00080000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
public MainMenu()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window_Loaded);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
}
}
다른 답변에서 언급했듯이 WindowStyle="None"
제목 표시 줄을 모두 제거하는 데 사용할 수 있습니다 .
그리고 다른 답변에 대한 주석에서 언급했듯이 창을 드래그 할 수 없으므로 초기 위치에서 창을 옮기기가 어렵습니다.
그러나 Window의 Code Behind 파일에서 생성자에 한 줄의 코드를 추가하여이를 극복 할 수 있습니다.
MouseDown += delegate { DragMove(); };
또는 Lambda 구문을 선호하는 경우 :
MouseDown += (sender, args) => DragMove();
그러면 전체 창을 드래그 할 수 있습니다. 버튼과 같이 윈도우에 존재하는 모든 대화 형 컨트롤은 여전히 정상적으로 작동하며 윈도우의 드래그 핸들로 작동하지 않습니다.
이에 대한 답을 많이 찾은 후에 다른 사람들을 돕기 위해 여기에서 공유 할 간단한 솔루션을 찾았습니다.
내가 설정했습니다 WindowStyle=0x10000000
.
윈도우 스타일 의 WS_VISIBLE (0x10000000)
및 WS_OVERLAPPED (0x0)
값을 설정합니다 . "겹침"은 제목 표시 줄과 창 테두리를 표시하는 데 필요한 값입니다. 을 제거함으로써 WS_MINIMIZEBOX (0x20000)
, WS_MAXIMIZEBOX (0x10000)
그리고 WS_SYSMENU (0x80000)
내 스타일 값의 값은, 제목 표시 줄에서 모든 버튼은 닫기 버튼을 포함하여 제거 하였다.
WindowStyle
값은 Windows API 상수와 일치하지 않는 열거입니다. 값을 WindowStyle
열거로 강제 변환하면 작동하지 않습니다. 확실히, ILSpy에서 .NET 소스 코드를 확인했습니다. 열거 형 값은 개인 함수에서 Windows API로 변환되며 CreateWindowStyle
함수에 알 수없는 WindowStyle
값이 있으면 간단히 적용됩니다 WindowStyle.None
. ( 내가 할 수있는 유일한 방법은 내부 속성 _Style
을 _StyleEx
사용하고 반사를 사용하는 것이 좋습니다.)
사용자가 창을 닫는 것을 금지해야하는 경우 이는 간단한 해결책입니다.
XAML 코드 :
IsCloseButtonEnabled="False"