1

The Problem

I'm new to WPF and trying to learn. I have a basic ListView showing info about people such as Name, Age and Grade.

I want the Grade result text to be green if enum is "Pass" and red if "Fail", otherwise the text colour isn't changed.

What I tried

I know you can hard code all text in a column to be green, red etc with Foreground="" but this wouldn't work. I tried implementing a function that checks if each enum in the list equals Pass etc but I couldn't get it and I'm quite stuck here.

XAML

<Grid Margin="10">
        <ListView Name="lvUsers">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
                    <GridViewColumn Header="Grade" Width="100" DisplayMemberBinding="{Binding Grade}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>

CS

public partial class MainWindow : Window
    {
        public class User
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public Grade Grade { get; set; }
        }

        public MainWindow()
        {
            InitializeComponent();
            List<User> items = new List<User>();
            items.Add(new User() { Name = "John Doe", Age = 42, Grade = Grade.fail });
            items.Add(new User() { Name = "Jane Doe", Age = 39, Grade = Grade.pass });
            items.Add(new User() { Name = "Sammy Doe", Age = 13, Grade = Grade.fail });
            lvUsers.ItemsSource = items;
        }

        public enum Grade
        {
            none = 0,
            pass = 1,
            fail = 2
        };
    }

Expected result

I'm don't want to have all text in Grade column to be green/red. And I don't want to add a Colour property inside the User Class.

When the enum value is "Pass" for the User, the "Pass" text in the Grade column will be green. When it's "Fail", the text will be red. Otherwise, text colour is not changed.

Any help is much appreciated, because I'm quite stuck here.

paddystar
  • 37
  • 5
  • Use converter: https://www.wpf-tutorial.com/data-binding/value-conversion-with-ivalueconverter/ – mshwf Jun 30 '19 at 20:46

1 Answers1

3

You actually have a multitude of options to use here:

Firstly you will need to replace this GridViewColumn entry with the examples in one of the following sections:

<GridViewColumn Header="Grade" Width="100" DisplayMemberBinding="{Binding Grade}"/>

1 - DataTrigger

MSDN documentation here

This will work but is not reusable.

<!-- A custom cell template lets you customise how the cell will display -->
<GridViewColumn Header="Grade" Width="10">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Name="GradeText" Text="{Binding Grade}" />
            <!-- define rules on how the ui will change based on the data bound -->
            <DataTemplate.Triggers>
                <!-- see NOTE below for how to get this working -->
                <DataTrigger Binding="{Binding Grade}" Value="{x:Static enum:Grade.pass}">
                    <Setter TargetName="GradeText" Property="Foreground" Value="Green"/>
                </DataTrigger>
                <!-- you can add a second one for fail ;) -->
            </DataTemplate.Triggers>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

NOTE you will also need to add a namespace declaration to where you enum is declared (full credit to this answer):

xmlns:enum="clr-namespace:YourEnumNamespace;assembly=YourAssembly"

2 - Converter

MSDN documentation here

This provides the least amount of XAML, allows you to reuse the logic elsewhere and if you really care about it, unit test the converter logic.

<!-- A custom cell template lets you customise how the cell will display -->
<GridViewColumn Header="Grade" Width="10">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Name="GradeText" Text="{Binding Grade}" Foreground="{Binding Grade, Converter={StaticResource GradeToBrushConverter}}"/>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

And the converter code:

public GradeToBrushConverter : IValueConverter
{
    public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is Grade grade)
        {
            switch (grade)
            {
                case Grade.pass:
                    return Brushes.Green;
                case fail:
                    return Brushes.Red;
                default:
                    return Brushes.Black; // Or a more sensible default.
            }
        }

        return Brushes.Black;
    }

    // I haven't provided the ConvertBack but you should be able to work this bit out.
}

3 - Styling

Yes I appreciate that this example looks quite similar to point 1 but it has the added benefit that if you declare the style somewhere else it could be reused in multiple places.

<!-- A custom cell template lets you customise how the cell will display -->
<GridViewColumn Header="Grade" Width="10">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Name="GradeText" Text="{Binding Grade}">
                <TextBlock.Style>
                    <Style TargetType="TextBlock">
                        <Setter Property="Foreground" Value="Black"/>
                        <Style.Triggers>
                            <!-- see NOTE below for how to get this working -->
                            <DataTrigger Binding="{Binding Grade}" Value="{x:Static enum:Grade.pass}">
                                <Setter Property="Foreground" Value="Green"/>
                            </DataTrigger>
                            <!-- you can add a second one for fail ;) -->
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>
Bijington
  • 3,661
  • 5
  • 35
  • 52