Search code examples
c#wpfformattingwpfdatagrid

Page shared DataTrigger style converter cannot be found with StaticResource


I am trying to learn some WPF in my free time, and I have gotten an application up and running, but one of the things that has eluded me for a while is how to set up a style for several datagrids that can appear on the pages, based on the row's value for a column which they will all share.

The intent is that I can style an entire row's text color (Foreground property) based on whether a currency amount is positive or negative.

What I have been able to get to so far works for single pair of hardcoded values, the issue I am having is wiring up to a Converter that will do the comparison I want in order to return True/False...

This is what I have so far:

<DataGrid AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Width="*" Binding="{Binding Date}" Header="Date"/>
        <DataGridTextColumn Width="*" Binding="{Binding Category}" Header="Category"/>
        <DataGridTextColumn Width="*" Binding="{Binding Amount}" Header="Amount"/>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

The specific Page I am trying to set up will have this same type of grid 3 different times, with different permutations of the data. The grids themselves are all working as desired.

The datagrid is populated with a collection of this class:

public class TransactionDto
{
    public long Id { get; set; }
    public decimal Amount { get; set; }
    public string Category { get; set; }
    public DateTime Date { get; set; }
}

The converter I have set up is as follows:

public class AmountConverter : IValueConverter
{
    private const decimal Threshold = 0.00m;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (decimal)value >= Threshold;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

However, I cannot seem to get the binding between the datagrid's columns and this converter figured out. This is what I have tried so far, based on experimenting with answers posted on other SO questions and elsewhere, but those have not been quite the same situation and attempting to apply it for my situation has not worked yet - I am getting an exception due to the converter binding here:

<Page.Resources>
<Style TargetType="DataGridRow">
    <Style.Triggers >
        <DataTrigger Binding="{Binding Amount, RelativeSource={RelativeSource Self}, Converter={StaticResource ResourceKey=AmountConverter}}" Value="False">
            <Setter Property="Foreground" Value="Red"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Amount}" Value="-24.71">
            <Setter Property="Foreground" Value="Green"/>
        </DataTrigger>
    </Style.Triggers>
</Style>        

The trigger that uses the hardcoded value of -24.71 works fine, as did a similar one for a positive value.

The code-behind C# file contains a property with the desired Converter type:

public AmountConverter AmountConverter { get; set; } = new AmountConverter();

The current exception I am getting is the following:

System.Windows.Markup.XamlParseException

Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an exception.

I don't really understand this exception and am not quite sure how to proceed or what it is telling me is the issue.

Ideally, I would set up this styling stuff in App.xaml, and reference it from the datagrids it is relevant to.


Solution

  • I found the answer to this via this article. The changes I made were to add the following to my App.xaml file, which then applied the desired changes across the entire application to all of the relevant datagrids:

    <Application.Resources>
        <local:AmountConverter x:Key="AmountConverter"/>
        <Style TargetType="DataGridRow">
        <Style.Triggers >
            <DataTrigger Binding="{Binding Amount, Converter={StaticResource AmountConverter}}" Value="False">
                <Setter Property="Foreground" Value="Red"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Amount, Converter={StaticResource AmountConverter}}" Value="True">
                <Setter Property="Foreground" Value="Green"/>
            </DataTrigger>
        </Style.Triggers>
        </Style>
    </Application.Resources>