CellStyle based on RowStyle in WPF - c#

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:

Related

Complementing Application.Resources.Style with UserControl.Style for DataRow in DataGrid

I have a global style used in all the applications DataGrid's, defined in the Application.Resources:
<Application.Resources>
<ResourceDictionary>
<Style x:Key="StyleDataGridRow" TargetType="{x:Type DataGridRow}">
<Setter Property="VerticalAlignment" Value="Center" />
<Style.Triggers>
<Trigger Property="DataGrid.IsSelected" Value="True">
<Setter Property="DataGrid.BorderBrush" Value="{DynamicResource SecondaryHueMidBrush}" />
<Setter Property="DataGrid.BorderThickness" Value="1" />
<Setter Property="Background" Value="{DynamicResource PrimaryHueLightBrush}" />
</Trigger>
<Trigger Property="DataGrid.IsSelected" Value="False">
<Setter Property="DataGrid.BorderBrush" Value="{DynamicResource SecondaryHueMidBrush}" />
<Setter Property="DataGrid.BorderThickness" Value="1" />
</Trigger>
</Style.Triggers>
</Style>
</Application:Resources>
In my UserControl I would like to add a style - keeping the global style - to highlight a row depending on an item used in this DataGrid. I could define this style in the UserControl's resources:
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="priceMissing" TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=priceIsMissing}" Value="true">
<Setter Property="Background" Value="LightSalmon" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
The global style is applied to the UserControl's DataGrid like this:
<DataGrid x:Name="dgCalculatedServices"
Margin="20,0,0,0"
CellStyle="{StaticResource StyleDataGridCell}"
DataContext="{Binding}"
ItemsSource="{Binding calculationServiceCodes.collection}"
Style="{StaticResource StyleDataGrid}"
RowStyle="{StaticResource StyleDataGridRow}">
Does anyone know a way to use the global style on a DataGrid and complement it with the local style?
That is what the BasedOn property is for.
Styles can be based on other styles through this property. When you use this property, the new style will inherit the values of the original style that are not explicitly redefined in the new style.
Specify the StyleDataGridRow style as the base style for priceMissing.
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="priceMissing" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource StyleDataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=priceIsMissing}" Value="true">
<Setter Property="Background" Value="LightSalmon" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
Then use the priceMissing style in your DataGrid.
<DataGrid x:Name="dgCalculatedServices
...
RowStyle="{StaticResource priceMissing}">
You could of course rename the priceMissing style to StyleDataGridRow instead. The style would then be overwritten in the scope of the UserControl.

How to extend a style inherited from a parent container in an UserControl?

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.

Datagrid Column value bind?

I need to accept only specific digits into a column in datagrid how can i handle it?
<DataGridTextColumn Binding="{Binding Path=SellingPrice, UpdateSourceTrigger=PropertyChanged}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}">
<Setter Property="MaxLength" Value="10"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
in above code i have given Value="10", I need to bind the value from ViewModel how can i do it
I'm not able to test this at the moment, but you may have to do this in your style:
<Style TargetType="{x:Type TextBox}">
<Setter Property="MaxLength" Value="{Binding Path=DataContext.MyMaxLength"/>
</Style>
Where MyMaxLength is a property on your bound object.

row-specific context menu for WPF DataGrid vs existing RowStyle

I am trying to add a context menu to my WPF datagrid is specific to each row, because its items need to depend on the DataContext for that row. For example "Save" might be disabled if I know the row has not been edited.
I am looking at the accepted answer for Create contextmenus for datagrid rows and trying to adapt it to the existing xaml I am working with, but I don't know how to use this solution on top of my existing RowStyle.
If I copy and paste the context menu code everything works, but I already have this for RowStyle:
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#eed3f7" />
</Trigger>
[...]
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
And I don't understand how to incorporate:
<DataGrid RowStyle="{StaticResource DefaultRowStyle}"/>
Please help!
I'm guessing that you've been looking at this for so long that you can't see the wood for the trees. In your linked answer, there is this Style:
<Style x:Key="DefaultRowStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
</Style>
You say that you can't use it because you have your own Style that you want to apply... but the Style above has only one Setter and so there is nothing stopping you from copying that one Setter into your Style:
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu" Value="{StaticResource RowMenu}" /><!--<<<<<<<<-->
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#eed3f7" />
</Trigger>
[...]
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Alternatively, you could have based your Style on the DefaultRowStyle:
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="DefaultRowStyle">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#eed3f7" />
</Trigger>
[...]
</Style.Triggers>
</Style>
</DataGrid.RowStyle>

How to make style to extend but not override other styles

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.

Categories