Converting RowStyle values Conditionally - c#

So I don't know if this is really possible, but it is worth a shot. I have two different DatagridRowStyles that I want to set in the DataGridBaseStyle Conditionally.
For example:
<Style x:Key="DataGridBaseStyle"
TargetType="sdk:DataGrid">
<Setter Property="CellStyle" Value="{StaticResource DataGridCellBaseStyle}" />
<Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGridColumnHeaderBaseStyle}" />
<Setter Property="RowHeaderStyle" Value="{StaticResource DataGridRowHeaderBaseStyle}" />
<Setter Property="RowStyle" Value="{StaticResource DataGridRowBaseStyle} />
...
The property setter value of RowStyle I want to use one of two Styles depending on what "Custom Theme" I am using.
So far I tried setting it this way, but it only defaults to the normal style.
<Setter Property="RowStyle">
<Setter.Value>
<Binding>
<Binding.Converter>
<conv:DataGridRowStyleConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
And the same with
<Setter Property="RowStyle" Value="{StaticResource DataGridRowBaseStyle} Converter={StaticResource DataGridRowStyleConverter}" />
Converter Coding
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (ColourScheme == "Dark")
return Application.Current.Resources["DataGridRowBaseDarkStyle"] as Style;
return Application.Current.Resources["DataGridRowBaseStyle"] as Style;
}
I am kind of thinking that my converter return values are incorrect, but I also did play around with those values with no luck.

Provided that ColorScheme is a public property in the current DataContext, the Setter should look like this:
<Setter Property="RowStyle"
Value="{Binding ColorScheme, Converter={StaticResource DataGridRowStyleConverter}}"/>
or like this in XML tag syntax:
<Setter Property="RowStyle">
<Setter.Value>
<Binding Path="ColorScheme"
Converter="{StaticResource DataGridRowStyleConverter}"/>
</Setter.Value>
</Setter>
The Converter would get the current value of the ColorScheme by the value argument of the Convert method:
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString == "Dark"
? Application.Current.Resources["DataGridRowBaseDarkStyle"] as Style
: Application.Current.Resources["DataGridRowBaseStyle"] as Style;
}

Related

InvalidCastException while binding with converter

I have a problem with a binding, I want to change an icon when the column has at least one element filtered. But it is not working.
This is my converter:
public class FilteredToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int index = ((DataGridColumnHeader)((MahApps.Metro.IconPacks.PackIconMaterial)value).TemplatedParent).Column.DisplayIndex;
return !((AdvancedDataGrid)parameter).FilterLists[index].Any(item => item.NotFiltered);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
A filter for each column is generated with a contentTemplate, and I have 2 problems, If I set this style in controlTemplate.Resources the dataGrid reference is not found.
If I set the style in the DataGrid.Resources then I get
InvalidCastException: Can not convert an object from type 'System.Windows.Controls.DataGrid' to type 'MahApps.Metro.IconPacks.PackIconMaterial'.
This is the style:
<Style TargetType="iconPacks:PackIconMaterial">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource FilteredToIcon}, ConverterParameter={x:Reference dataGrid}}" Value="True">
<Setter Property="Kind" Value="FilterMenu"/>
</DataTrigger>
</Style.Triggers>
</Style>
And this is a summary of the whole XAML of my custom AdvancedDataGrid:
<DataGrid x:Class="ZOT.GUI.Items.AdvancedDataGrid"
x:Name="dataGrid"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:ZOT.GUI.Items">
<DataGrid.Resources>
<local:FilteredToIconConverter x:Key="FilteredToIcon" />
<!-- The style can be here-->
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<ControlTemplate.Resources>
<!-- The style can be here as well, whatever that works-->
</ControlTemplate.Resources>
<Grid>
<ContentPresenter/>
<ToggleButton x:Name="Y">
<!-- This is the Icon I want to change -->
<iconPacks:PackIconMaterial Kind="Filter"/>
</ToggleButton>
<Popup x:Name="pop" Width="auto" IsOpen="{Binding IsChecked, ElementName=Y,Mode=TwoWay}">
<!-- .... -->
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
If you have any clue, tell me please.
Thank you.
If you want to bind to the PackIconMaterial itself, you should set the RelativeSource property of the binding to RelativeSource.Self:
<Style TargetType="iconPacks:PackIconMaterial">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource FilteredToIcon}, ConverterParameter={x:Reference dataGrid},
RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Kind" Value="FilterMenu"/>
</DataTrigger>
</Style.Triggers>
</Style>
That's the only way you will be able to cast value to MahApps.Metro.IconPacks.PackIconMaterial in the converter.
There are probably better ways of solving whatever you're trying to do though.

Datatrigger if string contains certain characters

im struggling with figuring out how to check if a string contains certain characters / words and make the datatriggers go off based on that.
In my example below I would like the datatrigger to go off when there is a color in the value, and what comes after doesnt matter. The first trigger, if value contains "RED" trigger should go off no matter if it says RED Apple ,RED car, RED little ball etc.
<DataTrigger Binding="{Binding Name}" Value="RED Apple" >
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Name}" Value="YELLOW Lemon" >
<Setter Property="Foreground" Value="Yellow" />
</DataTrigger>
<DataTrigger Binding="{Binding Name}" Value="GREEN Pear" >
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
How can I achieve this
Create a Converter
public class ColorConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return ((string)value.Contains("Color");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
Then use the following XAML.
<Window.Resources>
<myNamespace:ColorConverter x:Key="ColorConverter" Color="red" />
</Window.Resources>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Color,
Converter={StaticResource ColorConverter}}">
<DataTrigger.Value>true</DataTrigger.Value>
<Setter TargetName="Color" Property="Foreground" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>

