locked
How to make custom derived WPF controls to use the same style (and WPF theme) as their base, not custom control ? RRS feed

  • Question

  • I am using WPF themes in my app to improve how it looks. I do it just like suggested in WPF Toolkit:

     

    <Application.Resources>
    
       <ResourceDictionary Source="pack://application:,,,/My.Theme.Assembly;component/BureauBlue.xaml"/>
    
    </Application.Resources>
    

    When I do this, I can see that for all my application, almost all controls are changed to the BureauBlue style as expected.

    I say almost all because, of course, there are exceptions. All controls that I have derived from the basic WPF controls (such as System.Windows.Window and System.Windows.Controls.ContextMenu) do not apply the WPF theme.

    I have been looking around and could not find a satisfying solution to make my custom controls' style to behave the same way as their base class. I have seen a couple of forum and blog posts telling to put a static constructor in my derived controls and put the following (here for ContextMenu):

    1)

    static ContextMenu()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(ContextMenu), new FrameworkPropertyMetadata(typeof(ContextMenu)));
    }
    

    and sometimes I saw this variant as well:

    2) 

    static ContextMenu()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(ContextMenu), new FrameworkPropertyMetadata(typeof(System.Windows.Controls.ContextMenu)));
    }
    

    Propostion 1) causes weird unwanted styling to show up (a big black window appears instead of the correctly themed window, for the context menu it just stops showing up completely). When I put proposition 2), nothing happens. The derived controls still look the same as if no theme was applied at all.

    I also have been suggested to try the following:

    <Style TargetType="{x:Type local:CustomContextMenu}" BasedOn="{StaticResource {x:Type ContextMenu}}"/>

    But I don't really know where I should put this in my ContextMenu.xaml. I tried doing this:

    <ContextMenu x:Class="MyAssembly.WPF.UserControls.ContextMenu"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:WpfUserControls="clr-namespace:MyAssembly.WPF.UserControls">
      <ContextMenu.Style>
        <Style TargetType="{x:Type WpfUserControls:ContextMenu}" BasedOn="{StaticResource {x:Type ContextMenu}}"/>
      </ContextMenu.Style>

    but it does not work either.

    What should I do to make my custom derived controls to always look the same as their base control, i.e. the ones coming from WPF framework, when applying a WPF theme?



    -Sylvain
    Thursday, August 19, 2010 5:57 PM

Answers

  • Hi Sylvain,

    You can lookup the typed style in resource instead of using the default style.

    Here is an example:

    this.Style = new Style(GetType(),this.FindResource(typeof(System.Windows.Window)) as Style);
    

    Hope it helps.

    Best regards,

    Min


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Wednesday, August 25, 2010 2:38 AM

