Как скрыть кнопку закрытия в окне WPF?

Я пишу модальный диалог в WPF. Как установить окно WPF, чтобы не было кнопки закрытия? Мне бы хотелось, чтобы у WindowState была обычная строка заголовка.

Я нашел ResizeMode, WindowState и WindowStyle, но ни одно из этих свойств не позволяет мне скрывать кнопку закрытия, но отображать строку заголовка, как в модальных диалогах.

Ответ 1

WPF не имеет встроенного свойства, чтобы скрыть кнопку заголовка Close, но вы можете сделать это несколькими строками 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 все равно закроет окно. Если вы не хотите, чтобы окно закрывалось до завершения фонового потока, вы также можете переопределить OnClosing и установить Cancel на true, как предположил Гейб.

Ответ 2

У меня появилась аналогичная проблема, и решение 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>

Ответ 3

Задайте для свойства WindowStyle значение None, которое скроет поле управления вместе с заголовком. Нет необходимости использовать вызовы ядра.

Ответ 4

Это не избавит вас от кнопки закрытия, но это прекратит закрывать окно.

Поместите это в свой код за файлом:

protected override void OnClosing(CancelEventArgs e)
{
   base.OnClosing(e);
   e.Cancel = true;
}

Ответ 5

Чтобы отключить кнопку закрытия, вы должны добавить следующий код в свой класс 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();

Ответ 6

Я пытался ответить Вячеславу, потому что мне нравится идея не удалять кнопку, а отключать ее, но по какой-то причине она не всегда срабатывала: кнопка закрытия все еще была включена, но никаких ошибок не было.

Это, с другой стороны, всегда работало (ошибка проверки опущена):

[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 );
}

Ответ 7

Свойство set = = 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">

Ответ 8

Я просто добавляю свою реализацию Joe White answer с помощью Interactivity Behavior (вам нужно ссылаться на System.Windows.Interactivity).

код:

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>

Ответ 9

Позвольте пользователю "закрыть" окно, но на самом деле просто скрыть его.

В окне OnClosing event скройте окно, если оно уже видно:

    If Me.Visibility = Windows.Visibility.Visible Then
        Me.Visibility = Windows.Visibility.Hidden
        e.Cancel = True
    End If

Каждый раз, когда выполняется фоновый поток, повторно отобразите фоновое окно пользовательского интерфейса:

    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

Ответ 10

Итак, в значительной степени это ваша проблема. Кнопка закрытия в верхнем правом углу окна не является частью окна WPF, но она принадлежит части оконного фрейма, который контролируется вашей ОС. Это означает, что вам придется использовать Win32 interop для этого.

альтернативно, вы можете использовать noframe и либо предоставить свой собственный "фрейм", либо вообще не иметь фрейма.

Ответ 11

Ниже приведено отключение кнопок закрытия и максимизации/минимизации, на самом деле кнопки не удаляются (но удаляются элементы меню!). Кнопки в строке заголовка отображаются в отключенном/сером состоянии. (Я не совсем готов взять на себя всю функциональность сам ^^)

Это немного отличается от решения 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);
            }
        }
    }

Использование: Это должно быть сделано ПОСЛЕ инициализации источника. Хорошее место - использовать событие 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, проще всего подключить событие отмены и использовать команду установки флага, когда вы действительно хотите закрыть окно.

Ответ 12

Код 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>

должен работать

Изменить - для вашего мгновенного Thread показывает, как это можно сделать, но я не Думаю, Окно имеет свойство получать то, что вы хотите, не теряя нормальной строки заголовка.

Изменить 2 Этот Thread показывает способ его выполнения, но вы должны применить свой собственный стиль к системному меню, и он показывает способ, как вы можете сделайте это.

Ответ 13

Свойства окна goto установлены

window style = none;

u не будет закрывать кнопки...

Ответ 14

Попробуйте добавить в окно событие Закрытие. Добавьте этот код в обработчик событий.

e.Cancel = true;

Это предотвратит закрытие окна. Это имеет тот же эффект, что и скрытие кнопки закрытия.

Ответ 15

Используйте это, измененное с 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);
        }  

    }
}

Ответ 16

После долгих поисков ответа на этот вопрос я разработал это простое решение, которым я поделюсь здесь в надежде, что оно поможет другим.

Я установил WindowStyle=0x10000000.

Это устанавливает значения WS_VISIBLE (0x10000000) и WS_OVERLAPPED (0x0) для стиля окна. "Перекрытый" является необходимым значением для отображения строки заголовка и границы окна. При удалении WS_MINIMIZEBOX (0x20000), WS_MAXIMIZEBOX (0x10000) и WS_SYSMENU (0x80000) из значения моего стиля все кнопки из строки заголовка были удалены, включая кнопку "Закрыть".

Ответ 17

Это не скрывает кнопку, но предотвратит продвижение пользователя, закрыв окно.

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{            
    if (e.Cancel == false)
    {
        Application.Current.Shutdown();
    }
}

Ответ 18

Как указано в других ответах, вы можете использовать WindowStyle="None", чтобы удалить панель заголовка вообще.

И, как указано в комментариях к этим другим ответам, это предотвращает перетаскивание окна, поэтому его трудно перенести из его исходного положения.

Однако вы можете преодолеть это, добавив одну строчку кода в конструктор в файле Window Code Behind:

MouseDown += delegate { DragMove(); };

Или, если вы предпочитаете синтаксис лямбда:

MouseDown += (sender, args) => DragMove();

Это делает все окно перетаскиваемым. Любые интерактивные элементы управления, присутствующие в окне, такие как кнопки, будут по-прежнему работать как обычно и не будут выступать в качестве управляющих клавиш для окна.

Ответ 19

Используйте WindowStyle="SingleBorderWindow", это скроет кнопку max и min из окна WPF.

Ответ 20

Если требуется только запретить пользователю закрывать окно, это простое решение.

Код XAML: IsCloseButtonEnabled="False"