Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to .Net8 #32

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Conversation

DerekGooding
Copy link

Updated and refactored everything that was out of date or depreciated.

I've tested and it works perfectly with .Net8. Just copy the Themes folder into the project and use the instructions from the original readme.

Just a quick update with the VS upgrade assistant
Error: Cannot create a default one-way converter.

So I created one.
Depreciated code replaced
As per shivan's pull request on the main repo.

Names were incorrect in Themes\ControlColors
Removed a redundant BasedOn
Code Maids + general refactoring cleanup
Since the desired import method is to copy the entire Themes folder into a new project, converters needs to be in there.
Reversing a cleanup change that broke something in MainWindow
@HiMarioLopez
Copy link

Thank you for this. I wish it were published to the main repo, but for now I will refer to you the new maintainer and be using your changes on your fork. :)

@sjain882
Copy link

sjain882 commented Dec 2, 2024

Thanks for your work!

How did you add PresentationFramework.Aero2.dll as a Project Reference for the drop shadows? I grabbed the .dll from:

C:\Program Files\Microsoft Visual Studio\2022\Community\dotnet\net8.0\runtime\shared\Microsoft.WindowsDesktop.App\8.0.0\PresentationFramework.Aero2.dll

and tried to add it via right click project > add > project reference... > browse, but it says "The reference is invalid or unsupported". Any ideas?

The assemblies tab is missing in the Reference Manager so I can't do it that way, which is apparantly normal for .NET (previously .NET Core) projects.

Thanks!


The theme also appears to not work properly. Whether I do:

  1. This in App.xaml:
<Application
    x:Class="OcrMyPdf.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:OcrMyPdf"
    StartupUri="GUI/View/MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!--  Contains all of the colours and brushes for a theme  -->
                <ResourceDictionary Source="GUI/Theme/WPFDarkTheme/ColourDictionaries/DarkGreyTheme.xaml" />
                <!--  Contains most of the control-specific brushes which reference  -->
                <!--  the above theme. I aim for this to contain ALL brushes, not most  -->
                <ResourceDictionary Source="GUI/Theme/WPFDarkTheme/ControlColours.xaml" />
                <!--  Contains all of the control styles (Button, ListBox, etc)  -->
                <ResourceDictionary Source="GUI/Theme/WPFDarkTheme/Controls.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
  1. This in MainWindow.xaml:
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!--  Contains all of the colours and brushes for a theme  -->
                <ResourceDictionary Source="../Theme/WPFDarkTheme/ColourDictionaries/DarkGreyTheme.xaml" />
                <!--  Contains most of the control-specific brushes which reference  -->
                <!--  the above theme. I aim for this to contain ALL brushes, not most  -->
                <ResourceDictionary Source="../Theme/WPFDarkTheme/ControlColours.xaml" />
                <!--  Contains all of the control styles (Button, ListBox, etc)  -->
                <ResourceDictionary Source="../Theme/WPFDarkTheme/Controls.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

The markup is valid and there are no errors (especially no errors about missing resources, paths, etc). But even though I have no colours defined anywhere in my Window XAML, the window gets ruined:

Images Goes from this:

image

to this:

image

EDIT: Seems that solely the Window background isn't being applied correctly. I manually set it based on the theme colours in /ColourDictionaries and it works fine now.

@DerekGooding
Copy link
Author

@sjain882

Don't download dlls. Add a nuget package instead. I haven't tried them, but just looking at the packages, I'd use DynamicAreo2 over PresentationFramework.Areo2.

Don't add the resources to your MainWindow. Set the window style

Style="{StaticResource CustomWindowStyle}"

@sjain882
Copy link

sjain882 commented Dec 3, 2024

Apologies, that was my mistake for missing that!

