WPF Grid - How to apply a style for just one column? - wpf

I have a WPF Grid with many rows and columns, all containing things like TextBlocks and TextBoxes.
For this specific situation I want all the stuff in column 1 to have padding, and all the stuff in column 2 to be aligned right. It seems to be very non-WPF to have to set those properties on each item in the grid.
I know I can create a style for all TextBlocks within a grid by doing something like this:
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</Grid.Resources>
</Grid>
But is there a way to apply that style to only the controls in say, column 2?
Should I be using a different control?

Here's what I usually do:
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Style.Triggers>
<Trigger Property="Grid.Column" Value="0">
<Setter Property="Margin" Value="0,0,2,0" />
</Trigger>
<Trigger Property="Grid.Column" Value="2">
<Setter Property="Margin" Value="20,0,2,0" />
</Trigger>
</Style.Triggers>
</Style>

You can define some styles like below and assign them to your Column.ElementStyle property:
<Window.Resources>
<Style x:Key="elementStyle" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="2,0,2,0" />
</Style>
<Style x:Key="rightElementStyle" BasedOn="{StaticResource elementStyle}" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style x:Key="centerElementStyle" BasedOn="{StaticResource elementStyle}" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</Window.Resources>
<dg:DataGrid AutoGenerateColumns="False">
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Binding={Binding Path=Name}
Header="Name"
ElementStyle="{StaticResource centerElementStyle}"/>
<dg:DataGridTextColumn Binding={Binding Path=Amount}
Header="Amount"
ElementStyle="{StaticResource rightElementStyle}"/>
</dg:DataGrid.Columns>
</dg:DataGrid>

Related

Change colour of single cell in DataGrid when selected, overriding current colour

