Search code examples
wpfxaml

WindowTextBrushKey not loading from application.resources until value changed in VS during runtime


I'm trying to set the SystemColors.WindowTextBrushKey to white:

<Application x:Class="MN.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MN"
             Startup="Application_Startup"
             DispatcherUnhandledException="Application_DispatcherUnhandledException">
    <Application.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.WindowTextBrushKey}" Color="#FFF" />
    </Application.Resources>
</Application>

Instead of coming out white, the controls that use this color initially come out black. It remains black until I modify this particular XAML Color property in VS. Once I change it to any other value the value is respected, until I load the app again at which point it reverts to black.

Most (if not all) other SystemColors.*Key values seem to work as expected. Whatever value I set seems to be in effect at app load time.

I'm wondering if there is something I need to do to override the original defaults. Some kind of refresh/notify call I can make?

Also considering building this Application.Resources after a delay? But looking for a better/cleaner solution.

[EDIT 2023-01-16]

So I tried a text block directly in my Window and this works as expected, however if I load a textblock via a page loaded into a frame, it does not.

My Page:

<Page x:Class="MN.TestPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:MN"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="TestPage">
    <DockPanel Margin="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <StackPanel DockPanel.Dock="Top" VerticalAlignment="Top" HorizontalAlignment="Stretch">
            <TextBlock FontSize="36" Text="I expect this text to be not black in color."/>
        </StackPanel>
    </DockPanel>
</Page>

My Window:

<Window x:Class="MN.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MN"
        mc:Ignorable="d"
        Title="TestWindow" Height="702" Width="1245"
        WindowState="Maximized"
        Loaded="Window_Loaded">
    <DockPanel>
        <Frame x:Name="frame" />
    </DockPanel>
</Window>

And in my window code-behind:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        frame.Source = new Uri("TestPage.xaml", UriKind.Relative);
    }

As an aside I'm open to better ways to doing what I want to accomplish. I basically want my app to run in a dark mode, or even better respect the user's choice for dark or light mode. It sounds like this is something that is maybe supposed to "just work" but it doesn't seem to be for me, so my plan was to override the system colors to create a default scheme for most controls and then override any controls that are problematic from there.

Most guides I've found have recommended this approach or seem to rely on some framework or other that I don't want to depend on.


Solution

  • I recommend usercontrols instead of pages.

    When I try this with a usercontrol, it works ok.

    There are a number of downsides to frames and pages. Seems this behaviour is one I had not previously noticed.

    The Frame has a bunch of side effects isolating pages, retaining state and generally complicating things I usually don't find need complicating.

    Frame and pages are useful for "wizard" like scenarios where the user is inputting to a series of views and may want to go back and forth between them. In that scenario the features frame and page add are worth the overhead.

    But usercontrols.

    If I have a usercontrol:

        <Grid>
            <TextBlock FontSize="36" Text="I expect this text to be not black in color."/>
        </Grid>
    </UserControl>
    

    And instead do:

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Content = new UserControl1();
        }
    

    That gives me red text.

    Think of usercontrols for all encapsulation of UI. Think of them before you consider another window. You only ever need two windows defined in a multi window wpf app. One is mainwindow. Another: EverythingElseWindow which just has a contentpresenter whose content is bound to datacontext. Template out it's content.

    https://social.technet.microsoft.com/wiki/contents/articles/52485.wpf-tips-and-tricks-using-contentcontrol-instead-of-frame-and-page-for-navigation.aspx

    This approach with system brushes is not how I would usually approach such things. If extensive re styling is necessary, I suggest you look at off the shelf themes.

    Over riding these system keys is unreliable. You will find that the ( sometimes surprisingly complicated ) templates for controls do not always use these system brushes.

    Error handlers:

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            DispatcherUnhandledException += App_DispatcherUnhandledException;
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
            // Get too many spurious errors when handling firstchanceexception
            // AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;  
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    

    You have to be super careful with your bindings and templates before you can use FirstchanceException in production code. It is, however, sometimes useful to have this set up to explore issues whilst developing.

    All those should log via some central injectable interface. Nlog if you are writing in house software. Consider web and application insights if it's external.

    Rather a sprawling post, even for me, hope this helps.