2

I have a hybrid WinForms/WPF desktop application running on Windows 10 that still targets .NET 3.5. It has a day mode, dusk mode, and night mode. In night mode everything basically goes red on black, but the title bars still default to white.

In Settings > Personalization > Colors > Show accent color on the following surfaces, you can check Title bars and get the accent color to be used for the title bars, and then choose a dark color. However, not all of our users are aware of this setting.

Is there a simple way to programatically change the title bar background and foreground colors for all Windows created from the application? I can use p/Invoke if I have to.

If not, I would also accept a way to programatically change the title bar colors globally (all Windows on the system), or even something that could offer to automatically check that box (accent color on title bars) and set a dark accent color.

--

Edit: Can it be done without having to set WindowStyle="None" and custom-implement the title bars? That is what I am (tired of) doing already.

Jason
  • 309
  • 1
  • 3
  • 16

1 Answers1

4

In WPF (and I assume WinForms), it is easier to create a borderless window and create the title bar yourself. Here is WPF code to do it,,,

First the C#:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();            
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void Notify(params string[] PropertyNames)
    {
        foreach (string PropertyName in PropertyNames)
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    }

    public Visibility CanMaximize { get { return WindowState == WindowState.Normal ? Visibility.Visible : Visibility.Collapsed;  } }
    public Visibility CanRestore { get { return WindowState == WindowState.Maximized ? Visibility.Visible : Visibility.Collapsed; } }

    private void CloseWindow(object sender, ExecutedRoutedEventArgs e)
    {
        Application.Current.Shutdown();
    }

    private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;            
    }

    private void MinimizeWindow(object sender, ExecutedRoutedEventArgs e)
    {
        this.WindowState = WindowState.Minimized;            
    }

    private void MaximizeWindow(object sender, ExecutedRoutedEventArgs e)
    {
        this.WindowState = WindowState.Maximized;
        Notify("CanMaximize", "CanRestore");
    }

    private void RestoreWindow(object sender, ExecutedRoutedEventArgs e)
    {
        this.WindowState = WindowState.Normal;
        Notify("CanMaximize", "CanRestore");
    }

    private void TitleBar_MouseMove(object sender, MouseEventArgs e)
    {
        if (Mouse.LeftButton == MouseButtonState.Pressed)            
        {
            if (TitleBar.IsMouseDirectlyOver)
                DragMove();
        }
    }

}

now the XAML:

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="Segoe UI Symbol" 
    mc:Ignorable="d" x:Name="root"
    Title="MainWindow" Height="450" Width="800" WindowStyle="None">
<Window.CommandBindings>
    <CommandBinding Command="SystemCommands.CloseWindowCommand" Executed="CloseWindow" CanExecute="CanExecute" />
    <CommandBinding Command="SystemCommands.MinimizeWindowCommand" Executed="MinimizeWindow"  CanExecute="CanExecute" />
    <CommandBinding Command="SystemCommands.MaximizeWindowCommand" Executed="MaximizeWindow"  CanExecute="CanExecute" />
    <CommandBinding Command="SystemCommands.RestoreWindowCommand" Executed="RestoreWindow"  CanExecute="CanExecute" />
</Window.CommandBindings>
<DockPanel>
    <DockPanel DockPanel.Dock="Top" Background="#FF000020" x:Name="TitleBar" MouseMove="TitleBar_MouseMove">
        <Image DockPanel.Dock="Left" Source="{Binding ElementName=root, Path=Icon}" Stretch="Uniform" Height="25" Width="25" Margin="2,0"/>
        <Button Command="SystemCommands.CloseWindowCommand" DockPanel.Dock="Right" Margin="0" Padding="0">
            <Grid Width="25" Height="25" Background="{Binding ElementName=TitleBar, Path=Background}">
                <TextBlock  Text="&#xE10A;" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />
            </Grid>
        </Button>
        <Button Command="SystemCommands.RestoreWindowCommand" DockPanel.Dock="Right" Visibility="{Binding ElementName=root, Path=CanRestore}">
            <Grid Width="25" Height="25" Background="{Binding ElementName=TitleBar, Path=Background}">
                <TextBlock  Text="&#xE158;" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />
            </Grid>
        </Button>
        <Button Command="SystemCommands.MaximizeWindowCommand" DockPanel.Dock="Right" Visibility="{Binding ElementName=root, Path=CanMaximize}">
            <Grid Width="25" Height="25" Background="{Binding ElementName=TitleBar, Path=Background}">
                <TextBlock  Text="&#x10c3f;" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />
            </Grid>
        </Button>
        <Button Command="SystemCommands.MinimizeWindowCommand" DockPanel.Dock="Right">
            <Grid Width="25" Height="25" Background="{Binding ElementName=TitleBar, Path=Background}">
                <TextBlock  Text="&#xE108;" HorizontalAlignment="Center" VerticalAlignment="Bottom" Foreground="White" />
            </Grid>
        </Button>
        <TextBlock Text="{Binding ElementName=root, Path=Title, FallbackValue='Application Title'}" Foreground="White" VerticalAlignment="Center"/>
    </DockPanel>
    <Grid>
        <TextBlock Text="Content goes here." VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Grid>
</DockPanel>

Stephen Lee Parker
  • 1,215
  • 9
  • 19
  • This (WindowStyle="None") does work, and is what we are already doing for a lot of our windows. It's tricky to get all the details (appearance and behavior) to exactly match the system title bars. And anywhere we use MessageBox.Show still has the Windows border and title. Your example looks thorough though. – Jason Jul 09 '18 at 22:33
  • Well after posting the answer, I liked it so much, I created a project template and added some dependency properties to make my life easier. I like that it gives me control over all the buttons and I added a help button as well. So, I'm glad you asked the question even though it did not work for you, it prompted me to think about the problem and create a solution for myself. – Stephen Lee Parker Jul 10 '18 at 12:15
  • Hi - no, thanks, your answer is correct and works well. I was just wondering if it is the only option. Even Office and Visual Studio appear to custom-paint their title bar – Jason Jul 10 '18 at 16:25