WPF Toolbar removing custom styles for controls placed within it - c#

I have defined generic themes (key less, styles based on types), for buttons and text boxes.
When I create a button instance in my Window where these resources are merged, the styles are rendered properly. But for the same button hosted within a ToolBar, the styles are not applied; the style reverts back to Windows default.
Any idea why?
P.S: If I set the style of the buttons hosted within the ToolBar explicitly, then it works fine, but this is something I do not want to do.
This works:
<ToolBarTray>
<ToolBar Band="1"
BandIndex="1">
<Button Content="Add" Style="{DynamicResource ResourceKey={x:Type Button}}" />
</ToolBar>
</ToolBarTray>
This does not work:
<ToolBarTray>
<ToolBar Band="1"
BandIndex="1">
<Button Content="Add" />
</ToolBar>
</ToolBarTray>
In both the cases, if the button is placed outside the ToolBar, styles are applied properly!

So if we go look at a style template for your toolbar we find this in there.
<Style x:Key="{x:Static ToolBar.ButtonStyleKey}"
BasedOn="{StaticResource ToolBarButtonBaseStyle}"
TargetType="{x:Type Button}" />
Which is explicitly defining and overriding your embedded Button's with another style nested in the template.
You can either alter or remove this from the template, or override it at the instance level where you would just change the BasedOn value to point to your own Style template. There's also tutorials out on the net for styling the toolbar for more detailed information. Hope this helps.

Related

Custom combo box control in WPF behaves differently on Windows 10 and 7

I am currently working on a WPF application which is to be deployed on both Windows 7 and 10. In the app there is a custom Combo Box control:
<utils:FilteredComboBox
Height="28"
Background="#222222"
FontSize="14"
Foreground="White"
IsEditable="True"
IsEnabled="{Binding ElementsEnabled}"
IsTextSearchEnabled="False"
ItemsSource="{Binding Path=FlatItemSource, Mode=OneWay}"
SelectedItem="{Binding SelectedFlatItem}"
StaysOpenOnEdit="True">
<utils:FilteredComboBox.Style>
<Style>
<Setter Property="FrameworkElement.OverridesDefaultStyle" Value="False" />
</Style>
</utils:FilteredComboBox.Style>
<utils:FilteredComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</utils:FilteredComboBox.ItemsPanel>
<utils:FilteredComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Style="{StaticResource CardValue}" Text="{Binding Name}" />
<TextBlock
FontSize="10"
Foreground="White"
Text="{Binding Comments}" />
</StackPanel>
</DataTemplate>
</utils:FilteredComboBox.ItemTemplate>
</utils:FilteredComboBox>
On Windows 7 it appears normally having the dark gray background both in the TextBox part and in the Dropdown list, as can be seen on screen below.
Custom Combo Box
But on Windows 10 the TextBox part becomes White while the dropdown list remains dark gray. Making any changes to the background property doesn't affect the control, but for example changing the Foreground makes text a different color.
There are other combo boxes on the same screen which keep the proper coloring (they are normal combo boxes, not custom one like this one).
How can I fix this? I've tried creating a custom template for the control, but upon trying to Edit a copy of the template, VS (2015) returns an error that copying the template has failed.
Code for the Card Value style that's used in dropdown:
<Style x:Key="CardValue"
TargetType="TextBlock">
<Setter
Property="FontSize"
Value="14" />
<Setter
Property="FontFamily"
Value="Segoe UI" />
<Setter
Property="Foreground"
Value="White" />
</Style>
Seems like problem is in the control template. It uses different default colors on Windows 7 and Windows 10. So you should do next:
Open Visual Studio editor
Add your combobox somewhere
Right click on visual element in Editor: Edit Template=> Edit a copy
VS generates new template
Create your Resources with colors
Change them in your template
I ended up figuring out how to do this myself in just a few hours.
Create a separate resource dictionary to put the edited template in. There is no option to create one of these in Visual Studio, but I am pretty sure this is just a renamed XML file with some syntactic sugar. I made a copy of one I already had. It should look like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YourNamespace">
<!-- nothing here yet -->
</ResourceDictionary>
Let's assume that this file is named CompatibleComboBox.xaml.
Add the resource dictionary to your xaml file.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\path\to\CompatibleComboBox.xaml" />
</ResourceDictionary.MergedDictionaries>
This will make it avilable for the next step.
Do everything in #WinterMute's answer, placing it in the resource dictionary you just created. This will add a Style reference from your ComboBox to the copied template.
In the resource dictionary, find the Border that has a TextBox inside it. Mine is at line 240.
Change the Background property to Background="{TemplateBinding Background}". This is the most important part.
But now you have a problem because this won't run on Windows 7. To fix this we need to not use Aero2, which was included when we created a copy of the ComboBox template.
Go remove the project reference to PresentationFramework.Aero2
Add PresentationFramework.Aero instead.
Change the reference at the start of CompatibleComboBox.xaml from Aero2 to Aero.
You now have a ComboBox with a functioning Background property that works in Windows 7, 8, and 10.

