-1

i have a column in DataGridView , it include a duration in hours and minutes like

125:40

the problem is the column is a string , when the user sort the column as ascending sort the result will be like this

125:40

80:40

how can i sort this column as a date with keeping this format?

update: also if i deal with it as a timespan , how to format timespan as hhh:mm without convert it to string ?

Bahgat Mashaly
  • 510
  • 7
  • 15
  • 1
    You'd be better of using something like total minutes for the data (int) and just format/display it in that format. Otherwise look for a natural string sort. – Ňɏssa Pøngjǣrdenlarp May 27 '16 at 21:57
  • [Natural sort](http://stackoverflow.com/questions/248603/natural-sort-order-in-c-sharp) is the term you are looking for to implement this sorting. (Note that most likely you should be able to sort against value directly instead of formatted text, but I'm not DGV expert) – Alexei Levenkov May 27 '16 at 22:01
  • Looking at your comments you're mixing together _sorting_ the DataGridView with how you _display_ the data values. If this is the case, consider using a hidden column to do the sort, and another column for display, and the problem goes away. I've edited your question title to make it clearer what you are asking. – stuartd May 27 '16 at 22:06
  • thanks stuartd , but when the user click the column header the column will sorted as a built in behavior depend on column values – Bahgat Mashaly May 27 '16 at 22:19

2 Answers2

1

Using a custom formatter in a datagridview

 public partial class Form1 : Form
{
    public DataTable dt;
    public Form1()
    {
        InitializeComponent();
        dt = new DataTable("TestTable");
        dt.Columns.Add("Duration", typeof(TimeSpan));
        DataRow dr = dt.NewRow();
        dr.ItemArray = new object[] { new TimeSpan(1, 1, 1, 1) };
        dt.Rows.Add(dr);
        dataGridView1.DataSource = dt;
        this.dataGridView1.Columns["Duration"].DefaultCellStyle.Format = "l";

        this.dataGridView1.Columns["Duration"].DefaultCellStyle.FormatProvider = new TimeSpanFormatter();

        this.dataGridView1.DataError += DataGridView1_DataError;
        this.dataGridView1.CellFormatting += DataGridView1_CellFormatting;

    }



    void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {

        if (e.CellStyle.FormatProvider is ICustomFormatter)
        {
            e.Value = (e.CellStyle.FormatProvider.GetFormat(typeof(ICustomFormatter)) as ICustomFormatter).Format(e.CellStyle.Format, e.Value, e.CellStyle.FormatProvider);
            e.FormattingApplied = true;
        }
    }
    private void DataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
    {
        throw new NotImplementedException();
    }

    public class TimeSpanFormatter : IFormatProvider, ICustomFormatter
    {

        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
                return this;
            else
                return null;
        }

        public string Format(string fmt, object arg, IFormatProvider formatProvider)
        {
            if (arg == null) return string.Empty;

            if (arg.GetType() != typeof(TimeSpan))
                try
                {
                    return HandleOtherFormats(fmt, arg);
                }
                catch (FormatException e)
                {
                    throw new FormatException(String.Format("The format of '{0}' is invalid.", fmt), e);
                }

            string tResult = string.Empty;
            try
            {
                TimeSpan ts = (TimeSpan)arg;
                tResult = string.Format("{0:N0}:{1}", ts.TotalHours,ts.Minutes);
            }catch (Exception ex)
            {
                throw;
            }
            return tResult;
        }

        private string HandleOtherFormats(string format, object arg)
        {
            if (arg is IFormattable)
                return ((IFormattable)arg).ToString(format, System.Globalization.CultureInfo.CurrentCulture);
            else if (arg != null)
                return arg.ToString();
            else
                return String.Empty;
        }
    }
}
Community
  • 1
  • 1
