我正在WPF中写一个模态对话框。我如何设置一个WPF窗口没有关闭按钮?我仍然希望它的WindowState有一个正常的标题栏。

我找到了ResizeMode、WindowState和WindowStyle,但这些属性都不允许我隐藏关闭按钮,而是显示标题栏,就像在模态对话框中一样。


当前回答

我尝试了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类(代码是从这里,编辑和重新格式化一点):

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();

让用户“关闭”窗口,但实际上只是隐藏它。

在窗口的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

下面是我如何实现类似的目标使用自定义样式没有DllImports和P/Invoke调用。这删除现有的标题栏使用WindowStyle="none",并显示一个'TextBlock'与类似的背景颜色表明作为标题栏。

XAML 代码

<Window x:Class="AddBook"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="http://wpftoolkit.my-libraries.com/v5" 
    WindowStartupLocation="CenterOwner"        
    ResizeMode="NoResize" 
    Style="{DynamicResource WindowStyleX}"
    ShowInTaskbar="False"
    ShowActivated="True"
    SizeToContent="Height"
    Title="Add New Book" 
    Width="450">
..............

</Window>

XAML

<Style x:Key="WindowStyleX" TargetType="{x:Type Window}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="ResizeMode" Value="NoResize" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type Window}">
            <Border BorderBrush="{DynamicResource BlackColor}" BorderThickness="1">
                <Grid Background="{TemplateBinding Background}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Border
                        Grid.Row="0"
                        Grid.ColumnSpan="2"
                        Background="{DynamicResource BlackColor}">
                        <Grid>
                            <TextBlock
                                Grid.Column="1"
                                Margin="10,0,0,0"
                                HorizontalAlignment="Left"
                                VerticalAlignment="Center"
                                FontSize="16"
                                Foreground="{DynamicResource WhiteTextForeground}"
                                Text="{TemplateBinding Title}" />
                        </Grid>
                    </Border>
                    <ContentPresenter Grid.Row="1" />
                </Grid>
            </Border>
        </ControlTemplate>
    </Setter.Value>
</Setter>

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 这个线程展示了一种方法,但你必须将自己的风格应用到系统菜单,它展示了一种方法,你可以这样做。

在寻找了很多答案之后,我想出了这个简单的解决方法,我将在这里分享,希望它能帮助到其他人。

我设置WindowStyle=0x10000000。

这设置了窗口样式的WS_VISIBLE (0x10000000)和WS_OVERLAPPED (0x0)值。"Overlapped"是显示标题栏和窗口边框的必要值。通过从我的样式值中删除WS_MINIMIZEBOX (0x20000)、WS_MAXIMIZEBOX (0x10000)和WS_SYSMENU (0x80000)值,标题栏中的所有按钮都被删除了,包括关闭按钮。