This project has moved. For the latest updates, please go here.

How to make The Window Style changeable?

Nov 13, 2013 at 12:18 PM
Edited Nov 13, 2013 at 12:54 PM
Hi,

In this Project, when the user change theme, I want to make the window theme change also.

So I created a Custom Control named "StyleWindow" based on "System.Windows.Window", and put both .cs and .xaml(ResourceDictonary) in the "SimpleControls" Project. The style in this .xaml works fine.

Then I want to make the window theme change, When the user change theme from the menu. So I make a new .xaml in the "Themes" Project, and include it in "Theme.xaml", in every theme folder. But the Window theme dosen't change, when user change theme from the menu.

Did I forget something? I have no idea about what's wrong. Can you help me?


To show my problem, I fork Edi Project and submit my code. You can see my modification.
https://edi.codeplex.com/SourceControl/network/forks/tcsnhj/Edi/changeset/ff35eb9b241996799abf7099a6dc3e4da935767a
Coordinator
Nov 13, 2013 at 7:23 PM
Edited Nov 13, 2013 at 7:24 PM
I was Looking into your and I have found 2 interesting things:
  1. The color from SimpleControls/Generic -> Window.xaml is applied on the MainWindow
    (no other color from any other place is applied ever)
    -> You can verify this by changing the color of CaptionBackgroundBrush in SimpleControls to Green
  2. OnApplyTemplate is called only once when the application starts up but never again
I think your problem is that a change of a ControlTemplate is applied to a specific class only and not to a derived class.
In your implementation you are using the Window class as themeable control but you are instantiating and trying to re-style
a MainWindow class, which leaves you in a similar situation as reported here:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/e16ef092-a62b-4f45-bc63-6fee5f68b7d9/how-to-make-custom-derived-wpf-controls-to-use-the-same-style-and-wpf-theme-as-their-base-not

So, in short, you are probably better off if you:
  1. Assign your color items with DynamicResouces instead of StaticResource and re-define theme in each theme.
    (if changing colors is all you really want to do here)
  2. Pass down a reference to your MainWindow into the theming function and re-load the style with an explicit statement as suggested in the above reference:
    this.Style = new Style(GetType(),this.FindResource(typeof(System.Windows.Window)) as Style);
or here:

http://stackoverflow.com/questions/4069848/derived-classes-and-theme-inheritance-in-wpf

Sorry that I cannot give you a better suggestion but I really think your problem is that the
MainWindow class does not have a style key in the (non-existing) static constructor.

I have similar problems when theming the MainWindow as I am going to use the Metro style very soon.
I think reviewing this should be very interesting for your indeed:

https://edi.codeplex.com/SourceControl/changeset/b8f4a29f7449feaed003114cb2bf066fdccd8b40

Let me know if you find this useful or if you learn anything new about your project. What is confusing is the
fact that the system does apply the initial generic theme but does not implement the theme change. I think
this is due to the fact that the initial theme application is done via constructor->base constructor while the
theme change does not walk the inheritance tree ;-(

You can proof me wrong if you can derive a look-less control and change its theme without using a style key
in the derived class.
Marked as answer by dirkster on 11/14/2013 at 11:33 AM
Nov 14, 2013 at 8:32 AM
Thanks! As your Suggestion, I solve the problem. ^^

But I am not sure what I did is right. Could you check it for me? http://edi.codeplex.com/SourceControl/network/forks/tcsnhj/Edi/changeset/5722690c4da80117b1d6b9eecd8df79c76c68f35
Coordinator
Nov 14, 2013 at 6:28 PM
Edited Nov 14, 2013 at 10:58 PM
Hi it looks good to me. Its good to hear your problems solved :-)
I know how hard it is to get help from others since everyone seems to be busy doing something else :-(

I just noticed that you can use an alternative solution, which is to use global DynamicResources in your ResourceDictionary. Right now you are making your StaticResources (or DynamicResources) local to the window control template by defining the solidcolorbrushs within the ControlTemplate. Try to move the SolidColorBrushs out of your ControlTemplate definition.

See around Line 60 in SimpleControls/Window/Window.xaml in this package:
http://www.codeplex.com/Download?ProjectName=Edi&DownloadId=758173
  <SolidColorBrush x:Key="BorderBrush" Color="#131313"/>
  <SolidColorBrush x:Key="BorderBrush_A" Color="#131313"/>
  <SolidColorBrush x:Key="BorderBrush_B" Color="#414141"/>
  <SolidColorBrush x:Key="CaptionBackgroundBrush" Color="#414141"/>
  <SolidColorBrush x:Key="CaptionForegroundBrush" Color="#C7C7C7"/>

  <!-- <Style TargetType="{x:Type Controls:DefaultWindow}" x:Key="DefaultWindowStyle"> -->
  <Style TargetType="{x:Type window:Window}">
    <Style.Resources>
      <window:MaximizeVisibilityConverter x:Key="MaximizeVisibilityConverter"/>
      <window:MinimizeVisibilityConverter x:Key="MinimizeVisibilityConverter"/>
    </Style.Resources>
...
Doing this enables you to change these colors on a global level, like so in
Themes/Aero/Window.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:window="clr-namespace:SimpleControls.Window;assembly=SimpleControls"
                    xmlns:imagebutton="clr-namespace:SimpleControls.ImageButton;assembly=SimpleControls">

  <SolidColorBrush x:Key="BorderBrush" Color="#131313"/>
  <SolidColorBrush x:Key="BorderBrush_A" Color="#131313"/>
  <SolidColorBrush x:Key="BorderBrush_B" Color="#414141"/>
  <SolidColorBrush x:Key="CaptionBackgroundBrush" Color="Blue"/><!-- Redefined this for testing -->
  <SolidColorBrush x:Key="CaptionForegroundBrush" Color="#C7C7C7"/>

</ResourceDictionary>
This is how you can theme colors of your window (its far simpler if that all you want to do). If not, you could either go with the solution that you already have or apply a similar trick with your MainWindow style. Imaging you have a themes directory in the Edi assembly and it you have resourcedictinnary file for each theme, say, AeroWindow.xaml, ExpressionDarkWindow.xaml and so forth.

Now, you could place the following XAML in that file (I belief BasedOn does not allow Dynamic but it should work with Static here as well):
<Style TargetType="local:MainWindow" BasedOn="{StaticResource MyWindow}" />
Now, you could load the corresponding XAML file with a string reference as I already implemented it in:
Edi\ViewModel\Workspace_Theming.cs
          case EdiThemesViewModel.WPFTheme.Aero:
            Uris = new string[3];

            Uris[0] = "/Themes;component/Aero/Theme.xaml";
            Uris[1] = "/AvalonDock.Themes.Aero;component/Theme.xaml";
            Uris[2] = "/Edi;component/Themes/AeroWindow.xaml";
            break;
Hope this helps. You should now have about 3 possible solutions which is much better than none :)
Marked as answer by dirkster on 11/15/2013 at 10:22 AM
Nov 15, 2013 at 1:26 AM
Edited Nov 15, 2013 at 1:34 AM
Good Job! Your solutions are both much better than mine.

We are using your Edi project as framework in our company project. I take responsible to this project, so I want to make the pattern better. I consider using MVVM. But I don't want to use Prism, because I think it's too much complicated for us.(We are newbie to WPF, some of us worked on asp.net before, But I got in touch with c# 2 month ago... ). So I choose Edi. We get a lot of help from your project , your articles, and your responses here. Thanks a lot.

I want to contribute to the Edi project very much, if I get a more known about WPF, it's not far. Hope we could make friends, and communicate more, about WPF and something else. ^^