WPF - style hyperlinks in DataGrids - wpf

I need to style hyperlinks in WPF4 DataGrid control (they appear in columns of type DataGridHyperlinkColumn). I have many DataGrids in the project and would like to apply the hyperlink style to all of them.
I found this Q&A: WPF Style DataGridHyperlinkColumn and created the style for HyperLink control:
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="TextDecorations" Value="" />
</Style>
It works fine, but obviously it also affects all other hyperlinks, eg. in
<TextBlock>
<Hyperlink NavigateUri="http://www.google.co.in">Click here</Hyperlink>
</TextBlock>
How can I target only hyperlinks in DataGrids? In CSS syntax it would be something like
DataGrid Hyperlink {TextDecorations: ""; }

Due to property value inheritance all instances of links inherits the style that you have created because you did not use x:key attribute.
You can add x:Key attribute:
<Style TargetType="{x:Type Hyperlink}" x:Key="HyperlinkStyle1">
<Setter Property="TextDecorations" Value="" />
</Style>
by using this you can reference this from your controls like below:
<Hyperlink NavigateUri="http://www.google.co.in" Style={StaticResource HyperlinkStyle1}>Click here</Hyperlink>

Related

How to "inject" style in a DataTemplate

I am trying to separate the DataTemplates from the Styles in my code.
I use DataTemplate to define, e.g. that the data should be displayed as two buttons and I use styles to define, e.g. that the background of those buttons should be green.
What I am trying to achieve is having a DataTemplate defined in one file and using that in multiple UserControls where the style comes from the UserControls.
Let's say I have the following style in the Resources of a UserControl:
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
Another UserControl might have something similar with different colors.
Then, I have a ContentControl in that UserControl that will have some view model and some DataTemplate:
<ContentControl Content="{Binding SelectedViewModel}"
ContentTemplate="{Binding SelectedDataTemplate}"/>
The DataTemplate can be something as simple as this:
<DataTemplate x:Key="TwoButtonsTemplate">
<StackPanel>
<Button Content="One"/>
<Button Content="Two"/>
</StackPanel>
</DataTemplate>
I would like the two buttons to have the ButtonStyle from the UserControl.Resources without directly referencing it. (So the DataTemplate can come from a different file or being able to use the DataTemplate in a similar context with another UserControl's style).
I tried to change the TargetType of ButtonStyle to ContentControl, assign the style to the ContentControl and set Foreground="{TemplatedParent Foreground}" on the Buttons, but in this way both Foreground will change when any of them (i.e. the ContentControl itself) is hovered.
Is there any way of "inheriting" a style in the DataTemplate or "injecting" the style from the UserControl?
P.S. I understand if I move the style into a separate file and reference that in the DataTemplate file I can simply use it as StaticResource, but that will couple the DataTemplate to that specific style and I won't be able to re-use it with other styles.
try DynamicResource:
<DataTemplate x:Key="TwoButtonsTemplate">
<StackPanel>
<Button Content="One" Style="{DynamicResource ButtonStyle}"/>
<Button Content="Two" Style="{DynamicResource ButtonStyle}"/>
</StackPanel>
</DataTemplate>
when TwoButtonsTemplate template is instantiated in UserControl, which declares ButtonStyle resource, that resource will be found and applied to buttons.

How can I set a custom Controltemplate as the default for whole application?

I have decided to look at trying to customize a number of aspects of the default control template for Menu and MenuItem. In Visual Studio, I selected my Menu, found 'Template' in the property panel, and chose 'Convert to New Resource...'.
I did the same thing for MenuItem. Then, just to test, I changed the MenuItem SubMenuBorder color to red. At this point, I had a resource dictionary with the two ControlTemplates, and a Menu that looked like this:
<Menu Template="{DynamicResource CustomMenuControlTemplate}">
<MenuItem Header="File" Template="{DynamicResource CustomMenuItemControlTemplate}">
<MenuItem Header="Test 1" />
<MenuItem Header="Test 2">
<MenuItem Header="Subtest 1" />
<MenuItem Header="Subtest 2" />
<MenuItem Header="Subtest 3" />
</MenuItem>
<MenuItem Header="Test 3" />
</MenuItem>
</Menu>
The issue is that the red border will only appear on MenuItems where I explicitly set the Template to my modified CustomMenuItemControlTemplate. If I want to have my template used on all menu items, I would have to include:
Template="{DynamicResource CustomMenuItemControlTemplate}"
...on every MenuItem in my entire application.
So, next I thought that I'd have a style that applies to all MenuItems, and use a setter to set my default template:
<Style TargetType="MenuItem">
<Setter Property="Template" Value="{DynamicResource CustomMenuItemControlTemplate}" />
</Style>
This works (note the red borders on all sub-menus), but for some reason, it drastically changes how the menus look:
In that setter, I tried using both DynamicResource and StaticResource bindings, with the same results.
So, my main question: Is there a way to use a custom ControlTemplate as the default, so that I don't have to explicitly set it on each control? Secondary question: Why does using a style to set the template property cause such drastic changes to how it looks?
So you can't put a control template without a key, and the key can't be {x:Type ...} in WPF.
But you can put a Style without a key, and that style will be the default. -- And in that style, you can set the control template.
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<!-- Insert Control Template Here -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
To answer your first question:
If you put the ControlTemplate in your app.xaml's resource dictionary, it will be applied across your entire application. Your control template header should have something like this:
<ControlTemplate TargetType="MenuItem" ...(other properties)>
This will make all MenuItem controls implicitly use that control template.
To answer you second question:
This occurs because adding a style overrides the default style, essentially removing all built-in styling. If you want the style to only change a few things the style tag should look like this:
<Style BasedOn="{StaticResource {x:Type MenuItem}}" ...(other properties)>
This way it inherits all the normal styling and just changes the styles you explicitly define

WPF Apply default style to custom control

I have created a custom combo box control which inherits from Combobox class. There is a type style defined for ComboxBox type on ResourceDictionary which applied automatically for all combo boxes but this is not getting applied the custom control.
Style defined on App.xaml
<Application.Resources>
<Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Gray"/>
</Style>
</Application.Resources>
Custom ComboBox
public class AutoComboBox : ComboBox
MainWindow.xaml
<StackPanel Orientation="Vertical">
<ComboBox Width="200"/>
<local:AutoComboBox Width="200"/>
</StackPanel>
The first combo box will have Background as Gray color as the style defined on App.xaml is applied. But the second combo box still show the default background color.
Is there any way we can apply the same type style on custom control without creating duplicate style for custom control?
Are you using a Theme approach?
If so you should have a ../Themes/Generic.xaml which is your ResourceDictionary.
Your ResourceDictionary should have a <Style> in it with the TargetType="{x:Type local:YourControl}" set for your style.
Furthermore, you should check that in your AssemblyInfo.cs file that ResourceDictionaryLocation.SourceAssembly is set for each of your themes.

WPF: Why are nested styles not always working?

I'm trying to apply a nested WPF style to a Toolbar. I'd like to have all children of the Toolbar (MenuItems, Buttons, ToggleButtons etc.) to have the specified style.
The problem is, that the nested style definition is applied correctly to some controls like MenuItems, but not to Buttons.
What am I doing wrong?
The MenuItem is correctly placed at the bottom of the Toolbar, but the ToggleButton is in the middle:
<Window.Resources>
<Style x:Key="MyToolbarStyle" TargetType="ToolBar">
<!-- Setters for Toolbar properties -->
<Setter Property="Height" Value="80" />
<!-- Nested setters for children of the Toolbar -->
<Style.Resources>
<Style TargetType="MenuItem">
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
<Style TargetType="ToggleButton">
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<Grid >
<ToolBar VerticalAlignment="Top" Style="{StaticResource MyToolbarStyle}">
<MenuItem Header="MyMenuItem" /> <!-- Appears on the bottom like defined in the style-->
<ToggleButton Content="MyToggleButton" /> <!-- Nested style does not seem to be applied-->
</ToolBar>
</Grid>
The WPF ToolBar is a special type of control that defines some custom styles for some WPF controls like Button, ToggleButton... full list here, you can identify them by ElementName + StyleKey property name. If you'd like to change a default style for a specific control you will have to modify one of these styles.
Try replacing your style for the ToggleButton with the following:
<Style x:Key="{x:Static ToolBar.ToggleButtonStyleKey}" TargetType="ToggleButton">
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
What you are doing wrong is thinking that WPF Styles are like CSS styles. In WPF, Styles are just not used that way. Sure, if we could, we'd probably save a few lines of XAML, but we can't. The best that we can do is what you have done... I'm assuming that you've created a Style for a top level element like Control. As you have seen, not all controls will extend the Control class, so the Style won't be applied to all of them.
Instead, Styles in WPF are more like the .class styles in CSS... one Style per type and then we can apply a further Style per UI element. There are lots of situations like this in WPF where we wish we could write less code, but it is how it is and the sooner that everybody realises it, the better.
UPDATE >>>
In response to your first comment, you seem to be mistaken. Just to clarify, if what you are calling nested Styles are the Styles that you defined in the outer Style.Resources section, then there is nothing wrong with that... no problem what-so-ever. Just take those inner Styles out of the Resources section and you will see the same UI.
Now you're probably thinking of changing your question title to something like 'Why isn't my default ToggleButton Style being applied inside a ToolBar control?'. While I can't say for sure, I can only assume that this behaviour is caused by a Style that has been defined within the ToolBar ControlTemplate.
I'm thinking that because of the following points:
A custom implicit Style (no x:Key) will not work inside the ToolBar control.
A custom explicit Style (named) will work as expected inside the ToolBar control.
A Style property set on the element will work as expected inside the ToolBar control.

WPF ControlTemplate breaks style

The stuff that does work
I need to style controls of a certain type that are children of a StackPanel. I'm using:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">...</Style>
</StackPanel.Resources>
<TextBlock ...>
...
</StackPanel>
And this works fine! Each TextBlock looks to the resources of it's parent (the StackPanel) to find out how it should be styled. It doesn't matter how far down you nest the TextBlock down a StackPanel... if it doesn't find a style in its direct parent, it will look at its parent's parent and so on, until it finds something (in this case, the style that was defined in ).
The stuff that doesn't work
I ran into a problem when I nested a TextBlock inside a ContentControl, which had a Template (see code below). The ControlTemplate seems to disrupt the way a TextBlock retrieves its style from its parents, grandparents,...
The use of a ControlTemplate effectively seems to knock out cold the TextBlock's means of finding its rightful style (the one in StackPanel.Resources). When it encounters a ControlTemplate, it stops looking for its style in the resources up the tree, and instead defaults to the style in MergedDictionaries of the Application itself.
<StackPanel Orientation="Vertical" Background="LightGray">
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Green" />
</Style>
</StackPanel.Resources>
<TextBlock Text="plain and simple in stackpanel, green" />
<ContentControl>
<TextBlock Text="inside ContentControl, still green" />
</ContentControl>
<ContentControl>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<StackPanel Orientation="Vertical">
<ContentPresenter />
<TextBlock Text="how come this one - placed in the template - is not green?" />
</StackPanel>
</ControlTemplate>
</ContentControl.Template>
<TextBlock Text="inside ContentControl with a template, this one is green as well" />
</ContentControl>
</StackPanel>
Is there a way - besides duplicating the Style in StackPanel.Resources to ControlTemplate.Resources - to make the TextBlock inside that ControlTemplate find the defined style?
Thanks...
WPF considers ControlTemplates to be a boundry, and will not apply implicit styles (styles without an x:Key) inside of templates.
But there is one exception to this rule: anything that inherits from Control will apply implicit styles.
So you could use a Label instead of a TextBlock, and it would apply the implicit style defined further up your XAML hierarchy, however since TextBlock inherits from FrameworkElement instead of Control, it won't apply the implicit style automatically and you have to add it manually.
My most common way to get around this is to add an implicit style in the ControlTemplate.Resources that is BasedOn the existing implicit TextBlock style
<ControlTemplate.Resources>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource {x:Type TextBlock}}" />
<ControlTemplate.Resources>
Other common ways of getting around this are:
Place the implicit style in <Application.Resources>. Styles placed here will apply to your entire application, regardless of template boundaries. Be careful with this though, as it will apply the style to TextBlocks inside of other controls as well, like Buttons or ComboBoxes
<Application.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Green" />
</Style>
</Application.Resources>
Use a Label instead of a TextBlock since it's inherited from Control, so will apply implicit Styles defined outside the ControlTemplate
Give the base style an x:Key and use it as the base style for an implicit TextBlock styles inside the ControlTemplate. It's pretty much the same as the top solution, however it's used for base styles that have an x:Key attribute
<Style x:Key="BaseTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Green" />
</Style>
...
<ControlTemplate.Resources>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource BaseTextBlockStyle}" />
<ControlTemplate.Resources>

Resources