I am trying to figure out the best way to create a style/trigger to set foreground to Red, when value is < 0. what is the best way to do this? I'm assuming DataTrigger, but how can I check for negative value, do i have to create my own IValueConverter?
WPF TextBlock Negative Number In Red
If you are not using an MVVM model (where you may have a ForegroundColor property), then the easiest thing to do is to create a new IValueConverter, binding your background to your value.
In MyWindow.xaml:
<Window ...
xmlns:local="clr-namespace:MyLocalNamespace">
<Window.Resources>
<local:ValueToForegroundColorConverter x:Key="valueToForeground" />
<Window.Resources>
<TextBlock Text="{Binding MyValue}"
Foreground="{Binding MyValue, Converter={StaticResource valueToForeground}}" />
</Window>
ValueToForegroundColorConverter.cs
using System;
using System.Windows.Media;
using System.Windows.Data;
namespace MyLocalNamespace
{
class ValueToForegroundColorConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
Double.TryParse(value.ToString(), out doubleValue);
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
This would work, although I would rather create some sort of style (easier to reimplement), why would this not work with MVVM? –
Divers
@LnDCobra: It will work with MVVM. What I think Wonko means is that with MVVM you may choose to expose a ForegroundColor property on your viewmodel instead of using a ValueConverter. –
Heptode
@Jakob: absolutely correct. @LnDCobra: you could implement a Style, with the value of the Foreground property set to the same binding as above. <Style TargetType={x:Type TextBlock}> <Setter Property="Foreground" Value="{Binding MyValue, Converter={StaticResource valueToBackground}}" /> </Style> –
Konstanz
A more flexible solution would be to use a converter that determines if a value is < 0 and return a boolean instead of outputting a fixed color. You could then use that with a DataTrigger and make whatever UI changes you need to in the Setters. Doing that is much more flexible when you decide later that you need the text to be bold instead of red and keeps the UI in the XAML where it belongs. –
Snake
Yes, that is true. I was just going by the context of the question, where it seems like returning a color was "good enough." It was just one simple solution that could certainly be expanded as needed - enough to "get going." As you point out, adding a trigger is just one more step. –
Konstanz
Thanks for the answers guys! The reason I don't want to expose a foreground property in my MVVM is because I want UI to purely stay in the View, and I will be using it on about 80% of numeric TextBlocks so exposing a foreground for each one is out of the question, both seem great answers, but I think I will go with John Bowens comment as it is more flexible :) Thanks for the input Wonko. –
Divers
No problem. One more expansion that you can make is to use the BasedOn attribute of the Style. As you say, you will be using the style on 80% of the textboxes. For the other 20% (or whatever subset), if the style is mostly the same, you can create a Style BasedOn the one created for your 80% version, and change just the properties (and triggers) that apply to those textblocks. –
Konstanz
@WonkotheSane
Foreground="{Binding MyValue, Converter={StaticResource valueToBackground}}" />
this should be Foreground="{Binding MyValue, Converter={StaticResource valueToForeground}}" />
–
Breadthways You should have your view specific information in your ViewModel. But you can get rid of the Style specific information in the ViewModel.
Hence create a property in the ViewModel which would return a bool value
public bool IsMyValueNegative { get { return (MyValue < 0); } }
And use it in a DataTrigger so that you can eliminate the ValueConverter and its boxing/unboxing.
<TextBlock Text="{Binding MyValue}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMyValueNegative}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Agreed that this would be preferable. The original answer stems from the fact that he did not mention that he was using MVVM, and the question implied (to me) that he wasn't. –
Konstanz
This would work, but the reason I don't want to do this, is because for every TextBlock I would need a separate property for each textblock (10+ per view often) –
Divers
Just a small point on naming conventions.. My apps get so full of this kind of xaml hack that I find it allot easier to name them by purpose. i.e: public bool DisplayBasketValueInRed { get; set; }. I also use (though probably shouldnt) a Generic class to reduce how verbose implementing INotifyPropertyChanged on these properties can be. –
Awfully
@LnDCobra I have a special solution for these scenarios also. Converters are too verbose or there isn't any re-use outside the view and properties make the ViewModel verbose... So I bind my value to
TextBlock.DataContext
rather then TextBlock.Text
. Then write a general (casting sender) DataContextChanged event which can be applied to each Textblock
–
Awfully For Amsakanna's solution I had to add a class name to the Property Setter:
<Setter Property="TextBlock.Foreground" Value="Red" />
This is because style should define TargetType: <Style TargetType="TextBlock"> –
Spector
© 2022 - 2024 — McMap. All rights reserved.