I have this code which affects the whole row of a DataGrid:
<DataGrid x:Name="tblopenRequests" ItemsSource="{Binding}" Grid.Column="1" Grid.Row="2"
IsReadOnly="true" RowHeaderWidth="0" AutoGenerateColumns="False" CanUserAddRows="False" SelectionMode="Single">
<DataGrid.Resources>
<Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" TargetType="{x:Type DataGridColumnHeader}" >
<Setter Property="Background" Value="LightSeaGreen" />
<Setter Property="Foreground" Value="white" />
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1 1 1 1"/>
<Setter Property="Margin" Value="-1,-1,0,0" />
<Setter Property="Height" Value="28" />
<Setter Property="Width" Value="auto"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalContentAlignment" Value="Right" />
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="Background" Value="Goldenrod" />
<Setter Property="BorderBrush" Value="white" />
<Setter Property="Foreground" Value="black" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="15"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
I now want to make ONE cell in the row be overridden with a different format to stand out more.
This is all I have at the moment but I keep failing in my attempts.
<DataGridTextColumn Binding="{Binding expectedDate}" Header="Expected Date" Width="90">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
I have tried adding atrigger in here and colouring the cell only. However, this didn't do anything, just kept the orange from the above code in the resources section.
I just need this one cell pink/light red instead of orange but I can't find a site or forum article with this answer.
Thanks to #thatguy. The upper apporach helped me a lot. It's worth to mention that this is one of many answers on stackoverflow and the web which works with AutoGenerateColumns="True".
Conditional colors can also be done in code behind with:
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding Column.Header, RelativeSource={RelativeSource Self}}" Value="Expected Date">
<Setter Property="Background" Value="{Binding SomeColor}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
And for example:
public SolidColorBrush SomeColor
{
get
{
return IsDifferent ? new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 255, 139, 0)) : new SolidColorBrush(System.Windows.Media.Color.FromArgb(139, 255, 139, 0));
}
}
Single Cell Style With Bindings
In your cell style, you can refer to the associated column and identify it e.g. by comparing its Header.
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<!-- ...other setters. -->
<Style.Triggers>
<!-- ...other triggers. -->
<DataTrigger Binding="{Binding Column.Header, RelativeSource={RelativeSource Self}}" Value="Expected Date">
<Setter Property="Background" Value="Pink" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
Depending on the state in which the color should be changed, you might need to use a MultiDataTrigger for example to change the color only in selected state.
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True" />
<Condition Binding="{Binding Column.Header, RelativeSource={RelativeSource Self}}" Value="Expected Date" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Pink" />
</MultiDataTrigger>
Using the Header to identify a column is cumbersome and error prone. Unfortunately there is no built-in unique identifier on columns that you can use. However, you can create an attached property.
public static class DataGridColumnProperties
{
public static readonly DependencyProperty IdentifierProperty = DependencyProperty.RegisterAttached("Identifier",
typeof(string), typeof(DataGridColumnProperties), new PropertyMetadata(null));
public static string GetIdentifier(DependencyObject dependencyObject)
{
return (string)dependencyObject.GetValue(IdentifierProperty);
}
public static void SetIdentifier(DependencyObject dependencyObject, string value)
{
dependencyObject.SetValue(IdentifierProperty, value);
}
}
Using this attached property, you can set an identifier on your column.
<DataGridTextColumn local:DataGridColumnProperties.Identifier="ExpectedDate" ...>
You would refer to it in the style like this:
<DataTrigger Binding="{Binding Column.(local:DataGridColumnProperties.Identifier), RelativeSource={RelativeSource Self}}" Value="ExpectedDate">
<Setter Property="Background" Value="Pink" />
</DataTrigger>
This approach keeps your column definitions more robust, as styles are independent of the Header.
Multiple Cell Styles
If you prefer to use multiple cell styles, you can solve your issue without bindings. In the following, I have created all styles within the DataGrid, but you can move them to any resource dictionary.
Define a regular cell style that applies to most columns, here RegularCellStyle. This is the style from your question. Then, create a special style for the first column, here SpecialCellStyle.
This style uses the BasedOn attribute to inherit all setters and triggers from the regular style. In this style you only define what changes compared to the base style. In your case it is only the Background in selected state, so we add a trigger for it.
Next, we apply the regular style to the DataGrids CellStyle property. This style will be applied to all columns. Then we apply the special style to the CellStyle of the first column. The column cell style will take precedence. From the documentation:
A Style can be applied to a cell at the table, column, or cell level. To apply a Style to all cells in a column, set the DataGridColumn.CellStyle property. This will take precedence over the DataGrid.CellStyle property. To apply a Style to an individual cell, set the Style property directly on the DataGridCell. This will take precedence over all other styles applied to the cell.
Below is the complete code for your sample.
<DataGrid x:Name="tblopenRequests" ItemsSource="{Binding}" Grid.Column="1" Grid.Row="2"
IsReadOnly="true" RowHeaderWidth="0" AutoGenerateColumns="False" CanUserAddRows="False" SelectionMode="Single">
<DataGrid.Resources>
<!-- Style from your question. -->
<Style x:Key="RegularCellStyle" TargetType="DataGridCell">
<Setter Property="HorizontalContentAlignment" Value="Right" />
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="Background" Value="Goldenrod" />
<Setter Property="BorderBrush" Value="white" />
<Setter Property="Foreground" Value="black" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="15" />
</Trigger>
</Style.Triggers>
</Style>
<!-- Special first column style based on the regular style. -->
<Style x:Key="SpecialCellStyle"
BasedOn="{StaticResource RegularCellStyle}"
TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="Background" Value="Pink" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<StaticResource ResourceKey="RegularCellStyle" />
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="LightSeaGreen" />
<Setter Property="Foreground" Value="white" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1,1,1,1" />
<Setter Property="Margin" Value="-1,-1,0,0" />
<Setter Property="Height" Value="28" />
<Setter Property="Width" Value="auto" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding expectedDate}"
Header="Expected Date"
Width="90"
CellStyle="{StaticResource SpecialCellStyle}"/>
<!-- ...other columns WITHOUT cell style will apply the regular style. -->
</DataGrid.Columns>
</DataGrid>

RadioButton style property Padding not being applied

[Using .NET Framework 4.5.1]
I have the following resource set in one of my WPF Windows:
<Window.Resources>
<Style TargetType="{x:Type FrameworkElement}" x:Key="baseStyle">
<Setter Property="Margin" Value="5"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type FrameworkElement}" x:Key="basePlusEnabled" BasedOn="{StaticResource baseStyle}">
<Setter Property="IsEnabled" Value="{Binding TestIsRunning, Mode=OneWay}"/>
</Style>
<Style TargetType="RadioButton" BasedOn="{StaticResource basePlusEnabled}">
<Setter Property="Padding" Value="4,-5,0,0"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Foreground" Value="{Binding TestIsRunning,
Converter={StaticResource testIsRunningForegroundConverter}, Mode=OneWay}"/>
</Style>
</Window.Resources>
However, the Padding style is not being applied to the RadioButtons in the window. (The other properties in the style are getting applied.) If I explicitly specify the Padding in each RadioButton, then it works. What am I missing here?
EDIT:
Sample RadioButton instance:
<RadioButton Grid.Column="4" Content="Iowa" GroupName="Facility"
IsChecked="{Binding IowaFacilityChecked1, UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"/>
change TargetType="{x:Type FrameworkElement}" to TargetType="{x:Type RadioButton}"

