I have a application that already uses a style template to defining font family, font size, foregrounds colors and so on.
One funcionnality of that app is enable users to picker a particular color and then apply to all texts, including textblocks, listviews, buttons and etc.
I've already read this link (Find all controls in WPF Window by type), where I could find all objects' type of FrameworkElement and then apply the color to each element according with his type. However, I am not sure that is the best approach.
As you said, there are lots of way of doing this.
For starter, you can have a style template and then use StaticResource to get the style for it. For example:in your view mainwindow.xaml
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="baseStyle">
<Setter Property="FontSize" Value="100" />
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource baseStyle}"></Style>
<!-- ComboBox, RadioButton, CheckBox, etc... -->
</Window.Resources>
OR Another way is:
View: mainwindow.xaml
<Window>
<Window.resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="FontSize"
Value="14" />
</Style>
</Window.resource>
<Grid>
<TextBox Text="blah"/>
</Grid>
</Window>
OR
you can do it from mainwindow constructor
Style = (Style)FindResource(typeof(Window));
and this, you will put this in the app.xaml
<Style TargetType="{x:Type Window}">
<Setter Property="FontSize"
Value="14" />
</Style>
Related
I want to create a simple custom control that extends TextBox.
I create it by Add -> New Item... -> Custom Control and I make some change on the code generated automatically. I change the base class of the CustomControl into TextBox and delete the Template setter in Theme/Generic.xaml file.
But when I add it to MainWindow and run, it is blank. Here is my final code:
File Theme/Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="BorderThickness" Value="10"/>
</Style>
</ResourceDictionary>
File CustomControl.cs:
namespace Test
{
public class CustomControl : TextBox
{
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
}
}
There's nothing in it. It needs a template.
There are two ways to do that: First, easiest, base your Style on the default TextBox style. That'll give you the default template and everything else in the default style. Add setters at will to override the inherited ones, if you wish.
<Style
TargetType="{x:Type local:MyCustomControl}"
BasedOn="{StaticResource {x:Type TextBox}}"
>
<Setter Property="BorderThickness" Value="10"/>
<Setter Property="BorderBrush" Value="Black"/>
</Style>
Second, write your own template. If you find that you need to do anything that the default template won't do for you, you'll be doing it this way. But beware, control behavior always turns out to be much more complicated than you would naively assume. These can be deep waters at times.
Here's some documentation about retemplating a TextBox or a subclass of a TextBox.
You'll need to fill in a lot more properties than this, but here's a start:
<Style
TargetType="{x:Type local:MyCustomControl}"
BasedOn="{StaticResource {x:Type TextBox}}"
>
<Setter Property="BorderThickness" Value="10"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Border
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
>
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I override default styles of some controls in code. After this i want to disable all custom styles for all children(deep recursion) of some control. For example xaml:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Background" Value="Red"/>
</Style>
</StackPanel.Resources>
<Button>red style here is ok</Button>
<TextBlock> also ok</TextBlock>
<StackPanel>
<StackPanel.Resources>
<!-- magic command to disable ALL custom styles, for all controls like
<Style TargetType = "FrameworkElement"/> -->
</StackPanel.Resources>
<Button> no style plz </Button>
<TextBlock> bad style-_- </TextBlock>
</StackPanel>
</StackPanel>
I know that i can use style=null, but its bad solution for me, because i need to apply this trick for every type of controls. How can i solve my problem?
You could inject a blank Style that would take precedence over your other Style. Like:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Background" Value="Red"/>
</Style>
</StackPanel.Resources>
<Button>red style here is ok</Button>
<TextBlock> also ok</TextBlock>
<StackPanel>
<StackPanel.Resources>
<!-- magic command to disable ALL custom styles, for all controls -->
<Style TargetType="Button" />
<Style TargetType="TextBlock" />
</StackPanel.Resources>
<Button> no style plz </Button>
<TextBlock> bad style-_- </TextBlock>
</StackPanel>
</StackPanel>
I have a StackPanel with a UserControl inside:
<StackPanel>
<local:MyUC/>
</StackPanel>
The UserControl contains various TextBoxes and other controls that have their styles set via a ResourceDictionary, to achieve a common look all over the application.
For the TextBoxes, the ResourceDictionary entry looks like this:
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="MaxWidth" Value="{Binding RelativeSource={RelativeSource AncestorType=Border},Path=ActualWidth}"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
</Style>
Now I would like to apply a default style in the parent control (the StackPanel) to all of the TextBoxes inside the UserControl, so I can bind the IsReadOnly-property of the TextBoxes to a property that is only available in the parents Model:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Model.State}" Value="ReadOnly">
<Setter Property="IsReadOnly" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<local:MyUC/>
</StackPanel>
Unfortunatly, the default style for the TargetType="{x:Type TextBox}" that is defined inside of the ResourceDictionary of the UserControl overrides the style that I'm trying to set in the parent StackPanel.
How can I achieve to extend the default style that is defined inside the StackPanel.Resources with the default style defined in the UserControls ResourceDictionary instead of overwriting it?
This is what I do and I never have a problem:
In ResourceDictionary:
<Style x:Key="TextBoxBase" TargetType="{x:Type TextBox}">
<!-- Default styles go here -->
</Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxBase}"/>
This ensures that unless otherwise specified, all TextBoxes will have the style TextBoxBase and nothing more/less.
When defining styles that must have the above properties and then some, you define the style (in your case, this style is part of the StackPanel) like this:
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource TextBoxBase}">
<Style.Triggers>
<!-- These triggers should be in ADDITION to triggers defined in TextBoxBase.-->
<DataTrigger Binding="{Binding Model.State}" Value="ReadOnly">
<Setter Property="IsReadOnly" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
Another issue could be the fact that your defining a style for TextBoxes contained in a control. If the TextBoxes you are targeting the style with are unique the control they are contained in, why not define them in the control itself? To do that, you simply bind a reference to your ViewModel on the control, then use that reference IN the control to apply the DataTrigger to each TextBox using the method above.
On the current window I have a Grid with multiple controls (labels, textboxes, buttons).
General styles are set in App.xaml. Extensions for each control are set in Grid resources.
Each control visibility is determined by viewmodel property value. Not to bind each controls visibility to it (it uses custom converter and this will cause lots duplications) I wish to have "MyVisible1" style.
The problem is if I apply this style it overlaps other properties. What value should I use in "BasedOn"? Or what else can I do to implement it?
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Control}" x:Key="MyVisible1">
<Setter Property="Visibility" Value="{Binding ...}" />
</Style>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Width" Value="80" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="45" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
</Grid.Resources>
<TextBox Grid.Column="0" Grid.Row="0" Style="{StaticResource MyVisible1}"/>
</Grid>
The only way that I can imagine that you can do this is to define a local implicit Style for this:
<Style TargetType="{x:Type Control}">
<Setter Property="Visibility" Value="{Binding ...}" />
</Style>
By not defining an x:Key, this Style will be implicitly applied to all controls that extend the Control class and by declaring it locally, it will only apply to those elements in the current focus scope. So defining it in a Grid.Resources section will implicitly apply it to all controls within that Grid. You are then free to apply whatever additional Styles that you want to these controls.
I want to use a setter to set a default margin of all elements in my stackpanel, not just buttons but also textboxes and labels.
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="0,10,0,0"/>
</Style>
</StackPanel.Resources>
...
When I try to change above Button to Control or FrameworkElement (a derived type of each element) it doesn't work.
How can I fix this without having to specify 2 different Style elements with the same content but different x:Types on the TargetType?
You can do this with inheritance via Style's BasedOn attribute:
<StackPanel.Resources>
<Style x:Key="BaseStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Margin" Value="0,10,0,0"/>
</Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}" />
</StackPanel.Resources>