I have a DataGrid that styles the background of rows based on if the row is listed as "to delete".
The rows change colour when loading into the table and when scrolling around, but do not immediately change.
I use "ctrl+del" to mark the selected rows for deletion.
How can I get the row to update on this keypress?
DataGrid Implementation
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="WhiteSmoke" />
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightGray" />
</Trigger>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding>
<Binding.Converter>
<local:IsDeletedConverter/>
</Binding.Converter>
</Binding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Background" Value="OrangeRed"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
<Setter Property="Background">
<Setter.Value>
<Binding Path="">
<Binding.Converter>
<local:ValueToBrushConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
Converter
public class IsDeletedConverter : IValueConverter
{
public object Convert(object values, Type targetType, object parameter, CultureInfo culture)
{
if (values is ExpandoObject eo)
{
if ((Application.Current.MainWindow as MainWindow).LoadedTable.ToDelete.Contains((int)ExpandoUtils.GetExpandoProperty(eo, "id")))
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
i want to change style when toggle button checked
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource ToggleButtonPrimary}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ButtonNude, Path=IsChecked}" Value="True">
<Setter Property="Style" Value="{StaticResource ToggleButtonDanger}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
but my code not work and app crash
As suggested by #clemens , the right way would be to have template and apply in controlTemplate with TargetName
There is one more (may be ugly) way:
On view:
<StackPanel>
<StackPanel.Resources>
<local:Myconverter x:Key="MyConverter" />
<Style TargetType="ToggleButton" x:Key="ToggleButtonPrimary">
<Setter Property="Background" Value="blue" />
</Style>
<Style TargetType="ToggleButton" x:Key="ToggleButtonDanger">
<Setter Property="Background" Value="Red" />
</Style>
</StackPanel.Resources>
<CheckBox Margin="20" x:Name="chk" />
<ToggleButton Width="100" Height="100" >
<ToggleButton.Style>
<MultiBinding Converter="{StaticResource MyConverter}">
<MultiBinding.Bindings>
<Binding ElementName="chk" Path="IsChecked"/>
<Binding RelativeSource="{RelativeSource AncestorType=StackPanel}"/>
</MultiBinding.Bindings>
</MultiBinding>
</ToggleButton.Style>
</ToggleButton>
</StackPanel>
Converter:
public class Myconverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)values[0])
{
return (values[1] as FrameworkElement).Resources["ToggleButtonDanger"];
}
else
return (values[1] as FrameworkElement).Resources["ToggleButtonPrimary"];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Hope that helps.
I find solution Setter.TargetName does not work against an element from the BasedOn setting.
But I do not understand how prement him to me.
I have a class:
public class MyCheckBox: CheckBox
{
public bool DefaultValue
{
get { return (bool)GetValue(DefaultValueProperty); }
set { SetValue(DefaultValueProperty, value); }
}
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.Register("DefaultValue", typeof(bool), typeof(MyCheckBox), new UIPropertyMetadata(false));
protected override void OnToggle()
{
this.IsChecked = this.IsChecked == null ? !DefaultValue : this.IsChecked.Value ? false : true;
}
}
and XAML
<Style TargetType="{x:Type CheckBox}">
<Style.Resources>
<Imaging:BitmapImage x:Key="CheckBoxStatusSource" UriSource="FalseIfNull.png"/> <!-- icon if [IsChecked] is [null] -->
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image x:Name="CheckBoxStatus" Source="{DynamicResource CheckBoxStatusSource}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="b.png"/><!-- icon if [IsChecked] is [true] -->
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="c.png"/><!-- icon if [IsChecked] is [false] -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type My:MyCheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
<Style.Triggers>
<Trigger Property="DefaultValue" Value="true">
<!-- !!!!!!!!!!!!!!This need change [UriSource] in [CheckBoxStatusSource] to "TrueIfNull.png"!!!!!!!!!!!!!! -->
</Trigger>
</Style.Triggers>
</Style>
Perhaps there is another solution
Unless you need to extend CheckBox for other reasons, what you're trying to do here can be done with Attached Properties too, and it would allow you to have one single Style for all your CheckBoxes.
public static class CheckBoxExtensions
{
public static void SetDefaultValue(CheckBox element, bool value)
{
element.SetValue(DefaultValueProperty, value);
}
public static bool GetDefaultValue(CheckBox element)
{
return (bool)element.GetValue(DefaultValueProperty);
}
// Using a DependencyProperty as the backing store for DefaultValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue", typeof(bool), typeof(CheckBoxExtensions), new UIPropertyMetadata(false));
}
This property is now available for you to use in any CheckBox in your solution, and in any Style and Template you use for your CheckBoxes. In this case...
<Style TargetType="{x:Type CheckBox}">
<Style.Resources>
<Imaging:BitmapImage x:Key="CheckBoxStatusSourceFalse" UriSource="FalseIfNull.png"/> <!-- icon if [IsChecked] is [null] and [DefaultValue] is [false] -->
<Imaging:BitmapImage x:Key="CheckBoxStatusSourceTrue" UriSource="TrueIfNull.png"/> <!-- icon if [IsChecked] is [null] and [DefaultValue] is [true] -->
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image x:Name="CheckBoxStatus" Source="{DynamicResource CheckBoxStatusSourceFalse}"/>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="local:CheckBoxExtensions.DefaultValue" Value="True" />
<Condition Property="IsChecked" Value="{x:Null}" />
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxStatus" Property="Source" Value="{DynamicResource CheckBoxStatusSourceFalse}"/>
</MultiTrigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="b.png"/><!-- icon if [IsChecked] is [true] -->
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="c.png"/><!-- icon if [IsChecked] is [false] -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT: Even if you still need to extend CheckBox, this Style would work for both regular CheckBox and your MyCheckBox implementation. Just create another Style that's based on this one.
<Style TargetType="{x:Type My:MyCheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
...
</Style>
And if you want to access the DefaultValue property from code-behind, you can do it this way:
CheckBoxExtensions.SetDefaultValue(checkBox, true);
bool defaultValue = CheckBoxExtensions.GetDefaultValue(checkBox);
If any possible to give condition on DataTrigger?
<DataTrigger Binding="{Binding MessageBoxImage}" Value="{x:Static MessageBoxImage.Error}">
<Setter Property="Source" Value="../Images/Error48.png"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MessageBoxImage}" Value="{x:Static MessageBoxImage.Hand}">
<Setter Property="Source" Value="../Images/Error48.png"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MessageBoxImage}" Value="{x:Static MessageBoxImage.Stop}">
<Setter Property="Source" Value="../Images/Error48.png"></Setter>
</DataTrigger>
So, this is my Xaml code, in that Error,Hand,Stop all are setting same image
My question is any possible to give OR condition for these three values? (or one line statement)
Thanks,
You can use MultiDataTrigger for AND condition. As for OR condition you can use converter.
<Window.Resources>
<someNs:ORConverter x:Key = "ORConverter"/>
</Window.Resources>
....
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Path="MessageBoxImage" Converter="{StaticResource ORConverter}">
<Binding.ConverterParameter>
<x:Array Type="MessageBoxImage">
<x:Static MemberType="MessageBoxImage" Member="Error" />
<x:Static MemberType="MessageBoxImage" Member="Information" />
<x:Static MemberType="MessageBoxImage" Member="Question" />
</x:Array>
</Binding.ConverterParameter>
</Binding>
</DataTrigger.Binding>
<Setter Property="Source" Value="../Images/Error48.png"></Setter>
</DataTrigger>
And the converter's code:
public class ORConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
var list = parameter as IList;
if (list == null)
return false;
foreach (var o in list)
{
if (Equals(o, value))
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
With the simple DataTrigger is meant to check for a single values. if Possible you could use the Multi-DataTrigger to check the multiple conditions.
<Style TargetType="{x:Type dxg:GroupColumnSummaryControl}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<Binding Converter="{StaticResource c}"/>
</Setter.Value>
</Setter>
</Trigger>.
Whats wrong with this code here. my convertor doesnt seem to fire when i do a mouse over.
If i remove the convertor and assign the value as Red i can see the color.
this works perfectly fine
<Style TargetType="{x:Type dxg:GroupColumnSummaryControl}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red">
</Setter>
</Trigger>
This works for me: (I used RichTextBox)
<Window.Resources>
<my:ColorConverter x:Key="colorConverter" />
<Style TargetType="RichTextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<Binding Converter="{StaticResource colorConverter}"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
This is the converter:
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return new SolidColorBrush(Colors.Red);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
}
How does one style a specific row or rows in a WPF Datagrid during runtime? Each change depends on some values of the shown data?
I can't tell from your question whether you are adding columns to your grid at run time, but either way you can add a CellStyle to the grid at design time that handles your specific styling needs using DataTriggers.
For instance, the following would make all rows red where the Name property = "Billy Bob":
<DataGrid AutoGenerateColumns="True" Name="dataGrid1">
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding Name}" Value="Billy Bob" >
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
If you are adding columns programmatically at run time and you want to apply a certain style to them, you can still define those styles at design time in your xaml.
<DataGrid AutoGenerateColumns="False" Name="dataGrid1">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="MyCellStyle">
<Setter Property="Foreground" Value="Green"/>
</Style>
</DataGrid.Resources>
...
Then when you are adding the columns you can apply that style to them:
col.CellStyle = (Style)dataGrid1.Resources("MyCellStyle");
Update
If you have a list of songs and you want to change the row color of every song that has an artist whose name starts with an "a", then you could use an IValueConverter.
The following converter would do the trick:
public class ArtistNameConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
return value.ToString().StartsWith(parameter.ToString());
}
catch
{
return false;
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then you could use the converter in your xaml like so:
<DataGrid AutoGenerateColumns="True" Name="dataGrid1">
<DataGrid.Resources>
<converters:ArtistNameConverter x:Key="ArtistNameConverter"></converters:ArtistNameConverter>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding ArtistName, Converter={StaticResource ArtistNameConverter}, ConverterParameter=a}" Value="True" >
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
Notice how I am passing an "a" into the converter as the parameter. You can pass in whatever letter you want, and the rows that have artists that start with that letter will have their background color set to red.
Update 2
If you want to pass in a variable of some sort to the converter, you can use MultiBinding.
The Converter would look like this:
public class ArtistNameConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
try
{
return values[0].ToString().StartsWith(values[1].ToString());
}
catch
{
return false;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The first parameter passed in is the artist name, the second is the letter.
And you would use it in your grid like this:
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Value="True" >
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ArtistNameConverter}">
<Binding Path="ArtistName" />
<Binding Mode="OneWay" ElementName="FirstLetter" Path="Text" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
In this example, the first letter is coming from the "Text" property of a control called "FirstLetter". You can change that binding to whatever you want.