Default style for TextBlock not being picked up after applying a style key

I have a <ResourceDictionary> containing this:
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="..\..\Fonts\#Roboto"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
This works fine.
Now I've added another style:
<Style x:Key="MyText" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
However, after changing my <TextBlock> to <TextBlock Style="{StaticResource MyText}"/> - only the styles within the 2nd style block are picked up. E.g. the FontFamily, FontSize and TextWrapping are now ignored.
How can I have a default for a TextBlock, and then add to it? I don't want to add an x:Key to the 'default' style as this is in use throughout the system already.
I think you just need to base your keyed style on the type. See example below.
Note the BasedOn="{StaticResource {x:Type TextBlock}}"
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="Green" />
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="MyAppendedStyles" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="Hello" />
<TextBlock Style="{StaticResource MyText}" Text="Style Key" />
<TextBlock Style="{StaticResource MyAppendedStyles}" Text="Style Key" />
</StackPanel>

WPF How to change DataGrid corners and how to remove empty field in dataGrid

I have few problem with DataGrid style.
I need it change two corners, when I try to do that by example my data disappear, its possible do that with my code?
My code is:
...<Style TargetType="{x:Type DataGrid}">
<Setter Property="Background" Value="#FFFFFF"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="#E6E6E6"/>
</Style>
<DataGridTextColumn x:Key="price" Header="Price, € " FontFamily="Arial" Width="0.3*" Binding="{Binding adress}" IsReadOnly="True"
FontSize="18" FontWeight="Normal" Foreground="#4D4D4D">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Width" Value="25"/>
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="#FFFFFF"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="#E6E6E6"/>
<Setter Property="Height" Value="35"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.HeaderStyle>
</DataGridTextColumn>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Style.Triggers>
<Trigger Property="DataGrid.IsSelected" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FFD65E" />
<Grid>
<DataGrid x:Name="lbPersonList" Margin="30,98,362,30" AlternationCount="2" VerticalScrollBarVisibility="Visible" AutoGenerateColumns="False"
RowHeight="42" GridLinesVisibility="None" HorizontalGridLinesBrush="#E6E6E6" CanUserAddRows="False"
HeadersVisibility="Column" >
<DataGrid.Columns>
<StaticResource ResourceKey="product"/>
<StaticResource ResourceKey="unit price"/>
<StaticResource ResourceKey="quantity"/>
<StaticResource ResourceKey="price"/>
</DataGrid.Columns>
</DataGrid>
2.
How to remove this empty field, I removed last row so this empty field is not row ?
You have to edit the default ControlTemplate of DataGrid by adding <Border> to the root grid control
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border x:Name="Border" CornerRadius="13" Background="#232323" SnapsToDevicePixels="True"/>
<Grid>
........
.......
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
you can find the default ControlTemplate of DataGrid at
https://msdn.microsoft.com/en-us/library/cc278066(v=vs.95).aspx

Apply style to multiple controls when a style trigger fires in xaml

When the user enters info I want all the controls to look and act the same way, as a result I have done the following.
The label and textbox controls are in a stackpanel as:
<StackPanel Style="{StaticResource ResourceKey=myInput}" HorizontalAlignment="Left">
<Label Content="Label" Name="label1" />
<TextBox Name="textBox1" ></TextBox>
</StackPanel>
the style "myInput" is:
<Style x:Key="myInput" TargetType="{x:Type StackPanel}">
<Style.Resources>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="FontSize" Value="12" />
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="5,-5,2,2"></Setter>
<Setter Property="Height" Value="23"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="Blue" >
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
Now every time that I apply that style to a stackpanel that has a label and a textbox the background of the textbox changes to blue when it receives focus.
How could I set the label's fontweight to bold also when that event fires? I will like to do this with xaml.
Use a DataTrigger on your Label which looks to see if the keyboard focus is inside of the parent StackPanel that contains both objects
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="FontSize" Value="12" />
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsKeyboardFocusWithin,
RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>

Resources