Issues with XAML MaterialDesign TextBox style: background when enabled and helpertext - wpf

I'm using Material Design library in XAML. I need to re-style some components because I'm using a particular textbox in which I have a clickable icon.
<StackPanel Orientation="Horizontal" Width="328" Grid.Column="0" Background="#3B3A3A">
<TextBox Style="{StaticResource Style}" materialDesign:HintAssist.Hint="Text" Width="300"
TextWrapping="Wrap" materialDesign:HintAssist.HelperText="Text1"/>
<Button Opacity="1" Padding="2,0,0,0"
Height="53.2"
Background="Transparent"
BorderBrush="Transparent"
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
CommandTarget="{Binding ElementName=DialogSelection}">
<materialDesign:PackIcon Kind="ArrowExpand"/>
</Button>
First of all I need to remove the background when the textbox isFocused, so in the style I did something like this:
<Style x:Key="Style" TargetType="TextBox" BasedOn="{StaticResource MaterialDesignFilledTextFieldTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Width="328"
materialDesign:BottomDashedLineAdorner.Thickness="{TemplateBinding Margin}">
<TextBox x:Name="text" TextWrapping="WrapWithOverflow" FontSize="16" FontWeight="Regular" Foreground="White"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#656565"/>
<Setter Property="Background" Value="#3b3a3a"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#00B5CE"/>
<Setter Property="Background" Value="#656565"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The issue is that using this code for the style (with the control template) I don't have available the helper text. How can I combine the two things? (I mean my settings for the background when the textbox isFocused and a HelperText available?)
NB: If I don't modify the template and just setting the background it happens that the background when the textbox isFocused isn't as I want but the HelperText is visible

It does not work, because you overwrite the control template of the TextBox and omit most of it that is needed for it to work correctly. A control template defines the visual apprearance of a control, as well as the states and transitions between them. You cannot base a contol template on another as you can with styles. Creating a custom control template means:
Creating it from scratch with all the required states and parts or
Copying the base/default style and adapting it.
For standard WPF controls, you can find required parts and states in the documentation, e.g. for TextBox here. Omitting any of the required components will lead to unexpected behavior or broken visuals states. In case of Material Design, there are even more things necessary to make the controls work correctly, but they are not documented like on MSDN, so you have to create a copy of the default style and adapt it.
You can find the default styles for TextBox here on Github. In newer versions, the styles are renamed to MaterialDesignFilledTextBox because of this issue. The derived style hierarchy is as follows:
MaterialDesignTextBoxBase
MaterialDesignFloatingHintTextBox
MaterialDesignFloatingHintTextBox
MaterialDesignFilledTextFieldTextBox
The control template is defined in MaterialDesignTextBoxBase, which is the base style for all other styles. What you have to do now is to copy the MaterialDesignTextBoxBase style, give it a name and adapt the states as in your provided code. You can merge the setters of the three derived styles above into your custom style. Then you will have a custom style that incorporates all necessary states and parts that will work as you expect it.

You can't "combine" ControlTemplates. You must define a template as a whole.
This means that you should copy the default template in MaterialDesignFilledTextFieldTextBox that includes the helper text and then modify it as per your requirements, i.e. by adding a IsFocused trigger to it.
I am afraid you cannot base a template on another template or add triggers to a template that is defined elsewhere.

Related

How to set specific control element settings that override a global style in WPF?

