WPF TextBlock中负数以红色显示

8

我正在努力找到创建样式/触发器的最佳方法,当值<0时将前景设置为红色。如何做到最好?我假设使用DataTrigger,但是如何检查负值,我必须创建自己的IValueConverter吗?

3个回答

14
如果您没有使用MVVM模型(在其中可能有ForegroundColor属性),那么最简单的方法是创建一个新的IValueConverter,将您的背景绑定到您的值。
在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
    }
}

2
@LnDCobra:它将与MVVM一起工作。我认为Wonko的意思是,使用MVVM时,您可以选择在视图模型上公开ForegroundColor属性,而不是使用ValueConverter。 - Jakob Christensen
1
@Jakob:完全正确。 @LnDCobra:你可以实现一个样式,把Foreground属性的值设置为上面的绑定。<Style TargetType={x:Type TextBlock}> <Setter Property="Foreground" Value="{Binding MyValue, Converter={StaticResource valueToBackground}}" /> </Style> - Wonko the Sane
4
一种更加灵活的解决方案是使用一个转换器来确定值是否小于0,而不是输出固定的颜色,然后返回一个布尔值。你可以将其与DataTrigger一起使用,并在Setters中进行所需的UI更改。这样做在将来决定需要将文本变粗而不是红色时更加灵活,并将UI保持在XAML中。 - John Bowen
1
是的,没错。我只是根据问题的上下文来回答,似乎返回一个颜色就足够了。这只是一个简单的解决方案,当然可以根据需要进行扩展 - 足以“开始”。正如你所指出的,添加触发器只是多了一步。 - Wonko the Sane
1
@WonkotheSane Foreground="{Binding MyValue, Converter={StaticResource valueToBackground}}" /> 这应该是Foreground="{Binding MyValue, Converter={StaticResource valueToForeground}}" /> - fruggiero
显示剩余3条评论

9

您应该将视图特定信息放在ViewModel中。但是,可以在ViewModel中摆脱样式特定信息。

因此,在ViewModel中创建一个属性,它将返回布尔值。

public bool IsMyValueNegative { get { return (MyValue < 0); } }

可以在DataTrigger中使用它,这样就可以消除ValueConverter及其装箱/拆箱。

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

同意这将是更可取的。原始答案源于他没有提到他正在使用MVVM,而问题暗示(对我来说)他没有使用。 - Wonko the Sane
这个方法可以行得通,但我不想这样做的原因是,对于每个TextBlock,我都需要一个单独的属性(通常每个视图有10个以上)。 - Michal Ciechan
关于命名规范的小建议。我的应用程序中充斥着这种 XAML 技巧,所以我发现按照目的来命名它们更容易。例如:public bool DisplayBasketValueInRed { get; set; }。我还使用(虽然可能不应该)一个通用类来减少在这些属性上实现 INotifyPropertyChanged 时的冗长程度。 - Dead.Rabit
@LnDCobra,我有一种特殊的解决方案适用于这些情况。转换器过于冗长或者在视图之外没有重用,而属性又使ViewModel变得冗长……因此,我将我的值绑定到TextBlock.DataContext而不是TextBlock.Text。然后编写一个通用的(强制转换sender)DataContextChanged事件,该事件可以应用于每个Textblock - Dead.Rabit

6
对于Amsakanna的解决方案,我需要在Property Setter中添加一个类名:

<Setter Property="TextBlock.Foreground" Value="Red" />


这是因为样式应该定义TargetType: <Style TargetType="TextBlock"> - alphanoch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接