Peter4499
  • 685
  • 7
  • 15
  • i read about Formatting TimeSpan , i should convert TimeSpan to string to get this format "hhh:mm" – Bahgat Mashaly May 27 '16 at 22:15
  • No, you shouldn't have to do that. See this MSDN article on how to set the formatting of a cell. In your datasource you leave it as a timespan, you're just setting the property on how that column should display the value. [How to: Format Data in the Windows Forms DataGridView Control](https://msdn.microsoft.com/en-us/library/f9x2790s(v=vs.100).aspx) – Peter4499 May 27 '16 at 22:42
  • thanks Peter4499 , but there is no format 'hhh:mm' for datetime or timestamp – Bahgat Mashaly May 27 '16 at 23:02
  • You're right, I changed the answer to be a custom formatter implementation – Peter4499 May 28 '16 at 00:21
  • try to change this line dr.ItemArray = new object[] { new TimeSpan(1, 1, 1, 1) }; to dr.ItemArray = new object[] { new TimeSpan( 125, 40, 0) }; it will not give you hhh:mm format – Bahgat Mashaly May 28 '16 at 07:30
  • the result is 1.01:01:01 it is not hhh:mm format you should convert day to 24 hours to be like this 25:01 – Bahgat Mashaly May 28 '16 at 08:08
0

i get the answer i did a class implement IComparable

the class will be

 class hhhmmCustomComparer : IComparable 
    {
        private static int sortOrderModifier = 1;
        private int columnIndex;
        public string stringValue;
        public hhhmmCustomComparer(int columnIndex, System.Windows.Forms.SortOrder sortOrder)
        {
            this.columnIndex = columnIndex;
            if (sortOrder == SortOrder.Descending)
            {
                sortOrderModifier = -1;
            }
            else if (sortOrder == SortOrder.Ascending)
            {
                sortOrderModifier = 1;
            }
        }

        public hhhmmCustomComparer(int hours, int Minutes)
        {
            stringValue = hours.ToString() + ":" + Minutes.ToString();

        }

        public int Compare(DataRow Row1, DataRow Row2)
        {
            int CompareResult = 0;

            if (String.IsNullOrEmpty(Row1[columnIndex].ToString()) && String.IsNullOrEmpty(Row2[columnIndex].ToString()))
            {
                return 0;


            }
            else if (String.IsNullOrEmpty(Row1[columnIndex].ToString()))
            {
                CompareResult = -1;
            }
            else if (String.IsNullOrEmpty(Row2[columnIndex].ToString()))
            {
                CompareResult = 1;//1 , -1
            }
            else
            {
                TimeSpan CellValue1 = new TimeSpan(int.Parse(Row1[columnIndex].ToString().Split(':')[0]), int.Parse(Row1[columnIndex].ToString().Split(':')[1]), 0);
                TimeSpan CellValue2 = new TimeSpan(int.Parse(Row2[columnIndex].ToString().Split(':')[0]), int.Parse(Row2[columnIndex].ToString().Split(':')[1]), 0);
                CompareResult = TimeSpan.Compare(CellValue1, CellValue2);

            }

            return CompareResult * sortOrderModifier;


        }

        public int Compare(object x, object y)
        {
            try
            {

                DataGridViewRow Row1 = (DataGridViewRow)x;
                DataGridViewRow Row2 = (DataGridViewRow)y;
                int CompareResult = 0;

                if (Row1.Index >= 0 && Row2.Index >= 0 && !String.IsNullOrEmpty(Row1.Cells[columnIndex].Value.ToString()) && !String.IsNullOrEmpty(Row2.Cells[columnIndex].Value.ToString()))
                {

                    TimeSpan CellValue1 = new TimeSpan(int.Parse(Row1.Cells[columnIndex].Value.ToString().Split(':')[0]), int.Parse(Row1.Cells[columnIndex].Value.ToString().Split(':')[1]), 0);
                    TimeSpan CellValue2 = new TimeSpan(int.Parse(Row2.Cells[columnIndex].Value.ToString().Split(':')[0]), int.Parse(Row2.Cells[columnIndex].Value.ToString().Split(':')[1]), 0);
                    CompareResult = TimeSpan.Compare(CellValue1, CellValue2);


                }
                return CompareResult * sortOrderModifier;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        public override string ToString()
        {
            return stringValue;
        }

        public int CompareTo(object obj)
        {
            var val1 = this.ToString();
            var val2 = obj.ToString();
            var CompareResult = 0;
            if (String.IsNullOrEmpty(val1) && String.IsNullOrEmpty(val2))
            {
                CompareResult = 0;


            }
            else if (String.IsNullOrEmpty(val1))
            {
                CompareResult = -1;
            }
            else if (String.IsNullOrEmpty(val2))
            {
                CompareResult = 1;//1 , -1
            }
            else
            {
                TimeSpan CellValue1 = new TimeSpan(int.Parse(val1.Split(':')[0]), int.Parse(val1.Split(':')[1]), 0);
                TimeSpan CellValue2 = new TimeSpan(int.Parse(val2.Split(':')[0]), int.Parse(val2.Split(':')[1]), 0);
                CompareResult = TimeSpan.Compare(CellValue1, CellValue2);

            }

            return CompareResult * sortOrderModifier;
            // throw new NotImplementedException();
        }


    }

then when add the value to make it instance of this class like this

row["myColumnName"] = new hhhmmCustomComparer(((x.Days * 24) + x.Hours), x.Minutes);

Bahgat Mashaly
  • 510
  • 7
  • 15