I have defined a global style for Buttons in my application via an application-wide Resource Dictionary. The style looks like this (followed from another SO Example):
<Style TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="{DynamicResource BaseButtonBG}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- Triggers here -->>
</Style.Triggers>
It works. But I need to assign specific values directly to some of my buttons, like margin and padding to align them. I also would like to have the ability to override the color properties from the style in individual buttons.
It appears that any properties I set directly on specific buttons get completely ignored and only the properties from the global style are used. How do I overcome this?
UPDATE: To clarify what I want:
In the HTML/CSS world (which is older than dirt), you can add a style class to an element, but you can also assign properties directly to the element that override the class values. That's what I want to accomplish in WPF.
UPDATE 2
It's possible people think this question is stupid because the solution should be obvious. However, from my personal testing, there appears to be a bug with Padding not changing at all unless you specifically bind it in a control template. This behavior seems to change from property to property. Since my original attempt to override a property specifically involved Padding and it didn't work, I had to build this workaround.
Ok, form in Design:
XAML code for form:
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Content="Button" x:Name="btnNo1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="120"/>
<Button Content="Button" x:Name="btnNo2" HorizontalAlignment="Left" Margin="135,10,0,0" VerticalAlignment="Top" Width="120"/>
</Grid>
and in RunTime we going to change Margin by using this code in CS file:
public MainWindow()
{
InitializeComponent();
btnNo2.Margin = new Thickness(100, 100, 100, 100);
}
Result will be:
Can you create and use new style for button where you need custom margin/padding?
<Style x:Key="SpecialButtonType1" BasedOn="{StaticResource ResourceKey=CommonButtonStyle}">
...
</Style>
and change
<Style TargetType="Button">
to
<Style TargetType="Button" x:Key="CommonButtonStyle">
YES: It's completely doable, You can assign overriding properties directly on an element without doing the ugly process many are using of creating a special one-off dictionary entry just for the specific element in question.
I don't know if its caused by a bug in WPF, but there's an initial requirement...
Your dictionary-referenced base style might need to include any properties that you want to be overridable. For some reason different properties seem to exhibit different behavior. But at least in the case of Padding, if you don't include Padding on your ControlTemplate TemplateBinding, you won't be able to override it on your element.
Additionally, in the case of margin, there seems to be some kind of "doubling" effect that happens if you include Margin in the ControlTemplate TemplateBinding. If you don't templateblind the margin, you can still override margin but the behavior changes.
STEP 1
Define a base style with a ControlTemplate. Make sure that your ControlTemplate includes a TemplateBinding for all properties that you may want to customize/override on individual elements.
<Style TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="{StaticResource BaseButtonBG}"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
Margin="{TemplateBinding Margin}"
>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource BaseButtonBG_IsMouseOver}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource BaseButtonBG_IsPressed}"/>
</Trigger>
</Style.Triggers>
</Style>
I've defined a few StaticResource keys for my property colors so that I can put them altogether in another place for cleaner skinning. Here are those keys:
<SolidColorBrush x:Key="BaseButtonBG" Color="#5f636c"/>
<SolidColorBrush x:Key="BaseButtonBG_IsMouseOver" Color="#898C94"/>
<SolidColorBrush x:Key="BaseButtonBG_IsPressed" Color="#484B51"/>
STEP 2
Implement actual button like this:
<Button Content="New" />
And the result of this makes a button that looks like this:
STEP 3
Now let's say I want all of my buttons to look squashed like that, except one. I want to add some vertical padding to make one specific button look taller. I could alter the example button like this:
<Button Content="New" Padding="0,30"/>
And the result:
Alternatively, you could implement the button override as follows, which gives you the ability to override Triggers or other special Style options.
<Button Content="New">
<Button.Style >
<Style TargetType="Button" BasedOn="{DynamicResource {x:Type Button}}">
<Setter Property="Padding" Value="0,30"/>
</Style>
</Button.Style>
</Button>
TADA! We've assigned a one-off style tweak directly to the element WHERE IT BELONGS! We didn't have to create a style in a dictionary and reference it for this one case.
Important Points
In order to make this work "Padding" MUST BE defined in the ControlTemplate with the TemplateBinding code. If you don't do that, Padding directly applied to the button just gets ignored completely. Again, I'm not sure why its like this, but that seems to be the magic fix.
Further Reading: I was able to figure this out from some helpful info on this blog article:
explicit-implicit-and-default-styles-in-wpf
Interestingly, the last part of that article suggests creating a custom control with your own default style and properties that you can override similarly to how I've done here. This seems a bit overkill to me, but it might eliminate the weird bugginess problem with padding and margin behaving differently. Haven't tried that yet, though.

WPF themeing best practices

