Databinding, ListView/GridView and Converter

Had some trouble with using a converter when databinding on a GridView yesterday so I thought I’d do a quick write up.

Imagine for one instance you have the following class:

public class EntityDataItem
{
    public string Name { get; set; }
    public string Type { get; set; }
    public DateTime CreatedTS { get; set; }
}

Now imagine you have the following xaml:

<ListView Name="EntitiesGrid">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding Type}"/>
<GridViewColumn Header="CreatedTS" DisplayMemberBinding="{Binding CreatedTS}"/>
</GridView>
</ListView.View>
</ListView>

To bind to the grid all you would have to do on the window load event is:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    List<EntityDataItem> items = new List<EntityDataItem>();
    items.Add(new EntityDataItem { Name = "A", Type = "Person", CreatedTS = DateTime.Now });
    items.Add(new EntityDataItem { Name = "B", Type = "Equipment", CreatedTS = DateTime.Now });
    items.Add(new EntityDataItem { Name = "C", Type = "Location", CreatedTS = DateTime.Now });
    items.Add(new EntityDataItem { Name = "D", Type = "Unit", CreatedTS = DateTime.Now });
    EntitiesGrid.ItemsSource = items;
}

Well now, that’s all fine and good. But now imagine you were, oh, I don’t know, working on some military project that wanted you do use a specific format for the date. The way it is now when the binding happens it will use the default ToString() which isn’t acceptable in this case.

So we need to use a converter. Converters let you go from any object to any object on databinding. Let’s start by creating the converter:

[ValueConversion(typeof(DateTime), typeof(String))]
public class DateToMilitaryConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return "";
        DateTime date = (DateTime)value;
        return date.ToString("dd MMM yyyy HHmm", DateTimeFormatInfo.InvariantInfo);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string strValue = value.ToString();
        DateTime resultDateTime;
        if (DateTime.TryParse(strValue, out resultDateTime))
        {
            return resultDateTime;
        }
        return value;
    }
}

then in the resources for your xaml define:

<local:DateToMilitaryConverter x:Key="dateConverter"/>

assuming the local namespace is defined as your local assembly or whatever namespace you used for the converter.

Here’s where things get a bit tricky. You can’t put a converter directly on a GridViewColumn. Where you can put it is on the Binding. To do that make your xaml look like:

<ListView Name="EntitiesGrid">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
            <GridViewColumn Header="Type" DisplayMemberBinding="{Binding Type}"/>
            <GridViewColumn Header="CreatedTS">
                <GridViewColumn.DisplayMemberBinding>
                    <Binding Path="CreatedTS" Converter="{StaticResource dateConverter}"/>
                </GridViewColumn.DisplayMemberBinding>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

You can see here we are tapping into the DisplayMemberBinding propery of the GridViewColumn and assigning a Binding to it. On the Binding we can use a converter. As I mentioned though you can go from any object to any other object. Remember, however, regardless of what you go to, the ToString() will be called on it. A couple of points of interest on the converter. The source of the data in the real binding, not my contrived example, is a DateTime? which means the value could be null. Thus I have accounted for that in the converter. Additionally I allow for a class cast exception rather then doing:

DateTime date = value as DateTime;
if (value == null)
    return "";

in case someone else were to use my class where it doesn’t belong. Basically the converter handles DateTime? but nothing else, as it should be.

Well, I meant this one to be short and sweet and it was.

Later,
Brian

UPDATE:
It has been brought to my attention that you can do converters inline with the DisplayMemberBinding.

<GridViewColumn 
    Header="CreatedTS" 
    DisplayMemberBinding="{Binding Path=CreatedTS, Converter={StaticResource dateConverter}}"
/>

Live and learn 🙂
Brian

Previous Post

Comment (1)

  1. nazim

    Can you tell me how to retrieve the inserted into thé lisview?
    I want get the value of each column separatly.

Leave a Reply