I use the following technique to bind data to a ListView.

It supports proper (not text-based) sorting. In the case above, by string, DateTime and integer.
The ListView above was generated with this code:
var columnMapping = new List<(string ColumnName, Func<Person, object> ValueLookup, Func<Person, string> DisplayStringLookup)>()
{
    ("Name", person => person.Name, person => person.Name),
    ("Date of birth", person => person.DateOfBirth, person => $"{person.DateOfBirth:dd MMM yyyy}"),
    ("Height", person => person.HeightInCentimetres, person => Converter.CentimetresToFeetInchesString(person.HeightInCentimetres))
};
var personListview = new ListViewEx<Person>(columnMapping)
{
    FullRowSelect = true,
    View = View.Details,
    Left = 20,
    Top = 20,
    Width = 500,
    Height = 300,                
};
var people = new[]
{
    new Person("Cathy Smith", DateTime.Parse("1980-05-15"), 165),
    new Person("Bill Wentley", DateTime.Parse("1970-10-30"), 180),
    new Person("Alan Bridges", DateTime.Parse("1990-03-22"), 190),
};
personListview.AddRange(people);
Controls.Add(personListview);
In the column mapping, you'll notice that you have to specify how to get the item's value (for sorting) as well as a string (for displaying).
Full source:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace GUI
{
    public class ListViewEx<T> : ListView
    {
        public ListViewEx(IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> columnInfo) : base()
        {
            ColumnInfo = columnInfo;
            DoubleBuffered = true;
            //Create the columns
            columnInfo
                .Select(ci => ci.ColumnName)
                .ToList()
                .ForEach(columnName =>
                {
                    var col = Columns.Add(columnName);
                    col.Width = -2;
                });
            //Add the sorter
            lvwColumnSorter = new ListViewColumnSorter<T>(columnInfo);
            ListViewItemSorter = lvwColumnSorter;
            ColumnClick += ListViewEx_ColumnClick;
        }
        IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> ColumnInfo { get; }
        private readonly ListViewColumnSorter<T> lvwColumnSorter;
        public void Add(T item)
        {
            var lvi = Items.Add("");
            lvi.Tag = item;
            RefreshContent();
        }
        public void AddRange(IList<T> items)
        {
            foreach (var item in items)
            {
                Add(item);
            }
        }
        public void Remove(T item)
        {
            if (item == null) return;
            var listviewItem = Items
                        .Cast<ListViewItem>()
                        .Select(lvi => new
                        {
                            ListViewItem = lvi,
                            Obj = (T)lvi.Tag
                        })
                        .FirstOrDefault(lvi => item.Equals(lvi.Obj))
                        .ListViewItem;
            Items.Remove(listviewItem);
            RefreshContent();
        }
        public List<T> GetSelectedItems()
        {
            var result = SelectedItems
                            .OfType<ListViewItem>()
                            .Select(lvi => (T)lvi.Tag)
                            .ToList();
            return result;
        }
        public void RefreshContent()
        {
            var columnsChanged = new List<int>();
            Items
                .Cast<ListViewItem>()
                .Select(lvi => new
                {
                    ListViewItem = lvi,
                    Obj = (T)lvi.Tag
                })
                .ToList()
                .ForEach(lvi =>
                {
                    //Update the contents of this ListViewItem
                    ColumnInfo
                        .Select((column, index) => new
                        {
                            Column = column,
                            Index = index
                        })
                        .ToList()
                        .ForEach(col =>
                        {
                            var newDisplayValue = col.Column.DisplayStringLookup(lvi.Obj);
                            if (lvi.ListViewItem.SubItems.Count <= col.Index)
                            {
                                lvi.ListViewItem.SubItems.Add("");
                            }
                            var subitem = lvi.ListViewItem.SubItems[col.Index];
                            var oldDisplayValue = subitem.Text ?? "";
                            if (!oldDisplayValue.Equals(newDisplayValue))
                            {
                                subitem.Text = newDisplayValue;
                                columnsChanged.Add(col.Index);
                            }
                        });
                });
            columnsChanged.ForEach(col => { Columns[col].Width = -2; });
            //AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
            //AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
        }
        private void ListViewEx_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            if (e.Column == lvwColumnSorter.ColumnToSort)
            {
                if (lvwColumnSorter.SortOrder == SortOrder.Ascending)
                {
                    lvwColumnSorter.SortOrder = SortOrder.Descending;
                }
                else
                {
                    lvwColumnSorter.SortOrder = SortOrder.Ascending;
                }
            }
            else
            {
                lvwColumnSorter.ColumnToSort = e.Column;
                lvwColumnSorter.SortOrder = SortOrder.Ascending;
            }
            Sort();
        }
    }
    public class ListViewColumnSorter<T> : IComparer
    {
        public ListViewColumnSorter(IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> columnInfo)
        {
            ColumnInfo = columnInfo;
        }
        public int Compare(object x, object y)
        {
            if (x == null || y == null) return 0;
            int compareResult;
            var listviewX = (ListViewItem)x;
            var listviewY = (ListViewItem)y;
            var objX = (T)listviewX.Tag;
            var objY = (T)listviewY.Tag;
            if (objX == null || objY == null) return 0;
            var valueX = ColumnInfo[ColumnToSort].ValueLookup(objX);
            var valueY = ColumnInfo[ColumnToSort].ValueLookup(objY);
            compareResult = Comparer.Default.Compare(valueX, valueY);
            if (SortOrder == SortOrder.Ascending)
            {
                return compareResult;
            }
            else if (SortOrder == SortOrder.Descending)
            {
                return -compareResult;
            }
            else
            {
                return 0;
            }
        }
        public int ColumnToSort { get; set; } = 0;
        public SortOrder SortOrder { get; set; } = SortOrder.Ascending;
        public IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> ColumnInfo { get; }
    }
}