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.
Related
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>
I have a WPF DataGrid represented in XAML. I'm using a RowStyle for my grid's TableView but also need to set some properties for specific cells. I need those cells to have the properties of the row style and apply the extra properties from the cell style on top of those.
What I would need is something like this, although this doesn't work as it's saying:
Target type 'CellContentPresenter' is not convertible to base type 'GridRowContent'
<Style x:Key="MyGridRowStyle"
BasedOn="{StaticResource {themes:GridRowThemeKey ResourceKey=RowStyle}}"
TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Height"
Value="25" />
<Style.Triggers>
...
</Style.Triggers>
</Style>
<Style x:Key="MyCellStyle"
BasedOn="{StaticResource MyGridRowStyle}"
TargetType="{x:Type dxg:CellContentPresenter}">
<Style.Triggers>
...
</Style.Triggers>
</Style>
I've also tried not specifying the BasedOn property for MyCellStyle but that doesn't work either.
I use the MyCellStyle like this:
<dxg:GridColumn Header="My Header"
FieldName="MyFieldName"
Width="100"
CellStyle="{StaticResource MyCellStyle}" />
and MyGridRowStyle like this on the TableView:
RowStyle="{StaticResource MyGridRowStyle}"
How can I make the cell style only change the properties specified in MyCellStyle and use the values specified in MyGridRowStyle for the other properties?
You can't base a CellContentPresenter style on a GridRowContent style. These are two completely different types and just because they may happen to have some properties that have the same name these are still completely different and independant properties with no relationship to each other.
The best thing you can do is to define the common values as separate resources and use these resources in both styles, e.g.:
<Window.Resources>
<s:Double x:Key="commonHeight">25</s:Double>
<SolidColorBrush x:Key="commonBg">Red</SolidColorBrush>
<Style x:Key="MyCellStyle" TargetType="{x:Type dxg:CellContentPresenter}">
<Setter Property="Height" Value="{StaticResource commonHeight}" />
<Setter Property="Background" Value="{StaticResource commonBg}" />
</Style>
<Style x:Key="MyGridRowStyle" BasedOn="{StaticResource {themes:GridRowThemeKey ResourceKey=RowStyle}}" TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Height" Value="{StaticResource commonHeight}" />
<Setter Property="Background" Value="{StaticResource commonBg}" />
</Style>
</Window.Resources>
But you still need to define all setters in both styles.
based on normal WPF DataGrid you could try this and expand it for dxg
the class DataGridCell is derived from ContentControl (that is a child from Content).
the class DataGridRow is derived from Control.
now you could try the following:
<Style x:Key="BaseStyle" TargetType="Control" >
<!-- Maybe add BaseStyle / theme here with BasedOn -->
<Setter Property="Height" Value="25" />
<!-- Row and Column defaults -->
</Style>
<Style x:Key="MyGridRowStyle" BasedOn="{StaticResource BaseStyle}"
TargetType="DataGridRow">
<!-- Row specific implementation -->
</Style>
<Style x:Key="MyCellStyle" BasedOn="{StaticResource BaseStyle}"
TargetType="DataGridCell">
<!-- Column specific implementation -->
</Style>
summary:
use base type of both Row and Column classes for your BaseStyle and use this one as BasedOn. For dxg you can extend it by your own...
My understanding of the question: values of the cell style should change based off of values in the style of the row it is in.
Trigger off of value
Here is a simple working example (test it by changing the DataGridRow Background to Red, you will notice that the cell's foreground changes to Blue):
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="White"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow, Mode=FindAncestor}, Path=Background}" Value="Red">
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
You can do a similar binding to directly set properties of the CellStyle to the value of properties of the row it is in.
Relative bind directly to a property
<DataGrid x:Name="dataGrid1" ItemsSource="{Binding Collection}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Height" Value="20"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow, Mode=FindAncestor}, Path=Height}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
Explanation
RelativeBinding works due to the fact that DataGridCells are eventual children of DataGridRows as can been seen in this screenshot of the visual tree of a DataGrid:
I create an UWP App and define some styles like this:
<Style TargetType="TextBlock" >
<Setter Property="Foreground" Value="Orange" />
<Setter Property="Margin" Value="12" />
<Setter Property="FontSize" Value="18" />
So, all my TextBlocks are orange and have a margin of 12px. All fine. But now I want to define a second style for Headlines, which should be inherit the base style and just override the extra defined properties, like this:
<Style x:Key="HeadlineStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="32" />
But if I do it this way, all other style definitions are gone (no margin, no coloring).
So how could I keep the base style?
In WPF I can use the x:Type attribute and just say
BasedOn="{StaticResource {x:Type Button}}"
But x:Type are not available in UWP (and what I found it is no longer supported)
This does exactly what you want:
<Grid.Resources>
<Style TargetType="TextBlock" x:Key="medium">
<Setter Property="Foreground" Value="Orange"/>
<Setter Property="FontSize" Value="20"/>
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource medium}">
<Setter Property="FontSize" Value="10"/>
</Style>
<Style TargetType="TextBlock" x:Key="bigger" BasedOn="{StaticResource medium}">
<Setter Property="FontSize" Value="30"/>
</Style>
</Grid.Resources>
<StackPanel>
<TextBlock Text="normal"/>
<TextBlock Text="medium" Style="{StaticResource medium}"/>
<TextBlock Text="bigger" Style="{StaticResource bigger}"/>
</StackPanel>
The first TextBlock is 10-px orange
The second TextBlock is 20-px orange
The third TextBlock is 30-px orange
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.
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>