Edit wpf control template but use original styles

Sometimes when I'm editing a copy of the controls original template I don't need to change the original styles and colors, and would like to reference the original ones directly.
For example, I wanted to change the ComboBox template to add some filtering buttons in the drop down, its toggle button refers to a Style that is also copied into the file. I would like to refer to the original style so my XAML isn't overly cluttered.
Edit:
So here is part of the XAML code that is created when you choose to edit a copy.
The ControlTemplate is what I want to change, but I don't need the ComboBoxToggleButton Style, and so for the toggleButton I'd like to set its style to the one the ComboBoxToggleButton Style was copied from. Is there some namespace that they are all stored in, or are they inaccessible?
<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
...
</Style>
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
...
<ToggleButton x:Name="toggleButton" ... Style="{StaticResource ResourceKey=ComboBoxToggleButton}"/>
</Grid>
</ControlTemplate>
And approximately what I'd like it to be like
<Window xmlns:baseStyles="{namespace/url to the default wpf styles}">
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
...
<ToggleButton x:Name="toggleButton" ... Style="{StaticResource ResourceKey=baseStyles:ComboBoxToggleButton}"/>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
Right, so Combobox isn't your basic bare templated control. Within it's ControlTemplate is a unique ToggleButton (hence the additional instance-specific Style template for it) which it requires. Once you introduce a new ControlTemplate than that's now all it knows. It CAN NOT reference a Style template inside of the original ControlTemplate since it's not a resource available outside of it. Style and ControlTemplate are different beasts.
You have two options. Either you take that unique ToggleButton Style Template and put it somewhere it can be reached as a StaticResource and ref it on the ToggleButton instance inside your ControlTemplate via the normal <ToggleButton Style="{StaticResource ComboBoxUniqueToggleButtonStyleKeyNameYouGiveIt}" ..../>
(Like if it were in a resource dictionary, except then it's loaded all the time which generally isn't necessary).
Or, you can embed it directly in your ControlTemplate just like they do in the default style/controltemplate for ComboBox.
You can inherit parts of a Style template via BasedOn but you can only have one ControlTemplate at a time.
Hope this helps, and I'll retract my duplicate vote.
To reuse the default WPF style to ComboBox, use:
<Style TargetType="ComboBox">
<!-- Setters in need of change -->
</ Style>
If you want to inherit from a Style you created yourself, you can use:
<Style TargetType="ComboBox" BasedOn="{StaticResource YourExistentStyle}">
<!-- Setters that need to change -->
</ Style>

How to set background color of button in WPF?