This is a best practices question regarding wpf themeing and more specifically skinning.
This is more of an opinion based question since I don't have a problem making this work but more of a general wondering if my conclusions cover all the scenarios, and if any one else came across the same thoughts on the issue and what was their approach .
Some background, Our team is required to define a way to give our system the ability to be themeable.
We broke this ability down to 2 categories :
1) The styles of our controls which we simply call 'Theme'.
2) The resources they use to customize their appearance called 'Skin' this includes Brushes , and all sorts of sizing structs like CornerRadius , BorderThickness etc.
The way which a Skin is set for the system is a simple case of merging the skin dictionary last into our app's resources.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Default.skin.xaml" />
<ResourceDictionary Source="Theme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
A different skin being merged last into our app.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
string skin = e.Args[0];
if (skin == "Blue")
{ .
ResourceDictionary blueSkin = new ResourceDictionary();
blueSkin.Source = new Uri("Blue.skin.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(blueSkin);
}
}
Inside Theme.xaml :
<!-- Region TextBox ControlTemplate -->
<ControlTemplate TargetType="{x:Type TextBox}" x:Key="TextBoxTemplate">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource TextBoxCornerRadius}" >
<Border x:Name="shadowBorder" BorderBrush="{StaticResource TextBoxShadowBrush}"
CornerRadius="{StaticResource TextBoxInnerShadowCornerRadius}"
BorderThickness="{StaticResource TextBoxInnerShadowBorderThickness}"
Margin="{StaticResource TextBoxInnerShadowNegativeMarginForShadowOverlap}" >
<ScrollViewer x:Name="PART_ContentHost" Padding="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="BorderThickness" Value="0">
<Setter TargetName="shadowBorder" Property="BorderThickness" Value="0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- EndRegion -->
<!-- Region TextBox Style -->
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorderBrush}" />
<Setter Property="Background" Value="{StaticResource TextBoxBackgroundBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource TextBoxBorderThickness}" />
<Setter Property="Padding" Value="{StaticResource TextBoxPadding}" />
<Setter Property="Template" Value="{StaticResource TextBoxTemplate}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource TextBoxIsMouseOverBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource TextBoxIsMouseOverBorderBrush}" />
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="{StaticResource TextBoxIsMouseWithinBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource TextBoxIsMouseWithinBorderBrush}" />
</Trigger>
</Style.Triggers>
</Style>
<!-- EndRegion -->
In the TextBox ControlTemplate there are elements bound to DependencyProperties using TemplateBinding and some like the CornerRadius and InnerCornerRadius, InnerBorderThickness and InnerBorderBrush which are given their value from resources.
What would be the best approach ?
creating a derived control with the relevant Dependency properties
which would reference the relevant resources and then have the elements in the control template bind to them.
Or
have the elements inside the template reference these resources themselves.
Using the Dependency Property approach :
Advantages :
1) Clarity, we have a clearer API for our control and better understanding of how our control looks and behaves the way it does.
2) The template does not have to change in order to be customizable. Everything is controlled via style.
3) Triggers as well change the look and feel of the control without the need to override the control template, no need for ControlTemplate triggers.
4) "Blendabilty" using blend i can much easily customize my control.
5) Styles themselves are inheritable. so if i want to change just one aspect of the control all i need to do is inherit from the default style.
Disadvantages :
1) Implementing yet another custom control.
2) Implementing numerous dependency properties, some of which do not have much to do with the control and are only there to satisfy something we have in our template.
Just to clarify this means inheriting from TextBox something like InnerShadowTextBox and
implementing dependency properties with in it for all the above.
This will intensify if I have numerous elements inside my template which have to be customizable.
Something like this monstrosity:
<Style x:Key="{x:Type cc:ComplexControl}" TargetType="{x:Type cc:ComplexControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type cc:ComplexControl}">
<Grid>
<Ellipse Fill="Red" Margin="0" Stroke="Black" StrokeThickness="1"/>
<Ellipse Fill="Green" Margin="6" Stroke="Red" StrokeThickness="1"/>
<Ellipse Fill="Blue" Margin="12"/>
<Ellipse Fill="Aqua" Margin="24" />
<Ellipse Fill="Beige" Margin="32"/>
<StackPanel Orientation="Horizontal" Width="25" Height="25"
VerticalAlignment="Center" HorizontalAlignment="Center">
<Rectangle Fill="Black" Width="2" />
<Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/>
<Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/>
<Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which would require numerous resources :
<SolidColorBrush x:Key="Ellipse1Fill">Red</SolidColorBrush>
<SolidColorBrush x:Key="Ellipse2Fill">Green</SolidColorBrush>
<SolidColorBrush x:Key="Ellipse3Fill">Blue</SolidColorBrush>
<SolidColorBrush x:Key="Ellipse4Fill">Aqua</SolidColorBrush>
<SolidColorBrush x:Key="Ellipse5Fill">Beige</SolidColorBrush>
<SolidColorBrush x:Key="Ellipse1Stroke">Beige</SolidColorBrush>
<sys:Double x:Key="Ellipse1StrokeThickness>1</sys:Double>
......... and many more
I would have a large list of resources either way. But with dependency properties.
I would also need to assign need to find meaning in every little part,Which sometimes isn't much more then "it looks good" and does not have much to do with the control or What if tomorrow I would want to change the template.
Using the approach where the resources are referenced from within the control template.
Advantages :
1) Easy to use, side steps the ugliness describes in the disadvantages described above in the Dp approach while providing a "hack" that enables a theme.
Disadvantages :
1) If I would want to further customize my control like add a trigger that influences the inner border of my TextBox I would simply have to create a new control template.
2) Not a clear API, Lets say I would like to change the BorderBrush of the inner border in a specific view.
<TextBox>
<TextBox.Resources>
<SolidColorBrush x:Key="InnerBorderBrush" Color="Red" />
</TextBox.Resources>
</TextBox>
Which isn't that bad come to think about it…
we sometimes do this with Selector implementations which internally use the specific resources when getting rid of the inactive selection and hightlight colors like so :
<ListBox>
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="Transparent"/>
</ListBox.Resources>
</ListBox>
Conclusions :
The hybrid described in the TextBox Style above is the way to go.
1) Dependency properties will be introduced only for aspects of the control which relate to the control's logic including specific template part's .
2) The resource names would be comprised by a clear naming convention and separated in files based on the control they relate to and common usages in views,Like Common brushes used in views in our app.
3) Control templates should aspire to be minimalistic and to use existing Dependency properties. Like Background, Foreground, BorderBrush etc.
I would greatly appreciate your input and thoughts on the matter , thanks in advance.
As Xavier said, this might be a better question for Code Review. But I will convey some key thoughts on your question, even though a lot of it will come to personal (or team) style and requirements.
After creating several dozen themes, I would recommend against custom controls whenever possible. Over time, the maintainability goes down quite a bit.
If you require minor modifications to a style, it is better to use DataTemplates and Data Triggers if the situation allows. This way you are changing the style in a clean way.
Additionally, you can leverage the BasedOn property. Create your "base" style and have multiple styles that have the attribute BasedOn="{myBaseStyle}. This will allow you lots of options without cluttering your code.
As a rule of thumb, I always recommend having more brushes/colors/resources as opposed to more styles or templates. We usually have our hierarchy set for colors->brushes->styles->templates. This helps reuse the colors while still maintaining separation via brushes.
Using DynamicResource as opposed to StaticResource is also useful in some situations where you load resources dynamically.
Hope this helps. Would love to write more, but some of the parameters for writing a solid theme is very context specific. If you have further examples, I'd be glad to add more information.

