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
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
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
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);
}
}
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
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.
Related
I have a system where I use my WPF UI as a class library. I also use different usercontrols to control what the user see during the span of the UIs life cycle. These are all set in code. I then added a theme system where you could create a XAML ResourseDictionary file and use that to change the look on the UI. To apply the style, everytime I create the window or a user control, that FrameworkElement goes through this code:
public void ApplyStyle(FrameworkElement element)
{
var targetDir = element.Resources.MergedDictionaries.FirstOrDefault(d => d.Contains("SonaStyleDocument"));
if (targetDir != null && loadedResource != null)
{
element.Resources.MergedDictionaries.Remove(targetDir);
element.Resources.MergedDictionaries.Add(loadedResource);
}
}
In my view I do set a standard style document via the xaml code. I locate the default style in the code, remove it and add the new one. Now this works, and I can see the changes when I apply a external ResourceDictionary. However, for some reason my buttons does not react to the changes. They keep the same styles, even though it has been removed. Here is one of my buttons code:
<Button Style="{StaticResource KeyboardToggleButton}" Command="{Binding KeyboardToggleCommand}">
<Button.Content>...</Button.Content>
</Button>
It uses the style KeyboardToggleButton, which has the follow style in my default style document:
<Style TargetType="Button" x:Key="SonaButton">
...
</Style>
<Style TargetType="Button" x:Key="SonaPrimaryButton" BasedOn="{StaticResource SonaButton}">
<Setter Property="Template" Value="{StaticResource DxcPrimaryButtonTemplate}" />
...
</Style>
<Style TargetType="Button" x:Key="ManipulationButton" BasedOn="{StaticResource SonaPrimaryButton}">
...
</Style>
<Style TargetType="Button" BasedOn="{StaticResource ManipulationButton}" x:Key="KeyboardToggleButton" />
But I replace it with the following style(s):
<Style TargetType="Button" x:Key="SonaButton">
<Setter Property="Template" Value="{StaticResource RoundCornerTemplate}"></Setter>
</Style>
<Style TargetType="Button" x:Key="ManipulationButton" BasedOn="{StaticResource SonaButton}">
...
</Style>
<Style TargetType="Button" x:Key="KeyboardToggleButton" BasedOn="{StaticResource ManipulationButton}"/>
What could be going on here? They both have the same key for KeyboardToggleButton, though the templating is different and I can see that my other controls react fine to the new styles they get. I even tested just adding the new style and let the old one remain and that has the same result. The buttons just won't accept the new style.
Update:
So I was reading various articles about styles to see if I find some more information, and some article mentioned Dynamic resources. To isolate the problem I first remade my default style of one of my buttons to be very basic:
<Style TargetType="ButtonBase" x:Key="KeyboardToggleButton" />
And then changed the style I want to overwrite the default style, to just change the background:
<Style TargetType="Button" x:Key="KeyboardToggleButton">
<Setter Property="Background" Value="Yellow"/>
</Style>
With this setup nothing happened, but when I changed the Style from a StaticResource to DynamicResource, then it worked. However, this presented another problem. I tried testing this result on one of my other buttons with all substyles and control templates activated and the system throwsan exception in the code that adds the style:
System.InvalidOperationException: 'Property can not be null on Trigger.'
The style I try to use instead of the default style has a control template with triggers, to handle hover events, click event and more. My theory at this point is that I have had this problem also with the StaticResource, but the error is hidden and the system defaults directly to the old style. My issue with this theory is that I actively remove the default style before I enter in the new. Not removing the old style does not help.
I found the problem. The exception was indeed the answer, but only when I changed the code to apply each style individually. I should have followed the exception a bit more, but I had tunnel vision. The answer came from here: Why is the WPF property IsDefaulted not found?
I have first tried to change to buttonbase, but that does not work with the trigger "IsDefaulted". That will not resolve and the property will be null. Changing back to TargetType="Button" worked. No more exceptions. Then I also changed all style references to be dynamic and now all buttons (Except one, which I am looking into) have the correct style. It makes sense that it should be Dynamic as I change it during runtime.
I wrote a style for button. Unfortunately, I encountered a problem: font size of the button depends on a value, which I can compute only in the view (specifically, that's DisplayInformation stuff).
Ideally, I would do the following:
<Style x:key="MyStyle" BasedOn="{StaticResource SomeStyle}" TargetType="Button">
<Setter Property="FontSize" Value="{Binding ElementName=rootControl, Path=SomeProperty" />
</Style>
Then, I would simply provide necessary properties in view class. But that simply doesn't work (does nothing, no messages, no errors).
How can I solve this problem?
Assigning a binding to a setter value is not supported in the Windows Runtime. (It might be supported in WPF and Silverlight 5, however).
If you google around, there are workarounds, but they're kind of hacky. See this, which uses attached properties and a binding helper class.
I have a piece of AutoHotKey script that allows me to set transparency on an active window but it does not work with WPF application like Powershell ISE.
Is there a way to do that?
EDIT: As is stated in the question I need to do that on a running WPF app like Powershell ISE.
Set this in Window element
AllowsTransparency="True" WindowStyle="None" Background="Transparent"
Hi WPF is based on Direct3D, and is a bit different from win32 and forms which is based on GDI/GDI+.
In WPF you do this in your xaml(see example by maximus) or create your own window style.
There is a post here on how you should do this in WPF.
Another slightly related question.
A style :
<Style TargetType="Window" x:Key="TransparentWindowStyle">
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency=" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
Skip the x:Key and it will be applied to all windows or you have to place in app.xaml or a place where it is shared, and apply it to the window. A bit overkill for just 3 properties, but handy if you are going to do other changes, that should be applied to multiple windows.
Hope it helps,
Stian
I am looking for an example of a customized WPF button.
Ideally in a liked Blend/VS2013 configuration, i.e. a VS2013 test solution that includes a button project that can be edited in Blend for VS2013.
The button should have a visual appearance that makes it clear what state it is in, i.e.
Normal = default
MouseOver = inner glow
Pressed = smaller size / smaller shadow
ToggledOn = outer glow
Disabled = grayed out
Given such an example I could then just tweak the visual appearance of the states using Blend.
And on the application side I want to just instantiate the button, associate the style, and set properties for BackgroundColor, image/icon, text label, width, height.
I seems that using a ControlTemplate style is the recommended way of doing this, rather than sub-classing, see MSDN.
The three key issues seem to be:
how to setup the VS2013/blend project structure to use both interchangeably on a single set of source files
how to compute relative sizes in the ControlTemplate, i.e. what is the syntax for
Width = Button.Width x 1.1 to set a glow extend relative to the actual button size that is not in the template, but to be defined on the client application UI design.
how to compute relative colors from the base color of the button, i.e. what is the WPF XAML syntax for GradientStop Color = Button.BackgroundColor x 80% + White x 20%
This should be a very common need, but Google was not helpful in finding anything like the above.
Any help with any one of the three key issues would be greatly appreciated.
Your requirements do not require defining a new ControlTemplate and can be achieved with a Style with Triggers, e.g.:
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.75" ScaleY="0.75"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button Content="Click Me!" />
</Grid>
The Style can be accessible anywhere in the application if defined in your App.xaml Resources and given an x:Key
Using ScaleTransform's ScaleX and ScaleY are relative values.
You will need your own IValueConverter then bind the target color to a source color using your converter.
In our WPF application, we have a custom listbox that when unstyled, matches the default Windows theme for things like the highlighted, hovered or selected items (i.e. nice blue translucent gradient.)
However, when we try creating our own ListBoxItem template and use values like in this code...
<Trigger Property="Selector.IsSelected" Value="True">
<Setter TargetName="Bd" Property="Panel.Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</Trigger>
...the control loses all traces of the Aero theme reverting to the old Win95/Classic look. How can we say 'Hey... apply the theme's 'highlighted' style to our border.'?
Again, the resources are obviously loaded as they're there until we re-template the control, but how are we supposed to access the built-in styles of the theme? After all, that 'selection' look is all over the place... ListBox, ComboBox, ListView, everywhere. We just want it in our control too without having to reinvent the wheel.
Note: We used the ShowMeTheTemplate.exe app to get the default 'Aero' style, but again, that's not what we're seeing when we run it.
You can use BasedOn="{StaticResource {x:Type ItemType}}" in your Style to modify the existing one however there is no way to merge part of a default template with your custom template, templates are monolithic.
Your answer appears to be here:
How to change WPF Listbox/ListBoxItem DataTemplate for selected item WITHOUT affecting style & theming?
....you should use ItemsContainerStyle instead of just defining a replacement ItemTemplate...and define your Style using BasedOn to inherit the default style defined for your FrameworkElement element by the theme.
There are also some other techniques you can use to leverage the default style defined by a theme (...just gives you extra options).
http://corneliutusnea.wordpress.com/2009/12/07/merging-wpfthemes-with-your-own-styles/
And yet another technique not covered above that uses a markup extension to merge together some styles (i.e. you could merge the default style and your style).
http://www.zagstudio.com/blog/384#.UDpfPqVum6M
XAML Combine Styles
http://swdeveloper.wordpress.com/2009/01/03/wpf-xaml-multiple-style-inheritance-and-markup-extensions/