How to set the background color of a button in xaml?
It can't be much harder than
<Button Margin="2" Background ="LightGreen" ....>
But this does not work...for the avoidance of confusion, button here is System.Windows.Controls.Button
Edit
I forgot to mention that I use DevExpress's ThemeManager but did not think that would cause issues as per DevExpress they do not style the standard Windows Buttons...apparently they do, however, which basically makes it impossible to change the background color of a button without some major work...
According to documentation:
DevExpress provides multiple themes that can be applied to all
DevExpress controls for WPF and some standard controls (GroupBox,
ScrollViewer, Scroll, RadoiButton, Button, ListBox, Slider,
TabControl, Separator, ToggleButton, RepeatButton, Label, ListBoxItem,
TabItem, ToolTip, etc).
As you can see the Button control is listed here. But then in documentation it is said that you can disable theme of individual control by setting ThemeName attribute to None. So, you can just disable the theme for button or for some of its parent containers and use your own style.
Here is example:
<Button Margin="2" Background="LightGreen" dx:ThemeManager.ThemeName="None" ...>
Try this:
yourBtn.Background = new SolidColorBrush(Colors.Green);
Or in XAML:
<Window.Resources>
<SolidColorBrush x:Key="BG" Color="Green"/>
</Window.Resources>
<Button Background="{DynamicResource BG}"></Button>
<Button Margin="2" Background="LightGreen"/> works. If it doesn't work for you, perhaps you have another style or style manager overriding it somewhere.
Additional information - Another answer shows how to style an individual button using dynamic resources. To set the background colour for all buttons in a style using a resourcedictionary, you can use something like:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--background brush-->
<LinearGradientBrush x:Key="backgroundBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.3"/>
<GradientStop Color="#DDDDFF" Offset="1"/>
</LinearGradientBrush>
<!--The style for buttons-->
<Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource backgroundBrush}"/>
</Style>
</ResourceDictionary>

How can I apply a custom TextBox style to built-in controls?

Background:
In a standard WPF application, right clicking on a TextBox displays a ContextMenu with three commands: Cut, Copy, and Paste. My desire is to add a Select All command to all TextBox ContextMenus in my application.
Problem:
My standard approach is to create a Style that targets TextBox and supplies the new ContextMenu. That way, all TextBox controls inherit the style and pick up the change.
The trouble is that my style isn't inherited by controls that contain TextBoxes. For instance, when editing a DataGridTextColumn cell, I know a TextBox is used but it doesn't inherit my style. The same goes for some 3rd party controls used by my application.
Question:
Is there some other way to have controls, like the DataGridTextColumn's cell, pick up my style changes, or am I stuck altering their templates?
Addendum:
This is the style:
<Style
TargetType="{x:Type TextBox}"
BasedOn="{StaticResource {x:Type TextBox}}"
>
<Setter
Property="ContextMenu"
>
<Setter.Value>
<ContextMenu>
<MenuItem
Header="Cu_t"
Command="ApplicationCommands.Cut"
InputGestureText="Ctrl+X"
/>
<MenuItem
Header="_Copy"
Command="ApplicationCommands.Copy"
InputGestureText="Ctrl+C"
/>
<MenuItem
Header="_Paste"
Command="ApplicationCommands.Paste"
InputGestureText="Ctrl+V"
/>
<Separator
/>
<MenuItem
Header="Select _All"
Command="ApplicationCommands.SelectAll"
InputGestureText="Ctrl+A"
/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
your textbox style will be reflected to textboxes who dont have any style applied. So if you have any third party textbox and it has already a style applied with key. your style will fail. you may need to use expression blend to open the the control template and change style or understand how the textbox style applied there.
cheers..

WPF: Row of ToggleButtons with equal length?

I'm currently displaying RadioButtons in my application as rows of ToggleButtons (see my last question). However, I'd like the buttons to be of the same width - currently, every button is just as wide as it has to be.
Since I'm working with templates, I'd like to avoid specifying the width every time I use the control if possible - instead, the width of every button in the row should be equal to that of the widest button in that group.
Any ideas how to do this in XAML? :-)
Maybe using a UniformGrid and setting int the style the property HorizontalAlignement="Stretch" will help.
If you have access to all the toggle buttons (e.g. they aren't databound) then there is a neat trick you can do by binding the minwidth of each button to the width of the one next to it. With the final button being bound to the first:
<StackPanel Orientation="Horizontal">
<Button x:Name="Button1" Content="Long text" MinWidth="{Binding ElementName=Button2, Path=ActualWidth}"/>
<Button x:Name="Button2" Content="A" MinWidth="{Binding ElementName=Button3, Path=ActualWidth}"/>
<Button x:Name="Button3" Content="Extremely long text that should cause this button to be really wide" MinWidth="{Binding ElementName=Button1, Path=ActualWidth}"/>
</StackPanel>
in your resources section create a style that targets ToggleButtons and sets the width to whatever value you want.
<Window.Resources>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Width" Value="50" />
</Style>
</Window.Resources>
Use a grid with a 1 button in each column and definethe widths like this

Categories

Resources