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?
+1
A:
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 valueToBackground}}" />
</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
}
}
Wonko the Sane
2010-07-08 15:26:10
This would work, although I would rather create some sort of style (easier to reimplement), why would this not work with MVVM?
LnDCobra
2010-07-08 15:48:49
@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.
Jakob Christensen
2010-07-08 16:02:47
@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>
Wonko the Sane
2010-07-08 16:14:52
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.
John Bowen
2010-07-08 17:40:52
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.
Wonko the Sane
2010-07-08 18:07:44
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.
LnDCobra
2010-07-08 18:50:26
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.
Wonko the Sane
2010-07-08 19:08:07
+1
A:
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>
Veer
2010-07-09 11:19:17
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.
Wonko the Sane
2010-07-09 13:46:43
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)
LnDCobra
2010-07-10 20:08:40