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

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)

Related

Converting RowStyle values Conditionally

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;
}

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.

How to implement NullText in a TextBlock with Binding?

I would like to implement a "NullText" behavior for a TextBlock that is bound to a property in a ViewModel. When that property in the ViewModel is null or empty, I would like to display gray italic text something like "No Data". I'd like this to follow MVVM pattern but I am lost...
Update
So after playing around with the solution James Webster suggested, I got it to work like this...
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<c:NullOrEmptyValueConverter x:Key="NullOrEmptyValueConverter" Text="(No Data)"/>
</UserControl.Resources>
...
<TextBlock Name="SerialNumberTextBlock" Text="{Binding Path=SerialNumber, Converter={StaticResource NullOrEmptyValueConverter}}">
<TextBlock.Resources>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SerialNumberTextBlock, Path=Text}" Value="(No Data)">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Resources>
</TextBlock>
I'd recommend implementing an IValueConverter; if the source value is not null or empty, then pass it through to the TextBlock. If the source value is null or empty, then render your chosen text.
public class NullValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
if (str.IsNullOrWhitespace())
{
return "No Data";
}
return str;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
... //An empty implementation I expect...
}
}
However I have just realised that you want to set the style as well... hmmm, probably a DataTrigger that sets the style if the value is 'No Data' required I expect;
<TextBlock Text="{Binding Path=SomeProperty, Converter={StaticResource keyToNullValueConverter}">
<TextBlock.Triggers>
<DataTrigger Binding="{Binding Path=Text}" Value="No Data">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
</TextBlock.Triggers>
</TextBlock>
Something along those lines might work.
I think you don't need to create Converter Class, you can simply write your style code like this.
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SerialNumberTextBlock, Path=Text}" Value="{x:Null}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=SerialNumberTextBlock, Path=Text}" Value="{x:Static System:String.Empty}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
</Style.Triggers>
</Style>
Note :- You need to include the system namespace as
xmlns:System="clr-namespace:System;assembly=mscorlib"
You could try to bind to a property that looks thus
private string _textBlockText;
public string TextBlockText
{
get
{
if(string.IsNullOrEmpty(_textBlockText))
{
return "No Data";
}
return _textBlockText;
}
set
{
_textBlockText = value;
}
}
and then use the XAML that James has mentioned for styling. Saves the need for a converter.
Very late to the party, but here is my answer.
<TextBox >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding MyText, TargetNullValue=No data}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyText}" Value="{x:Null}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<DataTrigger Binding="{Binding MyText}" Value="{x:Static System:String.Empty}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Text" Value="{Binding MyText}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
My answer assumes that you want the default text to disappear on focus, which is more in line how inputs with default text usually behave. Also this works only for null values. If you need a check for string empty or additional logic, you could instead use a converter like in other provided answers. The key idea is, that you remove null value text or converter in the binding when control gains focus and thus only display the default text when control has no focus. This also prevents the default value flowing back to your view model.
DataTriggers part for font style was kindly borrowed from pchajer's answer.

Setting XAML Datagrid Text colour per row

I am trying to add validation to a datagrid in XAML.
Currently, I have created a method which checks 3 textboxes for valid input and if false sets a value in the collection the datagrid is bound to, to either true or false. This part works fine.
The problem I am now having is making this actually work.
I have created a converter :
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isRowValid = ((bool)value);
ConsoleColor validColour = ConsoleColor.Green;
ConsoleColor invalidColour = ConsoleColor.Red;
if (isRowValid)
{
return validColour;
}
else
{
return invalidColour;
}
}
So when the row is invalid it will display red, when valid it will display green.
The method to validate each row gets called when the row looses focus, and by default each row is set to invalid.
I have been attempting to set the background for each row by doing
<DataGrid.RowBackground>
<SolidColorBrush Color="{Binding Path=IsRowValid, Converter={StaticResource RowValidConverter}}" />
</DataGrid.RowBackground>
but ideally, I would like this to only adjust the text colour and not the background.
Any suggestions on how to implement this would be greatly appreciated,
Thanks.
If I understand your problem correctly then I think an easier approach is to add a Trigger for Validation.HasError in the RowStyle
<DataGrid ...>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="TextElement.Foreground" Value="Green"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="TextElement.Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<!--...-->
</DataGrid>
Or if you're controling this some other way with a Property called IsRowValid in your ViewModel then you can just change the Trigger to a DataTrigger
<DataTrigger Binding="{Binding IsRowValid}" Value="True">
<Setter Property="TextElement.Foreground" Value="Red"/>
</DataTrigger>

Categories

Resources