All replies

  • Anyone? I can hardly believe I am the first one to face this problem.

     

    Really easy to reproduce:

     

    1) Create new WPf application project.

    2) Create a new class. Make it derive from System.Windows.Window

    3) Use the derived window as the main window of your application.

    4) Add a few controls into it (so you will see the WPF theme is applied to them).

    5) Add a WPF theme to your application.

    6) Run it and see that the theme is applied to the controls, but not the main window. This is because it is a derived control.

     

     


    -Sylvain
    Saturday, August 21, 2010 4:27 AM
  • Hi Sylvain,

    You can create that typed style for your custom control at the same place where you apply the theme.

    Here is an example:

      <Application.Resources>
        <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/My.Theme.Assembly;component/BureauBlue.xaml"/>
          </ResourceDictionary.MergedDictionaries>
          <Style TargetType="{x:Type WpfUserControls:ContextMenu}" BasedOn="{StaticResource {x:Type ContextMenu}}"/>
        </ResourceDictionary>
      </Application.Resources>
    

    If it doesn't seem to work, please ensure that corresponding styled properties are correctly bound in Control Template for your custom control.

    If you still cannot get it to work, please post a simplest sample code that can reproduce your problem including custom control and theme. So we can see whether you are missing something.

    If you still have any doubts or concerns about this issue, please feel free to let me know.

    Best regards,

    Min


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Monday, August 23, 2010 6:26 AM
  • Hi Min,

    I uploaded a sample that reproduces the problem I have.

    http://www.fileswap.com/share/3eaae8f97c5d56c14d228da1cc7181d4/WpfThemeNotWorking.zip.html

    In this sample, I made a custom button and a custom context menu and used them in a simple window. In the window, the first button is a normal button with a normal context menu. The second button is a custom button with a custom context menu.

    Adding the piece of code in Application Resources as you suggested works for the custom button. It becomes dark gray as specified by the theme.

    However, there are 2 things still not working:

    1) The background of the window remains white. It seems the theme is applied only on the controls the window contains. Not the window itself. Why?

    2) Both the normal context menu and the custom context menu do not apply the theme. Their menu items and sub menus do have the theme applied on them. But not the context menus. Why?

    Also, adding a <Style TargetType={CustomControl} /> in the application resources fixes the problem. But I need to remember to add this piece of code in the application resources each time I want to use those custom controls in a new application. Isn't there a way to make my custom control to always use the same style as their base control class automatically, perhaps by configuring this from within the custom control class code itself? This would avoid me from remembering to do that.

    Thanks a lot for your help.

     


    -Sylvain
    Monday, August 23, 2010 3:02 PM
  • Hi Sylvain,

    The context menu and the window is not skinned because the Theme doesn't define anything for contextmenu or window.

    I add following styles into ExpressionDark.xaml and ContextMenu and Window are correctly changed.

      <Style TargetType="{x:Type ContextMenu}">
        <Setter Property="Background" Value="Green"/>
      </Style>
    
      <Style TargetType="{x:Type Window}">
        <Setter Property="Background" Value="Orange"/>
      </Style>
    

    You also need to define a style for MainWindow to apply the above style since MainWindow is a derived type from Window.

    <Style TargetType="{x:Type WpfThemeNotWorking:MainWindow}" BasedOn="{StaticResource {x:Type Window}}"/>
    

    I don't think you can make a custom control to always use it's base type's style. However, you could put all these extra style for Custom Controls into a seperate Resource Dictionary, and merge it into Appliaction.Resource when you apply a theme. Or you can just merge it into the Theme file. Please be careful that these extra styles that based on other styles must be placed after the base styles since static resource should be defined before it is used.

    If you still have any doubts or concerns about this issue, please feel free to let me know.

    Best regards,

    Min


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Tuesday, August 24, 2010 6:06 AM
  • Thanks Min for helping me out. Very nice of you.

    Yes, I noticed that the theme doesn't actually do anything for windows or context menus.

    I also found out that I can avoid having to specify the style override in App.xaml if I override the style directly in the XAML of my ContextMenu:

     

    <ContextMenu
     x:Class="MyAssembly.WPF.UserControls.ContextMenu"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:UserControls="clr-namespace:MyAssembly.WPF.UserControls">
     
     <ContextMenu.Style>
      <Style TargetType="{x:Type UserControls:ContextMenu}" BasedOn="{StaticResource {x:Type ContextMenu}}"/>
     </ContextMenu.Style>
    

     

    However, I cannot do this for the custom window, because a control defined using XAML cannot then be used as a Root element of another XAML (generates a compile error when doing this).

    So I guess if I could find a way to express this style override programmatically, it would make my window use the style of its base class automatically. I am thinking of something like:

     

    CustomWindow() // Constructor
    {
      this.Style = new Style(GetType(), StyleProperty.GetMetadata(typeof(System.Windows.Window)).DefaultValue as Style);
    }
    
    private void Window_Loaded(object iSender, RoutedEventArgs iEventArgs)
    {
        this.Style = new Style(GetType(), StyleProperty.GetMetadata(typeof(System.Windows.Window)).DefaultValue as Style);
    }

    In the constructor, doing so has no impact.

    In the Window Loaded event, doing this seems to apply the generic style of the window of the style customized by the theme.

     

    I will keep searching, but if you think of anything, thanks for letting me know.

    Thanks,

     


    -Sylvain
    Tuesday, August 24, 2010 4:32 PM
  • Hi Sylvain,

    You can lookup the typed style in resource instead of using the default style.

    Here is an example:

    this.Style = new Style(GetType(),this.FindResource(typeof(System.Windows.Window)) as Style);
    

    Hope it helps.

    Best regards,

    Min


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Wednesday, August 25, 2010 2:38 AM
  • Yep, that works! :)

    I just copy pasted your example:

    this.Style = new Style(GetType(),this.FindResource(typeof(System.Windows.Window)) as Style);

    in my Window constructor and now all derived windows apply the WPF theme as expected. Awesome.

    Cheers!


    -Sylvain
    Wednesday, August 25, 2010 3:46 AM
  • I had the same issue and after some searching came across this (which works)... 

    In your instance constructor of your custom control, add the following to set it's style to the default for the Base control:

    public MyContextMenu() {
    SetResourceReference(StyleProperty, typeof(ContextMenu));
    }

    www.datadaysolutions.com

    Wednesday, February 19, 2014 2:23 PM