When button isEnabled is false change background

I have an Itemscontrol with an Itemscontrol with buttons. This makes a matrix with 64 buttons.
Each button is bound to a ViewModel. The buttons have an eclipse that has a color.
A button can have two states.
1) The ViewModel gives a Color for the eclipse in the button and then the button IsEnabled=true.
2) the button IsEnabled=false and the button is not clickable anymore.
I would to have a transparant background for number 2 (when the button is not enabled). I worked something out and got this (see code on Pastebin), but now my borders are gone and everything is transparant.
How can I get a border for each button that is visible all the time, so it is not restricted to number 1 and 2.
Here is my code. I put it on Pastebin to save space.
Pastebin: http://pastebin.com/4GHCW0y8
Thanks
There are a couple issues with your code:
You are setting the Background, BorderBrush and BorderThickness properties directly on your button instead of using setters in the style. Setting them this way overrides anything the style does, so you cannot see the effects of the style triggers. This is the line I am referring to:
<Button Style="{StaticResource MyButton2}" Command="{Binding PlaceStoneCommand}" Background="Transparent" BorderBrush="Black" BorderThickness="0.75" Width="30" Height="30">
Remove the Background, BorderBrush and BorderThickness properties from that line and make setters in the style instead. Also, note that you are setting the default to transparent, so even with this change, you are just switching between Transparent and Transparent when IsEnabled changes.
Your style is overriding the control template for the button and not using the BorderBrush or BorderThickness properties in the template, so setting those properties on the button is not going to have any effect. You probably want to setup template bindings for those properties on the Border defined in the template. So, you would end up with a style looking something like this:
<Style TargetType="{x:Type Button}" x:Key="MyButton2">
<!-- Put default values here, or remove these if you want to use the existing button defaults -->
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
</Trigger>
</Style.Triggers>
</Style>
See if those changes give you the desired result.