I am, however, facing another difficult problem. Although I've looked much deeper this time, I'm really not sure whether it`s a quirk of this theme, or a gap in my WPF knowledge.

TL;DR: My aim is to override a single label's color, while using this dark theme. Several approaches haven't worked. I'm wondering what the best approach is for this, specific to this theme.

Normally (without this theme) my aim works fine with DynamicResource:

MainWindow.xaml:

<Label
    x:Name="ProcessingPolicyLbl"
    Content="Processing Policy:"
    Foreground="{DynamicResource ProcessingLblColor}" />

LightThemeAdditions.xaml

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="ProcessingLblColor" Color="#FF0000" />
</ResourceDictionary>
🖼 Image (click to expand)

image

However, this approach simply doesn't work with any of the themes. The text color remains the same.

🖼 Image (click to expand)

image

I tried the following as alternatives, in DarkThemeAdditions.xaml:

    <Style x:Name="ProcessingLblColor" TargetType="{x:Type Label}">
        <Setter Property="Foreground" Value="#FF0000" />
    </Style>
    <Style
        x:Key="ProcessingLblColor"
        BasedOn="{DynamicResource ProcessingLblColor}"
        TargetType="{x:Type Label}">
        <Setter Property="Background" Value="#FF0000" />
    </Style>

and even in MainWindow.xaml.cs:

this.ProcessingPolicyLbl.SetResourceReference(Control.ForegroundProperty, new SolidColorBrush(Colors.Red));

as well as a few other attempts I have since lost. None of them worked.

I suspect the issue lies in WPFDarkTheme\Controls.xaml:1271, where the label is set its own DynamicResource:

    <Style TargetType="{x:Type Label}">
        <Setter Property="Foreground" Value="{DynamicResource ABrush.Foreground.Static}" />

Which might mean I'm unable to use this trick. The Live Visual Tree confirms that although my custom DarkThemeAdditions.xaml is loaded, it is being overriden, as it is crossed out:

🖼 Image (click to expand)

2024-12-03_20-27-47 - Copy

I looked into manipulating the hierarchy of each ResourceDictionary (like CSS), but this doesn't seem to be possible. Leading me to believe there is simply no way of achieving this with this theme? Surely I'm doing something wrong.

I actually setup my own function for switching between WPF's default light theme (aero2) and this repo's dark theme, as I disliked the light theme included with this repo.

The relevant code is below for reference. It works perfectly, and I can confirm all the XAMLs are loaded fine, its just a hierachy issue.

Don't worry - you don't need to read it all! My sole point is - I have attempted to add my own DarkThemeAdditions.xaml to the MergedDictionaries before adding any other ResourceDictionaries (such as the ones from this repo).

This, however, doesn't seem to fix the issue.

👩‍💻 Code (click to expand)

Fully automatic runtime theme switching in MainWindow.xaml.cs:

    public partial class MainWindow : Window
    {
        bool currentAppThemeIsDark;

        public MainWindow()
        {
            this.ChangeTheme();
            InitializeComponent();
            // Subscribe to OS theme change event
            SystemEvents.UserPreferenceChanged += (s, e) => { this.SystemEvents_UserPreferenceChanged(s, e); };
        }

        public void ChangeTheme()
        {
            // Dark theme
            if (OSThemeDetector.DetectOSTheme())
            {
                // Contains all of the colours and brushes for a theme
                ResourceDictionary DarkThemeColourDict = new ResourceDictionary()
                { Source = new Uri("GUI/Theme/WPFDarkTheme/ColourDictionaries/DarkGreyTheme-Modified.xaml", UriKind.Relative) };

                // Contains most of the control-specific brushes which reference
                // the above theme. I aim for this to contain ALL brushes, not most
                ResourceDictionary DarkThemeControlColours = new ResourceDictionary()
                { Source = new Uri("GUI/Theme/WPFDarkTheme/ControlColours.xaml", UriKind.Relative) };

                // Contains all of the control styles(Button, ListBox, etc)
                ResourceDictionary DarkThemeControlStyles = new ResourceDictionary()
                { Source = new Uri("GUI/Theme/WPFDarkTheme/Controls.xaml", UriKind.Relative) };

                // Custom additions
                ResourceDictionary DarkCustomAdditions = new ResourceDictionary()
                { Source = new Uri("GUI/Theme/Custom/DarkThemeAdditions.xaml", UriKind.Relative) };

                App.Current.Resources.Clear();
                this.Resources.Clear();

                App.Current.Resources.MergedDictionaries.Add(DarkCustomAdditions);
                App.Current.Resources.MergedDictionaries.Add(DarkThemeColourDict);
                App.Current.Resources.MergedDictionaries.Add(DarkThemeControlColours);
                App.Current.Resources.MergedDictionaries.Add(DarkThemeControlStyles);
                App.Current.Resources.MergedDictionaries.Add(DarkCustomAdditions);

                // Unfortunately, we can't move Theme functionality to a separate file because of this single line:
                this.Style = (Style)FindResource("CustomWindowStyle");

                RefreshControls();

                currentAppThemeIsDark = true;

            }
            // Light theme
            else
            {
                ResourceDictionary DefaultStyle = new ResourceDictionary()
                { Source = new Uri("GUI/Theme/WPFDefault/aero2.normalcolor.xaml", UriKind.Relative) };

                // Custom additions
                ResourceDictionary LightCustomAdditions = new ResourceDictionary()
                { Source = new Uri("GUI/Theme/Custom/LightThemeAdditions.xaml", UriKind.Relative) };

                this.Style = null;
                App.Current.Resources.Clear();
                this.Resources.Clear();
                App.Current.Resources.MergedDictionaries.Add(DefaultStyle);
                App.Current.Resources.MergedDictionaries.Add(LightCustomAdditions);
                RefreshControls();

                currentAppThemeIsDark = false;
            }
        }

        private void RefreshControls()
        {
            if (currentAppThemeIsDark)
            {
                // This seems to be faster than reloading the whole file, and it also seems to work
                Collection<ResourceDictionary> merged = Application.Current.Resources.MergedDictionaries;
                ResourceDictionary dictionary = merged[2];
                merged.RemoveAt(2);
                merged.Insert(2, dictionary);
            }
        }

        public void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
        {
            ChangeTheme();
        }

My question is: what would be the best way of achieving my original aim, given the above circumstances? Is there even a clean way of doing this without modifying the theme?

My question has little relevance to this pull request, so I apologise in advance. I don't see where else this could be easily discussed, however, given the depth of these themes & inactivity of the original maintainer. Given your generous work on these themes, I figured you may be familiar with how it works. I hope you understand my situation! Thanks in advance for any assistance.

@DerekGooding
Copy link
Author

@sjain882

I'd follow the instructions from the forked repository. If I remember correctly, you don't set the individual components. There are a bunch of override styles that come with the theme's folder that just automatically cover everything.

Honestly, though, I wouldn't bother. I used this earlier this year but recently with .net9, microsoft included dark/light theming natively with C#. I also found WPF-UI nuget package to just be way easier to work with as another option for theming.

Both of those options just completely eclipse this project. Give them a try and see if you're still interested in getting this repo to work. I'll help if I can but it's been a minute since I worked on it.

@AngryCarrot789
Copy link
Owner

AngryCarrot789 commented Dec 4, 2024

I believe the issue is similar to why styling the data grid fully was impossible, because WPF creates an instance of the controls internally but doesn't apply the default styles for some reason. I remember it was truly impossible for data grids since it used an internal derivative of the standard controls or maybe it was because the parent of those controls weren't styleable, but looking through the ContentPresenter it looks possible hopefully

@AngryCarrot789
Copy link
Owner

AngryCarrot789 commented Dec 4, 2024

Ah I found how I fixed it for another control. You have to add a TextBlock style to the ContentPresenter's resources and bind to the Label's properties:

<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                  RecognizesAccessKey="True"
                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
    <ContentPresenter.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="TextWrapping" Value="Wrap"/>
            <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
            <Setter Property="TextAlignment" Value="Left"/>
            <Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Label}}}"/>
        </Style>
    </ContentPresenter.Resources>
</ContentPresenter>

This is just my guess but I assume what's happening is the TextBox (created by the ContentPresenter when Content is set to a string) style IS being applied, but maybe the ContentPresenter or something else creates a hole in the property inheritance and well since Foreground (and a few others) are inherited, the label's values aren't applied to the text block.

@sjain882
Copy link

sjain882 commented Jan 2, 2025

Apologies, I couldn't quite figure out how to implement your suggestion. Most likely due to my limited WPF knowledge.

Regardless, if anyone was looking for an example of how to seamlessly switch between WPF default aero2 light theme and this repo's dark theme, you can use OSThemeDetector.cs and ThemeChanger.cs from here.

Examples of how to use it are here and here.

You can grab the default aero2 theme file from C:/Program Files/Microsoft Visual Studio/2022/Community/DesignTools/SystemThemes/Wpf with Visual Studio 2022 installed.

Here's an example project implementing the above, with previews in the readme. I did adjust the background colour of DarkGrey theme to be lighter, imo this is much nicer on the eyes.

@sjain882 sjain882 mentioned this pull request Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants