8

I am a little confused with wpf themes. I would like to have the wpf screens look the same on Vista, Windows 7 and Windows 8. So I have styled the components accordingly and they don't pose problems except when run on Windows 8. For example I have a combobox and I am changing its default background in xaml like this.

<Style TargetType="{x:Type ComboBox}" >
    <Setter Property="FontStyle" Value="Normal"/>
    <Setter Property="Height" Value="24" />
    <Setter Property="Background" Value="{StaticResource GradientButtonBackgroundBrush}"/>
</Style>

The combobox Background property has no effect in windows 8 and all I get is a flat rectangle with a arrow on right (the default windows 8 combobox, which is rather poorly designed!).

So, my question is that how do I get the combobox look the same on all version of windows. I tried adding windows Aero theme in my App.xaml like below, but it has no effect on the combobox display. Here is how I added Aero theme

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/PresentationFramework.Aero;component/themes/aero.normalcolor.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

There is also another doubt regarding themes. I am building the wpf application on a windows 7 machine, which by default (I believe) has Aero Theme set. So, all my styles are based on the Aero theme when viewed on Windows 7 machine. What happens if I run the application on say XP. Then do I need to add an entry for the resource dictionary (Aero theme) in App.xaml as listed in code above?

I know my question is a bit vague, but believe me, I am really confused with default themes of wpf on different Windows versions.

EDIT: I still can't get combobox to style according to my needs. The combobox still appears like a gray rectangle.

Here is what I did. I downloaded the Aero.NormalColor.xaml from microsoft's site and included in themes folder of application. Then I added the following in App.xaml

    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/Themes/Aero.NormalColor.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

Then I Compiled the application and deployed on Windows 8. Still the same combobox as was shown previously. Note that all other elements get styled properly according to the theme. I did the same with Luna.Metallic.xaml and every element gets styled except the ComboBox.

I believe that when I load a particular theme, which defines styles with ControlTemplate, then it should be picked by wpf. I am confused as to why only the ComboBox even after giving it a Aero (or Luna) Control Template doesn't change its appearance. Any ideas ?

EDIT-2 Screen shot of combobox appearance on Windows 8 enter image description here

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Jatin
  • 4,023
  • 10
  • 60
  • 107
  • I updated my answer for your edit. Please add comments in the relevant answers or your question. It's not very easy to track when you make comment's on answers that aren't related to what your stating – Viv Apr 25 '13 at 13:52

5 Answers5

2

Well the ComboBox clickable area is actually a ToggleButton

and if you look at the Style for that ToggleButton in Windows-8, you see something like:

<ControlTemplate TargetType="{x:Type ToggleButton}">
  <Border x:Name="templateRoot"
          Background="{StaticResource ComboBox.Static.Background}"
          BorderBrush="{StaticResource ComboBox.Static.Border}"
          BorderThickness="{TemplateBinding BorderThickness}"
          SnapsToDevicePixels="true">
...

As you can see from above, Background used is not a {TemplateBinding Background} but {StaticResource ComboBox.Static.Background}. Hence why you see no effect when you set the Background property in Windows-8 for that ComboBox

If your looking to carry a Style across the different OS Versions(Without having to backtrack and keep checking if new versions screwed up your over-rides), simple rule is Create it yourself.

Create a Style and set it to be applied by TargetType and without a Key to get applied automatically. That way in any OS it's your Style that gets used and not the default underlying one.

This thus guarantees your code run's as you expect on every single OS. Base your Style on the default's of any OS and tweak it to your heart's content.

side-note:

From a usability POV giving the user a Windows-7 ComboBox in an app that run's on Windows-8 is not very nice(unless your entire app looks like a Windows-7 app which is even worse). Your expecting the user to get used to your app's Style's and forget what he's used to from every other app he uses in his OS that use default Styles based on OS. If you have specific reasons for doing so, go ahead but do consider the implications.

Just for example you stated the Windows-8 Style is something your not a fan of, well I'm the opposite. I actually do like the Windows-8 clean and simple look. No distractions to the UserExperience with flashing gradients and things that throw you off the content your putting in front of them. This is an argument that goes on forever. Just be warned abt what the end-user expects and thinks than just what you think is good while writing your program.

Update:

Firstly comment on the relevant answer please. Your answer and your comment update has no relation.

Ok and as for your question edit, what you tried has not worked in windows-8 because PresentationFramework.Aero.dll does not exist in Windows-8 which is what holds Aero.NormalColor.xaml. In Windows-8 your options are PresentationFramework.Aero2.dll which is default and PresentationFramework.AeroLite.dll which I think is used by Windows Server 2012(Not Sure)

Try to compile your program on Windows-8 and you'll see it does not even want to compile.

You'll have to explicitly add a reference to PresentationFramework.Aero and also PresentationUI(which i think is part of .net3) to your project.

Then you'll have to edit your Aero.NormalColor.xaml to something like:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
                    xmlns:ui="clr-namespace:System.Windows.Documents;assembly=PresentationUI">
...

^^ we explicitly state the assembly for Aero Theme. I don't use Windows-7 so Am not sure if that's all that's needed. but you can give that a try.

Try to compile your code in Windows-8 to make sure it will work fine on Windows-8

Viv
  • 17,170
  • 4
  • 51
  • 71
  • I have followed steps suggested by you but the combobox still appears in gray. Please see my EDIT-2 on the original question for the screen shot. Maybe that would give an hint of what XAML theme is being picked up. Compiling on Windows 8 machine will take some time as I don't have Visual Studio 2010 installed on that machine. – Jatin Apr 25 '13 at 14:49
2

Finally, after hours of frustration, I came accross an explanation post here. Scroll for the LONG ANSWER. It explains exactly the scenario I have been facing particularly Aero style not getting applied to my Combobox. The link explains very well why we need to add a BasedOn attribute to every element that we style if we dont want the default OS style being picked up. So adding this BasedOn for the Combobox got it working for me.

<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">

Now the Aero theme style is used for the combobox. As @Viv pointed out it may also require copying of PresentationFramework.Aero.dll and PresentationUI.dll to the windows 8 machine as they are not supplied with the OS.

Thanks, Nirvan

Community
  • 1
  • 1
Jatin
  • 4,023
  • 10
  • 60
  • 107
1

I did a bit of a hack on the built-in template. Not the cleanest solution, but removes the headache of having to roll my own template. The code behind basically binds the built-in template border's properties with the combo box's properties.

<Style TargetType="ComboBox">
    <Setter Property="Border.Background" Value="White"/>
    <EventSetter Event="Loaded" Handler="ComboBox_Loaded"/>
    <Style.Triggers>
        <Trigger Property="IsReadOnly" Value="True">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
        </Trigger>
        <Trigger Property="IsFocused" Value="True">
            <Setter Property="Background" Value="{StaticResource ResourceKey=FocusedControlBackcolorBrush}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ResourceKey=FocusedControlBorderBrush}"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="BorderBrush" Value="{StaticResource ResourceKey=FocusedControlBorderBrush}"/>
        </Trigger>
    </Style.Triggers>
</Style>


    private void ComboBox_Loaded(object sender, RoutedEventArgs e)
    {
        var comboBox = sender as ComboBox;
        var toggleButton = comboBox.Template?.FindName("toggleButton", comboBox) as ToggleButton;
        var border = toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
        if (border != null)
        {
            Binding b = new Binding("Background");
            b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ComboBox), 1);
            BindingOperations.SetBinding(border, Control.BackgroundProperty, b);

            b = new Binding("BorderBrush");
            b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ComboBox), 1);
            BindingOperations.SetBinding(border, Control.BorderBrushProperty, b);
        }
    }
Matstar
  • 402
  • 3
  • 6
0

In Response to @Viv

@Viv, I feel that your answer was very clear, but somehow your views/suggestions aren't digestable to me and I will briefly mention why

Your Suggestion

If your looking to carry a Style across the different OS Versions(Without having to backtrack and keep checking if new versions screwed up your over-rides), simple rule is Create it yourself.

It seems impracticable. The very idea of themes is creation of consistent look for all elements in the application and accross all platforms. So, if I use a particular theme provided by the framework, no matter what, I should be able to achieve a satisfactory level of consistency, atleast on major current platforms. Above all, not every one has the expertise to create all the styles and templates from scratch. The concept of themes should be,

"The framework provider is providing the themes that will work well for normal scenarios and if anyone wishing to roll out his themes he is welcome to do so".

Instead here the concept seems like

"The framework provider is providing theme and it is not guranted that it will be consistent and work without breaking. So always roll out your themes"

Your quote

As you can see from above, Background used is not a {TemplateBinding Background} but {StaticResource ComboBox.Static.Background}. Hence why you see no effect when you set the Background property in Windows-8 for that ComboBox

I don't know whose idea it was to make a template that way! Must be out of his minds or I am too dumb to understand the advantages. Just imagine that I am using some Blue colored theme in my application and tommorrow, on Windows 9, somebody defines a Red as {StaticResource ComboBox.Static.Background}. So, guess what, I have a fancy window screen now with all my elements themed "Blue" and only the combobox's appear "Red". I mean the very idea of theme is broken here!

Your side note

Just for example you stated the Windows-8 Style is something your not a fan of, well I'm the opposite. I actually do like the Windows-8 clean and simple look.

My combobox looks like a disabled button and I couldn't even change its background! The buttons don't look like they are clickable! Any way as you said this is a personal choice, ofcourse.

My Conclusion I will wait for some time if some one has any suggestions on how I get the combobox working on windows 8. Your answer brings forward the truth, so I would be happy to mark it a correct if I don't get any alternative solution.

Nirvan

Jatin
  • 4,023
  • 10
  • 60
  • 107
  • Well default themes IMO are not intended to keep the look the same across OS but they keep the look the same across different controls in an OS environment. Yes ComboBox appearing Blue in Windows-7 might be Red in Windows-9 and User with Windows-9 might actually prefer that than see an app use Blue just because that's what they reckon is the right choice. Your themes are consistent when you use the ComboBox with a button and run it on Windows-8 . they are not glaringly different with default style to each other. That's what the default theme gives you. – Viv Apr 24 '13 at 13:39
  • As for the concern with creating styles from scratch. Again I never create a Style from Scratch. Either use Blend and get it to create a Style of an Element as a Copy or Google the base Style for a Control, Copy paste it in and then edit and hack away. Trying to re-invent the wheel there is pointless. You can get the full source of the default theme. Just edit and tweak it from there than start all over from ground-zero – Viv Apr 24 '13 at 13:41
  • Again Theme's do not break. Your perception of theme is different to mine I guess. As i explained in my comment above, Default theme standardizes look across control's not across OS versions. Simple example is take a cross OS UI framework like Qt or sorts. they have control's and themes. If left at default. Button on Windows will render likewise and be different to Button on Fedora. Now you wouldnt want your Windows button's looking like Unix would ya. I think that's kinda the idea I take for Themes and standardize against Controls. – Viv Apr 24 '13 at 13:44
  • There is atleast a simple reason they have done it that way. Cos if they just set the TemplateBinding for Background, what abt MouseOver, Disabled, Focus. You don't have DP Background Values for each at Control level. WPF is lookless, If you do-not like what the default look in an OS is, It's upto you to render it how you see fit and your app users to critisize your decisions – Viv Apr 24 '13 at 13:46
  • "My combobox looks like a disabled button and I couldn't even change its background!" . You can change it's Background. Apply a custom Style if you don't like what comes out of the box. It's not a hack to apply a custom style to a Control. Hence the power of WPF – Viv Apr 24 '13 at 13:48
  • In response to your last comment, its not about applying just the style to combobox, its about having to override the Control Template to change the display. I believe that the Control Template is more closely associated with the behavior rather than changing some minor properties like background. That is what breaks the theme. – Jatin Apr 24 '13 at 14:04
  • well the ControlTemplate is defined in the Style. If you want to change what it specifies you need to modify it yes. When you make a copy of the Default Style from Blend, Don't change what you don't want and leave those Trigger's intact. Update what you need. Encapsulate the Style in a section and your done. Like I said these things can stir up very long arguments. Bottom line is you want your code completed with some future-proof and stable. So look at your options and work them out accordingly. – Viv Apr 24 '13 at 14:10
  • 1
    Yes I get that. Thanks very much for the help. I would never have been able to discover the issue, had you not taken the effort to actually view the Control Template in first place. – Jatin Apr 24 '13 at 14:31
  • I was a bit hasty to mark your response as answer. I noticed that even after giving a style with control template the Combobox doesn't change its appearance. See the EDIT to my original question for details of what I did. – Jatin Apr 25 '13 at 12:59
0

The newer template has a Grid->ToggleButton->Border with a hardcoded background colour which does not respect any background styles. Based on the answer from @Matstar I made a way to sync the background colour across.

You can still set the binding to background colour and the code will pick this up and reapply this when needed:

<ComboBox ItemsSource="{Binding Values}" x:Name="Cbo2"
Background="{Binding Path=SelectedValueBgColor}" ... />

Then in xaml.cs

cbo.Loaded += (sender, args) =>
{
    var comboBox = sender as ComboBox;
    if (comboBox != null)
    {
        var toggleButton = comboBox.Template?.FindName("toggleButton", comboBox) as ToggleButton;
        var border = toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
        if (border != null)
        {
            var existing = BindingOperations.GetBinding(comboBox, BackgroundProperty);
            BindingOperations.SetBinding(border, BackgroundProperty, existing);
        }
    }
};

Make sure your background binding never returns null

If you have no meaningful value return transparent or white or some other default. If binding to a background that returns null the combobox will stop working.

Make sure you notify property changed on your background binding if it needs refreshing when an event happens.

CRice
  • 12,279
  • 7
  • 57
  • 84