Binding to CustomControls issue

I have a custom control that inherit from TextBox with dependcy property called State.
State is enum with 4 options {Mismatch, Wait, Ok, None}
The Generic looks like this:
<Style TargetType="{x:Type local:CustomTextBox}">
<Style.Triggers>
<Trigger Property="State" Value="Mismatch">
<Setter Property="Background" Value="{StaticResource MismatchBrush}"/>
</Trigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomTextBox}">
<Border Background="{TemplateBinding Background}"
BorederBrush="{TemplateBinding BorederBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
I have class Named MyData with properties DataState, Sent, Recieved each property implement INotifyPropertyChanged.
In my main window i have the following row in the xaml:
<custom:CustomTextBox State="{Binding BindingData.DataState}" Text="{Binding BindedData.Sent}"/>
and in my view model i have field from type MyData that called BindedData and when user send some data the BindedData.Sent change and then if some logic occur the BindedData state change to Mismatch and the customTextBox background need to be in some color.
The problem is that the background not change its mean the binding of the state not working and also the text binding not working.
I know how the binding works and its work for me in other cases (not with custom controls) and here i also checked in debug and i saw that all going right so did i make something wrong with the customcontrol?
Thanks in advance!
maybe you set the background again from the main window?
I have tried copy your code and it works perfectly , unless I set the background from the main window (since it overrides the style setters)

In code-behind how can I override tabitem colors set by an application-wide theme?

I change the background color of my TabItem conditionally in code behind. This works fine as long as no theme is set in the App.xaml. How can I change colors of a TabItem (in code) while keeping an application-wide theme?
Background:
I'm using a free WPF theme from Nukeation Reuxables. The theme is set in the Application.Resources section of my App.xaml:
<ResourceDictionary Source="/ReuxablesLegacy;component/Edge.xaml" />.
I'm trying to conditionally set the background color of a TabItem in code behind:
MyTabItem.Background = new SolidColorBrush(Colors.Gray);
The background color changes if I remove or comment out the App.xaml line that sets the theme. Why does the theme break my code? I'm changing the tab color (as data is loaded) to show which tabs contain data.
I'm aware that XAML and binding are typically used to change colors, but the solutions I've attempted seem overly complex. Here is my related StackOverflow question seeking an all XAML and binding solution. The answers given just raised more questions which I haven't found answers to.
Thanks in advance.
Edit:
The problem does not happen when changing the background of a button:
button1.Background = new SolidColorBrush(Colors.Red);
The button changes color as expected (while using an application-wide theme).
It REALLY depends on how the theme itself is implemented. If the theme was using TemplateBinding to bind to the background color of the tab to the theme's controls, then your code behind solution would work. That is probably why your button properly changes background colors in your example.
You'll probably have to dig deep into the tab's style xaml, and modify it there first for the codebehind solution to work.
If you look at the ControlTemplate of TabItem its Background property is bound to a internal resource it doesn't do TemplateBinding, I presume the theme has just given colors to it. For your code to work you have to restyle it.
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,-4,0"
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have made two changes to the normal ControlTemplate, you can do the same to your theme.xaml.
Changed Background="{StaticResource LightBrush}" to Background="{TemplateBinding Background}"
Removed Background setter in IsSelected = True trigger.
Unfortunately, this is a simple problem with no simple answer (that I have found so far). Taking a different path to satisfy users was my final solution.
Instead of attempting to change the TabItem Background color I simply change the TabItem Foreground color to Gray if NO data exists. I also add the word "Blank" like this: "Tab 1 Blank", "Tab 2 Blank", etc. This an effective solution to the original problem. I describe it in detail here.

Resources