Override dictionary style with local style - c#

I have a user control (a modernTab, provded by modernui) that has a style applied to it, as is specified in a resource dictionary (that again came with modernui).
That's fine, styling for this app is provided through some default resources in the App.xaml file that look like this:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>
</ResourceDictionary.MergedDictionaries>
That's well and good. However, I want to override the link style I am using for a specific instance of a modernTab. So in my XAML, I'm trying to do it like this:
<mui:ModernTab ListWidth="Auto"
Layout="List"
Links ="{Binding MyViewModelLinks}">
<mui:ModernTab.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="Yellow" />
</Style>
</mui:ModernTab.Resources>
</mui:ModernTab>
Now, I know from looking at the source that down inside the guts of a modernTab control it's got a bunch of ListBoxItems - these are what I want to change the style on.
What I don't get is why my "local" style isn't going down and overriding for this specific instance. Any ideas?
I tried defining my style override in App.xaml (even though I don't really want it to be global) and it didn't work. Clearly I'm missing something.

What you are doing here is not overriding default style of ModernTab but specifying resources of a particular instance, the style is still taken from ModernTab.xaml
What you need to do here is to specify inline style for your instance of ModernTab:
<mui:ModernTab ...>
<mui:ModernTab.Style>
<Style TargetType="mui:ModernTab">
<!------- Full ModernTab Style ----->
</Style>
</mui:ModernTab.Style>
This inline style will override the default. The bad news is that you cannot create a style based on default ModernTab style and just tweak small details because the default style does not have a name (x:Key). But you can copy the whole style, change whatever you want in it, and use it instead. You should probably put it in a resource file and then use it on your ModernTab instance like this:
<mui:ModernTab Style={StaticResource MyAwesomeStyle} .../>
Hope this helps

You need to "override" the ItemContainerStyle of the ListBox in the ModernTab. This should do the trick:
<mui:ModernTab ListWidth="Auto"
Layout="List"
Links ="{Binding MyViewModelLinks}">
<mui:ModernTab.Resources>
<Style TargetType="ListBox">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="Yellow" />
</Style>
</Setter.Value>
</Setter>
</Style>
</mui:ModernTab.Resources>
</mui:ModernTab>

Related

C# WPF Xaml: Globally set all text in a view to one color, and all backgrounds to another

Is there a way (using data binding or simply xaml) to set the background of all elements in a view to one color, and all text to another?
I know I can edit each element in the view one by one, but I I'd like to see if this is possible with settings at a global level. Kind of like how everything by default is set to black on white.
I guess what I'm asking is if there is a feature/setting of a WPF application that offers what I'm looking for, and/or what I should search to find an answer online.
My project isn't using anything but what visual studio offers when you create a WPF project, so I can't use a prism or mvvm light approach.
Thanks in advance for your answer!
Globally...or simply XAML...
if there is a feature/setting of a WPF application that offers what I'm looking for
In Application Resource add style like this:
<Style TargetType="Control">
<Setter Property="Background" Value ="Blue"/>
<Setter Property="Foreground" Value ="Red"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Background" Value ="Blue"/>
<Setter Property="Foreground" Value ="Red"/>
</Style>
Based on your target element you want to set background.
When you say "the background of all elements in a view" you should be more specific, If by 'element' you mean UIElement then there is no Background property in UIElement. If it means Control then not all UIElementsderive from Control (e.g. TextBlock) and finally if it means every UIElement derived type defined in your view that have a Background property, then you should add different styles for each type without setting the x:key to the YourView.Resources like this:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Blue" />
</Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Blue" />
</Style>
...
</Window.Resources>
uses controls collection through which you can control all controls in WPF

General WPF Rendering Rules (Multiple styles on same target type)

Relating on this question (Multiple styles on same target type) i have another point concerning the general wpf styling rules.
As mentioned in one of the answers above, wpf applies the closest style definition to the control when no x:key is specified. (also applies to styles with the same key; given the fact that we have the same key here: the control type)
My question:
What if we have two same-named styles in different resource dictionaries that are both equal close and accessible to the control?
Compared to CSS you have two definitions for i.e. an a-element. In CSS same style properties are getting overriden by the last definition while different properties are merged into one logical style definition.
Does WPF behave the same or does it prefer one whole style-resource with all its properties while avoiding the other one completely?
And if the latter how does it decide to apply one or the other.
Update
dict1.xaml:
<ResourceDictionary>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="BorderBrush" Value="SomeValue1" />
<Setter Property="Background" Value="SomeValue2" />
<Setter Property="FontSize" Value="SomeValue3" />
</Style>
</ResourceDictionary>
dict2.xaml:
<ResourceDictionary>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="BorderBrush" Value="SomeValue4" />
<Setter Property="Foreground" Value="SomeValue5" />
</Style>
</ResourceDictionary>
SomeView.xaml:
...
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="path/dict1.xaml" />
<ResourceDictionary Source="path/dict2.xaml" />
</ResourceDictionary.MergedDictionaries>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="FontSize" Value="SomeValue6" />
</Style>
</Grid.Resources>
<Button /><!--THIS IS THE CONTROL THAT MATTERS-->
</Grid>
...
I think for FontSize its simple, it applies SomeValue6 because its the closest style definition. But what about BorderBrush. Both Dictionaries are equally close to the Button and are both accessible.
And What about Background and Foreground? Does it merge both style definitions to something like this:
<ResourceDictionary>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="BorderBrush" Value="SomeValue4" />
<Setter Property="Background" Value="SomeValue2" />
<Setter Property="Foreground" Value="SomeValue5" />
<Setter Property="FontSize" Value="SomeValue6" />
</Style>
</ResourceDictionary>
Of course the chaos is much bigger when you include cross-assembly dictionaries, that depend on each other and you are seeking where the actual style comes from.
This code example isn't working because of the merged dictionary and its hard to isolate the exact behavior because my actual application is really chaotic like I described.
So I want to know the official rules intended by Microsoft that should be basic for every developer. But I didnt find anything in the docs.

Why Is Not Default Style Applied?

For some reason, I made a CustomButton class.
I tried to apply the default Button style to a CustomButton.
<my:CustomButton Style={StaticResource {x:Type Button}}/>
But this doesn't work. Do you think why?
<Button x:Name="button1" />
<CustomButton Style={Binding Style, ElementName=button1} />
This works well. But I think it's not very good.
You have to create a style for your button and apply the style.
If you remove x:Key="ButtonStyle1" then your style will be applied to all your CustomButton, of course if your style for the button is included
<Style x:Key="ButtonStyle1" TargetType="{x:Type yourNameSpace:CustomButton}">
<Setter Property="Foreground"
Value="Red" />
<Setter Property="Margin"
Value="10" />
</Style>
<yourNameSpace:CustomButton Width="100" Height="100" Style="{StaticResource ButtonStyle1}"> </yourNameSpace:CustomButton>
You should check Styling and Templating and this Tutorial
Style={StaticResource {x:Type Button}} this is wrong is expecting a key for a resource not a type.
From documentation StaticResource:
Provides a value for any XAML property attribute by looking up a reference to an already defined resource. Lookup behavior for that resource is analogous to load-time lookup, which will look for resources that were previously loaded from the markup of the current XAML page as well as other application sources, and will generate that resource value as the property value in the run-time objects.

How to give my custom control an overridable default margin?

Problem
I've created a custom control (OmniBox), which has its base style set with:
<Style x:Key="GridStyle" TargetType="Grid" BasedOn="{StaticResource BaseElement}">
<Setter Property="Margin" Value="0,2" />
</Style>
But when I'm using my control, I want to be able to do something like:
<UserControl.Resources>
<Style TargetType="{x:Type ui:OmniBox}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,10"/> <!--Not Working?-->
</Style>
</UserControl.Resources>
<Grid>
<StackPanel>
<ui:OmniBox x:Name="One"... />
<ui:OmniBox x:Name="Two"... />
...
And have all instances of my control take on that default margin. Unfortunately, my controls are not responding to the style set in the resources. They are just keeping their default margin of "0,2".
Strangely, if I explicitly set the margin on my controls like so:
<ui:OmniBox x:Name="One" Margin="0,10" Style="OBDefaultStyle" ... />
<ui:OmniBox x:Name="Two" Margin="0,10" ... />
...
They DO use the margin of "0,10" rather than "0,2". How come the template type isn't working?
If it's relevant, my OmniBox control templates all look like this:
<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
<Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>
<ControlTemplate TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultTemplate">
<Grid x:Name="PART_Grid" Style="{StaticResource GridStyle}">
... (Content)
</Grid>
</ControlTemplate>
First Attempt
In my grid style, I've tried setting Margin to
<Setter Property="Margin"
Value="{Binding Path=Margin, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:OmniBox}}}" />
But it didn't help in sucking down the templated margin.
Second Attempt
I tried creating a custom margin dependency property and binding the grid to that:
<Style x:Key="GridStyle" TargetType="Grid" BasedOn="{StaticResource BaseElement}">
<Setter Property="Margin" Value="{Binding Path=MyMargin, RelativeSource={RelativeSource TemplatedParent}}" />
</Style>
My custom property was defined as:
public static readonly DependencyProperty MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(OmniBox), new FrameworkPropertyMetadata(new Thickness(0,2,0,2), new PropertyChangedCallback(OnMarginChanged)));
Anyways it didn't work. The default margin set in the dependency property above is still overriding the margin I'm trying to set in the style template.
You can add a default style for a custom control by overriding the metadata for the DefaultStyleKey:
public class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
You then create a resource dictionary called Generic.xaml that is located in a directory called Themes in the root of the project (so the path will be "/Themes/Generic.xaml"). In that resource dictionary you create a default style for your control:
<!-- Base the style on the default style of the base class, if you don't want to completely
replace that style. If you do, remember to specify a new control template in your style as well -->
<Style TargetType="SomeNamespace:MyButton" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Margin" Value="10" />
</Style>
If you just add a MyButton control it will get the default style, but you can override properties set in the default style by applying a new style:
<Window x:Class="SomeNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:SomeNamespace="clr-namespace:SomeNamespace"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="SomeNamespace:MyButton">
<Setter Property="Margin" Value="20" />
</Style>
</Window.Resources>
<Grid>
<SomeNamespace:MyButton />
</Grid>
</Window>
GridStyle specifies TargetType="Grid", so the setter <Setter Property="Margin" Value="0,2" /> applies to the Grid at the root of the control template. Setting the Margin property of the containing OmniBox has no effect of the margin of that grid.
Try specifying this in the template:
<Grid x:Name="PART_Grid" Margin="{TemplateBinding Margin}">
Notice I did not set the Style property as you did in the template. This is because the grid's Margin property will always reflect the Margin property of the OmniBox containing it, negating the effect of the Margin property in GridStyle. Instead you will want to default the OmniBox.Margin property and remove GridStyle entirely:
<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
<Setter Property="Margin" Value="0 2" />
<Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>
Have you overridden the DefaultStyleKey property in your OmniBox control?
After happening on this question, I figured out what I needed to do. In the control's class, I need to override the margin property's default value:
static OmniBox()
{
MarginProperty.OverrideMetadata(typeof(OmniBox), new FrameworkPropertyMetadata(new Thickness(0,2,0,2)));
}
After that, I get rid of the margin on the "Grid" component of the omnibox completely, since the control itself carries a margin. Now when the user sets the "Margin" property on the OmniBox, it accepts it, if they don't, it uses the default value.
Thank you all so much for your suggestions and effort.

Silverlight apply implicit style that sets other style

I have a TextBox Style with a Key defined in external resource dictionary file, then I'm trying to define a new implicit TextBox Style that sets "Keyed" style from resource dictionary, so basically I want to seet the style from resource dictionary as a default for TextBox, but I can't remove Key from there, because it is used by other code.
<ResourceDictionary Source="FileWithNiceTextBoxStyle.xaml"/>
<Style TargetType="TextBox">
<Setter Property="Style" Value="{StaticResource NiceTextBoxStyle}"/>
</Style>
However this doesn't work, and result in Visual Studio crash.
Use the BasedOn attribute:
<Style TargetType="TextBox" x:Key="GlobalTextBox">
<Setter Property="Background" Value="Pink"/>
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource GlobalTextBox}"></Style>
...
<TextBox Text="I have pink background"/>

Categories

Resources