0

I want to show negative currencies in a DataGridTextColumn in red and also in brackets such as ($200.00). I am able to convert the foreground to Red whenever the amount is negative using a converter as shown in the following XAML. However, when I try to include a string format such as Binding="{Binding Path=NonTaxable, StringFormat=c2}" the foreground color conversion fails.

How can I show negative currencies

(a) in Red

(b) in Brackets

(c) with a $ currency sign?

Here are the XAML and codes.

<DataGridTextColumn Width="*" Header="NonTaxable" Binding="{Binding Path=NonTaxable}" >
   <DataGridTextColumn.ElementStyle>
      <Style TargetType="{x:Type TextBlock}">
          <Style.Triggers>
              <DataTrigger  Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text, Converter={StaticResource NegativeValueConverter}}" Value="-1" >
                   <Setter Property="Foreground" Value="Red" />
             </DataTrigger>
          </Style.Triggers>
       </Style>
   </DataGridTextColumn.ElementStyle>               
</DataGridTextColumn>

VB.Net

Imports System.Globalization

Public Class NegativeValueConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
        Dim doubleValue As [Double] = 0.0
        If value IsNot Nothing Then
            If [Double].TryParse(value.ToString(), doubleValue) Then
                If doubleValue < 0 Then
                    Return -1
                End If
            End If
        End If
        Return 1
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function

End Class

C#

using System.Globalization;

public class NegativeValueConverter : IValueConverter
{

    public object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Double doubleValue = 0.0;
        if (value != null) {
            if (Double.TryParse(value.ToString(), doubleValue)) {
                if (doubleValue < 0) {
                    return -1;
                }
            }
        }
        return 1;
    }

    public object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

}

2 Answers2

0

EDIT

Sorry for misunderstanding your question.

It does not work, because you are using ElementStyle, and binding to the Text property of the underlying TetxBlock. After applying StringFormat, your converter will no longer work, because it will get, for example, "($-200.00)" instead of "200.0", and then Double.TryParse() will fail and return false. You could either change the converter accordingly (to work with the formatted text) or bind your trigger to the underlying data instead of the formatted text.

For example, it will work with CellStyle instead, because then you can access the bound values directly.

<DataGridTextColumn Width="*" Header="NonTaxable" Binding="{Binding Path=NonTaxable, StringFormat=(${0:0.00})}" >
    <DataGridTextColumn.CellStyle>
       <Style TargetType="DataGridCell">
           <Setter Property="Foreground" Value="Black"/>
           <Style.Triggers>
               <DataTrigger Binding="{Binding Path=NonTaxable, Converter={StaticResource NegativeValueConverter}}" 
                       Value="-1" >
                    <Setter Property="Foreground" Value="Red" />
                </DataTrigger>
              </Style.Triggers>
            </Style>
         </DataGridTextColumn.CellStyle>
     </DataGridTextColumn>
  </DataGrid.Columns>
</DataGrid>

END EDIT


Your StringFormat would be:

Binding="{Binding Path=NonTaxable, StringFormat=StringFormat=({0:C2})}"

If your value is going to be first in the formatted string, remember to prefix it with additional pair of brackets like his:

Binding="{Binding Path=NonTaxable, StringFormat=StringFormat={}{0:C2}}"

Also, beware that C2 format is culture-specific. Always beware of the language set in your application, otherwise you may end up with unexpected formatting. Take a look at this StackOverflow question.

If you want to make sure that the currency sign is always $ in your case, then maybe setting the format explicitly would be better:

Binding="{Binding Path=NonTaxable, StringFormat=StringFormat=(${0:0.00})}}"
Arie
  • 5,251
  • 2
  • 33
  • 54
  • The first thing is to detect whether the currency is negative and if so the foreground should be red. That is why I have used a converter which works fine. After that I want to include a currency sign which works but then the converter in the DataTrigger works only if I don't put the StringFormat. It does not work in combination with a StringFormat. – user10403352 Nov 21 '19 at 02:18
  • A similar question was asked ( https://stackoverflow.com/questions/7492343/pass-value-from-binding-to-converter-in-elementstyle) – user10403352 Nov 21 '19 at 02:39
  • @user10403352 sorry, I misunderstood your question. I edited my answer. – Arie Nov 21 '19 at 07:55
0

What Arie has observed is true. I was about to post my own answer before I saw that Arie has also observed the same thing I did. Nontheless credit should go to Arie.

Basically I wanted to show negative Currencies in both RED and BRACKET. To do that I decided to:

(a). Use StringFormat in binding just the way Excel works i.e Binding="{Binding Path=NonTaxable, StringFormat={}{0:$#,##0.00;($#,##0.00)}} to convert negative to bracketed amount with a Dollar sign.

(b) After putting the negative currency in bracket I wanted to show the text in Red. I thought I could use StringFormat={}{0:$#,##0.00;[Red]($#,##0.00) the way it is done in Excel and unfortunately that cannot be done in XAML. Hence I decided to use a Converter in a DataTrigger and the color did not change.

The challenge was if I use the StringFormat on its own it works. Similarly the DataTrigger on its own it also works. But whenever I combine the StringFormat and the DataTrigger, the DataTrigger was not firing.

After digging and digging I noticed that the StringFormat fires first followed by the DataTrigger. This is crucial because it means that by the time the DataTrigger fires the text is no longer a numeric value but a formatted text (string) with Dollar sign and in brackets. Therefore, sending this formatted text (string) to the converter in the DataTrigger was not working because in the Converter I was checking whether the numeric value is < 0 when in fact it is no longer a numeric value but a text (string) as formatted by the StringFormat.

In short the solution involved modifying the Converter as follows:

As Arie has said, after entering say -8000 the StringFormat changes this text to ($8,000.00). Hence what is being sent to the converter is not -8000 but in fact ($8,000.00). Through the use of the Replace command this string must be converted back to -8000 before any checking can be done in the converter.

Hence the converter should read:

Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
        Dim doubleValue As [Double] = 0.0
        If value IsNot Nothing Then

            Dim Input As String = TryCast(value, String)
            If Not String.IsNullOrEmpty(Input) Or
                    Not String.IsNullOrWhiteSpace(Input) Then
                Input = Input.Replace("$", "")
                Input = Input.Replace(" ", "")
                Input = Input.Replace(",", "")
                Input = Input.Replace(")", "")
                Input = Input.Replace("(", "-")                

                If [Double].TryParse(Input.ToString(), doubleValue) Then

                    If doubleValue < 0 Then
                        Return -1
                    End If
                End If
            End If

        End If
        Return 1
    End Function

Thanks Arie for your answer as well. It now works.