Search code examples
wpfwindow-chrome

WindowChrome button not hit-test visible when disabled


In WPF you can customize the caption bar using WindowChrome. Adding buttons in the non-client area is fairly simple as long as you remember to set WindowChrome.IsHitTestVisibleInChrome="True".

Now, there is a bug or unexpected/weird behavior when a disabled button is double-clicked. Nothing should happen, but instead the app is maximized.

Steps to reproduce

  1. Create a new WPF project. Preferably targeting .NET 6.
  2. Paste the code below in MainWindow.xaml.
  3. Run the app and double-click the button.

Expected: Nothing happens

Actual: The app is maximized

<Window x:Class="WpfChromeTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" WindowStyle="None" Height="450" Width="800">
    <WindowChrome.WindowChrome>
        <WindowChrome CaptionHeight="20" />
    </WindowChrome.WindowChrome>
    <Window.Template>
        <ControlTemplate TargetType="{x:Type Window}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="32" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Button Grid.Row="0" Width="200" Height="28" VerticalAlignment="Top"
                        Content="I'm disabled! DOUBLE-CLICK ME!"
                        WindowChrome.IsHitTestVisibleInChrome="True"
                        IsEnabled="False" />
            </Grid>
        </ControlTemplate>
    </Window.Template>
</Window>

What am I missing here? Is it a bug? And if so, is there a workaround?

Update

The accepted answer got it right. The reason why it works can be seen in WindowChromeWorker.cs(670) where the call to UIElement.InputHitTest will indeed skip any disabled element. However, on line 673 we find the magic that allows for the suggested solution:

When the parent element has WindowChrome.IsHitTestVisibleInChrome set to true, the callback will correctly return HTCLIENT, effectively swallowing our double click.

In the provided sample, we could simply replace <Grid> with the following, to get the desired behavior:

<Grid WindowChrome.IsHitTestVisibleInChrome="True">

Solution

  • Double clicking on the title bar causes the window to change its state,

    1. when the button is enabled, the double click will be handled by the button and not passed to the title bar.
    2. when the button is disabled, the double click will be passed to the title bar, this will happen if you replace the button with any disabled UIElement.

    So the current behavior is normal.

    Is there a workaround?

    Yes, If you want to disable WindowState change regardless of the button's IsEnabled value, wrap <Button/> with another UI element that will prevent the double click from being passed to the window's title bar if the button is disabled.

    <ContentControl
        Grid.Row="0"
        Width="200"
        Height="28"
        VerticalAlignment="Top"
        WindowChrome.IsHitTestVisibleInChrome="True">
        <Button
            x:Name="MyButton"
            Content="I'm disabled! DOUBLE-CLICK ME!"
            IsEnabled="False" />
    </ContentControl>