How to bind a binary property to a property with multiple values?

I have a ToggleButton being a standard WPF class and I want to bind IsChecked to a property status of my model and the Status can have more than 2 values: Status1, Status2, Status3, Status4. The type of Status is SomeThirdPartyClassStatus and I don't have access to its source code.
<ToggleButton IsChecked="{Binding Status???}" />
So how can I bind Status to IsChecked then? I prefer a xaml solution.
I want to bind IsChecked property so that it's True when the Status is equal to Status1, and it's false in other cases. I prefer not to write any code in a *.cs file, but only xaml code.
As an alternative you can use pure XAML solution with DataTrigger. Assuming that you have something like
public enum SomeThirdPartyClassStatus {
Status1,
Status2,
Status3,
Status4
}
you can do
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="IsChecked" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="{x:Static local:SomeThirdPartyClassStatus.Status1}">
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
Where local is namespace for SomeThirdPartyClassStatus like
xmlns:local="clr-namespace:WpfApplication1"
Caveat is that it will work one-way only
EDIT
For two-way binding you'll need custom IValueConverter
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (SomeThirdPartyClassStatus)value == SomeThirdPartyClassStatus.Status1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool?)value == true ? SomeThirdPartyClassStatus.Status1 : SomeThirdPartyClassStatus.Status2;
}
}
and then the binding would look like
<ToggleButton IsChecked="{Binding Path=Status, Converter={StaticResource EnumConverter}}"/>
Create a class derived from IValueConverter which you apply to your binding.
https://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter(v=vs.110).aspx
Update:
Simply use this style for toggle button
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="Status1" >
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
So if ever Status has value Status1 toggle button will be checked.

WPF Custom Control "style model"

I have custom control in wpf which change his look based on one Property:
...
<Grid>
<Rectangle Fill="[Something]" />
</Grid>
In code i have the property AlarmLevel, when AlarmLevel is bigger than 5 I want the fill to be red, otherwise blue.
How can I do this. (I don't want the fill property to be exposed)
Tnx
Since you're basing your fill value on an inequality, you could do this a couple of ways.
The recommended way is probably to use a converter on your binding to make it into a boolean value. Then use a data trigger to set the fill value based on whether the value is true or false, like so:
<Rectangle>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding AlarmLevel, Converter={StaticResource AlarmLevelConverter}}" Value="True">
<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="Red" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding AlarmLevel, Converter={StaticResource AlarmLevelConverter}}" Value="False">
<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="Black" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Your converter could look something like (perhaps with more exception handling):
public class AlarmLevelConverter: IValueConverter {
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return ((int)(value) > 5);
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotSupportedException();
}
}
Don't forget you'll need to add a reference to the converter class as a resource on your user control:
If you wanted to forego the converter method, you could also create a "helper" boolean property in your data context called something like "IsAlarming". It would look something like:
public bool IsAlarming {
get { return AlarmLevel > 5; }
}
You would then bind your data trigger to IsAlarming rather than AlarmLevel. This isn't recommended though, because it's not pure MVVM.

When an error is presented, how can I set the style?

sometimes I see when the binding present an incorrect input shows a red border around the control. How can I modify it or where? And if it's possible, put me an example.
Thanks in advance.
You can do something like this for a TextBox
<Style x:Key="TextBoxWithValidation" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource TextBoxValidationTemplate}"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="{StaticResource BackgroundValidationBrush}"/>
<Setter
Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Mode=Self},
Path=(Validation.Errors)[0].ErrorContent)}"
/>
</Trigger>
</Style.Triggers>
</Style>
Where BackgroundValidationBrush would be say Pink.
Note that the common solution of binding ToolTip to (Validation.Errors)[0].ErrorContent will result in lots of debug spew (technical term) when there are no errors, so you're better off with a converter like this:
[ValueConversion(typeof(ReadOnlyObservableCollection<ValidationError>), typeof(string))]
public class ValidationErrorsToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
var errors = value as ReadOnlyObservableCollection<ValidationError>;
// If there are no errors then return an empty string.
// This prevents debug exception messages that result from the usual Xaml of "Path=(Validation.Errors)[0].ErrorContent".
// Instead we use "Path=(Validation.Errors), Converter={StaticResource ValidationErrorsConverter}".
if (errors == null)
{
return string.Empty;
}
var errors2 = errors.Select(e => e.ErrorContent).OfType<string>().ToArray();
return errors.Any() ? string.Join("\n", errors2) : string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
and the we can use
<converters:ValidationErrorsToStringConverter x:Key="ValidationErrorsConverter"/>
<!-- Style to be used as the base style for all text boxes -->
<Style x:Key="TextBoxWithValidation" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource TextBoxValidationTemplate}"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="{StaticResource BackgroundValidationBrush}"/>
<Setter
Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Mode=Self},
Path=(Validation.Errors),
Converter={StaticResource ValidationErrorsConverter}}"
/>
</Trigger>
</Style.Triggers>
</Style>
I think you searching for something like
Data Validation
Can use a sniplet:
<Binding.ValidationRules>
<DataErrorValidationRule/>
</Binding.ValidationRules>
Define your validation rules and in case of failure you will be given a red rectangle
arround the associated control.
Set the attached property Validaton.ErrorTemplate in your TextBox style. (See the docs for an example)

Categories

Resources