I'm wondering, is there any way to set custom DataGrid
selection color, when DataGrid
or window, containing DataGrid
, becomes inactive?
E.g., here's the DataGrid
and ListBox
, displaying the same data. Both controls has one selected item. Initially, DataGrid
has input focus:
Everything looks OK - selected item in ListBox
is grayed. Then, let's move focus to the ListBox
:
Now behavior of DataGrid
is incorrect - selection color hasn't changed.
I know about SystemColors.HighlightBrushKey
and SystemColors.ControlBrushKey
. This XAML was placed in window's resources:
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="BlueViolet"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="DarkGray"/>
But looks like DataGrid
ignores the second one - SystemColors.ControlBrushKey
, and I want DataGrid
to behave like any other controls (ListBox
, ComboBox
, ListView
).
Something similar I can achieve with triggers:
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="DarkGray"/>
</MultiTrigger>
</Style.Triggers>
</Style>
But this solution is incomplete. First of all, it grays selected, but unfocused cell, even grid selection unit is FullRow
. The second thing - trigger doesn't fires when application window loses its focus.
Any suggestions?
UPDATE.
This bug was fixed in .NET 4.5, so, it isn't actual anymore.
I've found the solution, but it doesn't looks elegant.
Basic problems are:
DataGrid.IsFocused
is permanently false
, because focus has
concrete cell, not a grid itself.IsFocused
for the current cell.The only way to determine, if data grid has a focus, is a checking of DataGrid.CurrentCell
property. Unfortunately, it is a struct, and you can't make a trigger, that checks this property for {x:Null}
.
To solve these problems, I need two attached properties.
First of them is intended to determine, is there any focused cell in grid. The result must be bool
, the source is DataGridCellInfo
, so, first of all, the converter must be written:
[ValueConversion(typeof(DataGridCellInfo), typeof(bool))]
public sealed class DataGridCellInfoToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || value.GetType() != typeof(DataGridCellInfo) || targetType != typeof(bool))
return DependencyProperty.UnsetValue;
// IsValid will be false, if there's no focused cell.
return ((DataGridCellInfo)value).IsValid;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
The attached property:
public static bool GetHasFocusedCell(DependencyObject obj)
{
return (bool)obj.GetValue(HasFocusedCellProperty);
}
public static void SetHasFocusedCell(DependencyObject obj, bool value)
{
obj.SetValue(HasFocusedCellProperty, value);
}
public static readonly DependencyProperty HasFocusedCellProperty = DependencyProperty.RegisterAttached(
"HasFocusedCell",
typeof(bool),
typeof(FocusedCellBehavior),
new UIPropertyMetadata(false));
The second attached property must be changed, when parent window of grid becomes inactve:
public static bool GetIsParentWindowActive(DependencyObject obj)
{
return (bool)obj.GetValue(IsParentWindowActiveProperty);
}
public static void SetIsParentWindowActive(DependencyObject obj, bool value)
{
obj.SetValue(IsParentWindowActiveProperty, value);
}
public static readonly DependencyProperty IsParentWindowActiveProperty = DependencyProperty.RegisterAttached(
"IsParentWindowActive",
typeof(bool),
typeof(FocusedCellBehavior),
new UIPropertyMetadata(false));
Now, let's bind attached properties in XAML:
<!-- A converter to define, is there any focused cell in DataGrid -->
<local:DataGridCellInfoToBooleanConverter x:Key="DataGridCellInfoToBooleanConverter"/>
<DataGrid Grid.Row="0" SelectionUnit="FullRow" SelectionMode="Single"
ItemsSource="{Binding}"
local:FocusedCellBehavior.HasFocusedCell="{Binding CurrentCell, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource DataGridCellInfoToBooleanConverter}}"
local:FocusedCellBehavior.IsParentWindowActive="{Binding IsActive, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
Next, I need a cell style to set appropriate background color:
<!-- A style of selected cell in DataGrid, when there's no any focused cells in DataGrid -->
<Style TargetType="{x:Type DataGridCell}" x:Key="InactiveSelectedCellStyle">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
</Style.Triggers>
</Style>
and a grid style to fire triggers, when attached properties will change their values:
<!--
A style of DataGrid, that defines a couple of triggers, which being fired
when helper attached properties will change their values
-->
<Style TargetType="{x:Type DataGrid}">
<Style.Triggers>
<Trigger Property="local:FocusedCellBehavior.IsParentWindowActive" Value="False">
<Setter Property="CellStyle" Value="{StaticResource InactiveSelectedCellStyle}"/>
</Trigger>
<Trigger Property="local:FocusedCellBehavior.HasFocusedCell" Value="False">
<Setter Property="CellStyle" Value="{StaticResource InactiveSelectedCellStyle}"/>
</Trigger>
</Style.Triggers>
</Style>
Are there any better solutions?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With