Search code examples
c#wpfwindowcustomizationtitlebar

Dragging custom window title bar from top when maximized does not work


I have a custom title bar and with the window style set to none. On the click of the title bar I check to see if it is a double click (that does window maximize and restore) if it is not double clicked I do Window.DragMove. This works great for the snapping to the side and top. But when I try to drag the window when it is maximized (which would normally restore the window down), it doesn't do anything. Here is my code:

    static Window Window { get { return Application.Current.MainWindow; } }

    /// <summary>
    /// TitleBar_MouseDown - Drag if single-click, resize if double-click
    /// </summary>
    private static void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Left)
        {
            if (e.ClickCount == 2)
            {
                AdjustWindowSize();
            }
            else
            {
                Window.DragMove();//Here is where I do the drag move
            }
        }
    }

    /// <summary>
    /// Adjusts the WindowSize to correct parameters when Maximize button is clicked
    /// </summary>
    internal static void AdjustWindowSize()
    {
        if (Window.WindowState == WindowState.Maximized)
        {
            SystemCommands.RestoreWindow(Window);
        }
        else
        {
            SystemCommands.MaximizeWindow(Window);
        }

    }

    #region Button Events

    /// <summary>
    /// CloseButton_Clicked
    /// </summary>
    public static void Close()
    {
        SystemCommands.CloseWindow(Window);
    }

    /// <summary>
    /// MaximizedButton_Clicked
    /// </summary>
    public static void Maximize()
    {
        AdjustWindowSize();
    }

    /// <summary>
    /// Minimized Button_Clicked
    /// </summary>
    public static void Minimize()
    {
        SystemCommands.MinimizeWindow(Window);
    }

    #endregion

Modern UI and MahApps.Metro does it somehow and I looked at their source code briefly but could not find how they do it.

Thanks in advance.


Solution

  • I am able to get the desired behavior of title bar including aero snap in pure xaml

    as result you can see a custom title bar, it is completely draggable, double click to maximize and restore and drag to snap and unsnap.

    xaml

    <Window x:Class="CSharpWPF.MainWindow" 
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Title="MainWindow" >
        <WindowChrome.WindowChrome>
            <WindowChrome CaptionHeight="{Binding ActualHeight,ElementName=titlebar}"/>
        </WindowChrome.WindowChrome>
        <DockPanel LastChildFill="True">
            <Border Background="LightBlue" DockPanel.Dock="Top" Height="25" x:Name="titlebar">
                <TextBlock Text="{Binding Title, RelativeSource={RelativeSource FindAncestor,AncestorType=Window},FallbackValue=Title}" 
                           Margin="10,0,0,0"
                           VerticalAlignment="Center">
                    <TextBlock.Effect>
                        <DropShadowEffect Color="White" ShadowDepth="3"/>
                    </TextBlock.Effect>
                </TextBlock>
            </Border>
            <Border BorderBrush="LightGray" BorderThickness="1" Padding="4">
                <TextBlock Text="Window content"/>
            </Border>
        </DockPanel>
    </Window>
    

    result

    result

    so now you don't need any code behind to handle title bar manually.

    Reusable styles

    you can also wrap above in a custom style which you can apply on several windows

    <Style TargetType="Window" x:Key="CustomTitleBar">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CaptionHeight="{x:Static SystemParameters.CaptionHeight}" />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Window">
                    <DockPanel LastChildFill="True">
                        <Border Background="LightBlue" DockPanel.Dock="Top" 
                                Height="{x:Static SystemParameters.CaptionHeight}" x:Name="titlebar">
                            <Grid>
                                <TextBlock Text="{TemplateBinding Title}" 
                                            Margin="10,0,0,0"
                                            VerticalAlignment="Center">
                                    <TextBlock.Effect>
                                        <DropShadowEffect Color="White" ShadowDepth="3"/>
                                    </TextBlock.Effect>
                                </TextBlock>
                            </Grid>
                        </Border>
                        <Border Background="{TemplateBinding Background}" BorderBrush="LightGray" 
                                BorderThickness="1" Padding="4">
                            <ContentPresenter/>
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    usage

    <Window x:Class="CSharpWPF.View" 
                    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    Title="MainWindow" 
                    Style="{StaticResource CustomTitleBar}" >
        <TextBlock Text="Window content"/>
    </Window>
    

    How to implement in your code

    After looking at your code, I did manage to implement it with very little changes

    Changes are

    File: CustomChrome.cs

    line 41: change CaptionHeight = 36, currently it is 0. this should be equal to your title bar height

    var chrome = new WindowChrome() { GlassFrameThickness = new Thickness(-1), CaptionHeight = 36 };
    

    line 60: remove ((FrameworkElement)sender).MouseDown += TitleBar_MouseDown; as not required

    line 70: remove no more used event TitleBar_MouseDown

    File: CornerButtons.xaml

    line 13: add WindowChrome.IsHitTestVisibleInChrome="True" to StackPanel

        <StackPanel SnapsToDevicePixels="True" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
    

    File: MainWindow.xaml

    line 17: add WindowChrome.IsHitTestVisibleInChrome="True" to StackPanel

    <cc:CornerButtons Grid.Column="2">
        <StackPanel Orientation="Horizontal"
                    WindowChrome.IsHitTestVisibleInChrome="True">
    

    this is all, and your app will have a normal title bar without need to handle custom logic