Why Is Not Default Style Applied? - c#

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.

Related

Using Style.Triggers without overriding default style [duplicate]

I had a button that was styled in the default metro style. However when I added a trigger to the button, the style is overridden. How to preserve the original mahapps metro styling when adding your own style and triggers?
<Button x:Name="startButton" Content="Start" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,20,26" Width="75" Click="startButton_Click" Height="67" Grid.Column="1">
<!--<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>-->
</Button>
This is how the button looked originally
This is how it looks after I added the trigger
You need to set the "BasedOn" property of your style to the one mahapps.metro is providing as default:
<Style TargetType="Button" BasedOn="{StaticResource MetroButton}">
The documentation doesn't say the default static resource, but it's OpenSource so easy to track it down in the source code for the Controls.xaml you import to load the default styles in your app.xaml (or top of window etc):
https://github.com/MahApps/MahApps.Metro/tree/develop/src/MahApps.Metro/Styles
A search for TargetType="Button" finds our default style (without a key):
Which is even also based on the base style, MahApps.Metro.Styles.MetroButton.

Override dictionary style with local style

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>

Wpf, style is not being applied

I've written a user control with popup, who's content is being set outside the control. The ControlTemplate of that control looks like the following:
<ControlTemplate TargetType="local:InfoIcon">
<Grid>
<ToggleButton x:Name="HelpButton" Style="{StaticResource HelpButton}" />
<Popup PlacementTarget="{Binding ElementName=HelpButton}" Placement="Bottom"
IsOpen="{Binding ElementName=HelpButton, Path=IsChecked, Mode=TwoWay}" StaysOpen="False">
<Border BorderBrush="#767676" BorderThickness="1"
Background="#f1f2f7">
<Border.Resources>
<!-- Important -->
<Style TargetType="Label">
<Setter Property="Foreground" Value="#575757" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#575757" />
</Style>
<!-- /Important -->
</Border.Resources>
<ContentPresenter Content="{TemplateBinding HelpContent}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
The Important part - I want to assign custom styles to items, which are being put inside the popup (it serves as a clickable hint)
I'm using my control in the following way:
<local:MyControl>
<local:MyControl.HelpContent>
<TextBlock>Ala ma kota</TextBlock>
</local:MyControl.HelpContent>
</local:MyControl>
But despite styles in the Border, TextBlock's text's color always inherit the value from its parent (checked using Snoop) - resulting in white text on white background.
You can downlad the small PoC application, which demonstrates the problem.
My observations:
The styling does work for Label. It only doesn't work for TextBlock.
When I add TextBlock.Foreground="Red" to the Border, TextBlock becomes red, still ignoring style (but now using color from Border).
Snoop informs, that this TextBlock actually has the Style resolved correctly. But despite it shouldn't, it uses the inherited value instead of one specified in the style.
How can I solve this problem and why does it occur?
I received answer on Microsoft forums; I'll leave it here in case someone encounters the same problem.
The difference is that a TextBlock is not a control, i.e. it doesn't have any ControlTemplate and because of this the implicit style doesn't get applied to it when it is located inside the StackPanel. Please see the following page for more information: http://blogs.msdn.com/b/wpfsdk/archive/2009/08/27/implicit-styles-templates-controls-and-frameworkelements.aspx
You could use Label elements or set the style for the TextBlock elements explicitly.
-- Magnus (MM8)
Edit2
I've set the Foreground of the UserControl to something else. This behavior is because the child TextBlock controls of the UserControl inherit the Foreground-Settings somehow. This has nothing to do with the popup or some other approaches we tried yet.
I've stumbled upon another question with a similar problems here: Cannot override controls foreground colour in wpf
I suggest to accept this strange behavior and just set a Foreground Color of the UserControl instead:
<Style TargetType="{x:Type local:InfoIcon}">
<Setter Property="Foreground" Value="Red"/>
</Style>
previous Edit
You had my curiousity with this weird behavior, but after looking at your PoC it was rather obvious :) The Popup has some attached Properties TextElement.* where you can style the text elements in the popup. This was new to me, too and I will reseach a bit more afterwards. Nevertheless: Workaround for your Problem is to not style the TextBlock but the Popup instead. your code could look something like following :
<ControlTemplate TargetType="{x:Type local:InfoIcon}">
<ControlTemplate.Resources>
<Style TargetType="Popup">
<Setter Property="TextElement.Foreground" Value="Red"/>
</Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
</Style>
</ControlTemplate.Resources>
<Grid>
<ToggleButton x:Name="TB" Width="16" Height="16"/>
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=TB}" IsOpen="{Binding ElementName=TB, Path=IsChecked}" StaysOpen="False">
<ContentPresenter Content="{TemplateBinding InfoContent}"/>
</Popup>
</Grid>
</ControlTemplate>
I changed the styles to be outside of the controls, of course you can just use the attached properties of the popup directly. But initially you wanted to know how it works with the styles attached at the border, it does not matter now where you add the styles. You can use a ResourceDictionary for example.
As a suggestion, shouldn't this:
TargetType="local:InfoIcon"
be like this?
TargetType="{x:Type local:InfoIcon}"
Maybe you have some TextBlock style defining that it shouldd take the parent's control foreground.
Did you try to add a BasedOn property like this ?
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="#575757" />
</Style>
I tried with your code example and this works :
<ContentPresenter Content="{TemplateBinding InfoContent}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="TextBlock.Foreground" Value="Red" />
</Style>
</ContentPresenter.Style>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Red" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
That's kind of odd because when I put the Foreground setter for the Label control inside the ContentPresenter.Style then this time it's Label wich doesn't work...I think it's because Label is a considered as a ContentControl whereas TextBlock is just a FrameworkElement.
Had a similar issue caused by another problem:
There is a strange bug in WPF that prevents styles, defined in merged dictionaries, from being applied to the first element:
https://www.engineeringsolutions.de/wpf-fix-style-is-only-applied-to-first-element/

How to have multiple window resource styles for same element type in WPF

I have an element in my window, as below:
<Grid>
<DockPanel LastChildFill="True">
<Label Name="StatisticsLabel" DockPanel.Dock="Bottom"></Label>
<RichTextBox Style="{StaticResource FocusMode}" Name="RichTextBox1" />
</DockPanel>
</Grid>
I would like to swith between two styles at runtime depending of the state I need the control to be in.
I had assumed I could use the following code:
<Window.Resources>
<Style x:Name="FocusMode" TargetType="RichTextBox">
<Setter Property="VerticalScrollBarVisibility" Value="Disabled"></Setter>
</Style>
<Style x:Name="NormalMode" TargetType="RichTextBox">
<Setter Property="VerticalScrollBarVisibility" Value="Auto"></Setter>
</Style>
</Window.Resources>
Of course this isn't working.
Why does WPF not support multiple styles per element? Seems like a pretty basic requirement?
Otherwise, how do I achieve this?
Sorry figured it out, instead of x:Name use x:Key as below:
<Window.Resources>
<Style x:Key="FocusMode" TargetType="RichTextBox">
<Setter Property="VerticalScrollBarVisibility" Value="Disabled"></Setter>
</Style>
<Style x:Key="NormalMode" TargetType="RichTextBox">
<Setter Property="VerticalScrollBarVisibility" Value="Auto"></Setter>
</Style>
I'd have a look at style triggers. You can probably get a good start on the subject from this post: How to make Style.Triggers trigger a different named style to be applied

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.

Categories

Resources