using Sarand.Net.Standard.Base.Utils; using System; using System.Collections.Generic; namespace Sarand.Net.Standard.Base.Core { //======================================================================= // Definitions. public enum ColumnAlignment { Default, Left, Center, Right } public enum ColumnBoolFormat { /// Text: 'Y' (true) or 'N' (false). YorN, /// Text: 'Y' (true). Y, /// Text: 'N' (false). N, /// Text: '1' (true) or '0' (false). OneOrZero, /// Text: 'True' or 'False'. TrueFalse, /// Graphic icons: Tick (true) or Cross (false). TickCross, /// Graphic icon: Tick (true). Tick, /// Graphic icon: Cross (false). Cross, /// Graphic icons (inverted): Tick (false) or Cross (true). TickCross_Inv, /// Graphic icon (inverted): Tick (false). Tick_Inv, /// Graphic icon (inverted): Cross (true). Cross_Inv } public enum ColumnTriStateFormat { /// Text: R, A, G etc. RAG, /// Text: 1, 2, 3 etc. Numeric, /// Graphic icons. Lights } //======================================================================= public class ColumnDfn { //------------------------------------------------------------------- // Definitions. public enum ColumnSortMode { Off, On, AscendOnly, DescendOnly } //------------------------------------------------------------------- // Data members. //------------------------------------------------------------------- // Construction. public ColumnDfn (int id, String caption, int width = 0, int dbColumn = 0, String dataName = "", String sortDataName = "", DataValue.DataType dataType = DataValue.DataType.String, ColumnAlignment alignment = ColumnAlignment.Default) { this.Id = id; this.Caption = caption; this.Width = width; this.DbColumn = dbColumn; this.DataName = dataName; this.SortDataName = sortDataName; this.DataType = dataType; this.Alignment = alignment; } //------------------------------------------------------------------- // Properties. public int Id { get; set; } = 0; /// Database column identity (DbDfn.ColumnIdx etc). public int DbColumn { get; set; } = 0; /// Column code - eg. for configuration options. public String Code { get; set; } = String.Empty; /// Column header text. public String Caption { get; set; } = String.Empty; /// Column width (device dependent). public int Width { get; set; } = 0; public ColumnAlignment Alignment { get; set; } = ColumnAlignment.Default; /// Data value name (may be an alias). public String DataName { get; set; } = String.Empty; public ushort NumDecimals { get; set; } = 2; public char NumThousands { get; set; } = ','; /// Default: Zero values hidden. public bool ShowZeroValues { get; set; } = false; public ColumnBoolFormat BoolFormat { get; set; } = ColumnBoolFormat.YorN; public ColumnTriStateFormat TriStateFormat { get; set; } = ColumnTriStateFormat.RAG; /// Null date - values on or before are considered invalid. public DateTime NullDate { get; set; } = DateTime.MinValue; public String DateFormat { get; set; } = "d-MMM-yy"; public String DateTimeSeparator { get; set; } = " "; public String TimeFormat { get; set; } = "HH:mm"; public DataValue.DataType DataType { get; set; } = DataValue.DataType.String; public bool IsNumeric { get { return DataType == DataValue.DataType.Long || DataType == DataValue.DataType.Double || DataType == DataValue.DataType.Decimal; } } public bool Resizable { get; set; } = true; public bool WrapText { get; set; } = false; public bool Hidden { get; set; } = false; public bool Frozen { get; set; } = false; // Drop-down configuration. public List Values { get; } = new List(); /// User must pick from the supplied list (default: true). Non-exclusive lists (editable) have same value and display members. public bool ExclusiveValues { get; set; } = true; /// User entered values (Exclusive=false) are converted if corresponding value found in list. public bool MatchCase { get; set; } = true; // Edit configuration. public int MaxLength { get; set; } = 0; public bool ReadOnly { get; set; } = false; public bool Uppercase { get; set; } = false; public bool Lowercase { get; set; } = false; public bool Password { get; set; } = false; // Sort attributes. /// Sort mode. Default: 'On'. public ColumnSortMode SortMode { get; set; } = ColumnSortMode.On; /// Data value name for sort (fully qualified). public String SortDataName { get; set; } = String.Empty; public bool SortAscending { get; set; } = true; // Property setters that can be chained with construction. public ColumnDfn SetSortDescending() { SortAscending = false; return this; } //------------------------------------------------------------------- // Population. public void Set (String caption, int width, int dbColumn = 0, String dataName = "", String sortDataName = "", DataValue.DataType dataType = DataValue.DataType.String, ColumnAlignment alignment = ColumnAlignment.Default) { this.Caption = caption; this.Width = width; this.DbColumn = dbColumn; this.DataName = dataName; this.SortDataName = sortDataName; this.DataType = dataType; this.Alignment = alignment; } //------------------------------------------------------------------- // Formatting. public String FormatValue(NamedValue.Map values) { String value = String.Empty; if (DataName.IsEmpty()) return value; DataValue dataValue = values.GetValue(DataName); if (dataValue == null || !dataValue.IsValid()) return value; return FormatValue(dataValue); } public String FormatValue(DataValue dataValue) { String value = String.Empty; if (dataValue == null || !dataValue.IsValid()) return value; switch (DataType) { case DataValue.DataType.Date: if (dataValue.IsDate()) { DateTime date = dataValue.AsDate(); if (DateUtil.IsValid(date, NullDate)) value = date.Format(DateFormat); } else value = dataValue.ToString(); break; case DataValue.DataType.Time: if (dataValue.IsTime()) { DateTime time = dataValue.AsTime(); if (DateUtil.IsValid(time, NullDate)) value = time.Format(TimeFormat); } else value = dataValue.ToString(); break; case DataValue.DataType.DateTime: if (dataValue.IsDateTime()) { DateTime dateTime = dataValue.AsDateTime(); if (DateUtil.IsValid(dateTime, NullDate)) value = dateTime.Format(DateFormat + DateTimeSeparator + TimeFormat); } else value = dataValue.ToString(); break; case DataValue.DataType.Long: if (dataValue.IsNumeric()) { long longValue = 0; if (dataValue.GetDataType() == DataValue.DataType.Long) longValue = dataValue.AsLong(); else longValue = (long)dataValue.ToNumeric(); if (longValue != 0 || ShowZeroValues) value = longValue.ToString(); } else value = dataValue.ToString(); break; case DataValue.DataType.Double: if (dataValue.IsNumeric()) { double doubleValue = dataValue.ToNumeric(); if (doubleValue != 0.0 || ShowZeroValues) value = StringUtil.FormatNumeric(doubleValue, NumDecimals, 0, NumThousands); } else value = dataValue.ToString(); break; case DataValue.DataType.Decimal: if (dataValue.IsNumeric()) { decimal decimalValue = 0.0m; if (dataValue.GetDataType() == DataValue.DataType.Decimal) decimalValue = dataValue.AsDecimal(); else decimalValue = (decimal)dataValue.ToNumeric(); if (decimalValue != 0.0m || ShowZeroValues) value = StringUtil.FormatNumeric(decimalValue, NumDecimals, 0, NumThousands); } else value = dataValue.ToString(); break; case DataValue.DataType.Bool: switch (BoolFormat) { case ColumnBoolFormat.YorN: value = (dataValue.ToBool() ? "Y" : "N"); break; case ColumnBoolFormat.Y: value = (dataValue.ToBool() ? "Y" : ""); break; case ColumnBoolFormat.N: value = (dataValue.ToBool() ? "" : "N"); break; case ColumnBoolFormat.TrueFalse: value = (dataValue.ToBool() ? "True" : "False"); break; default: value = (dataValue.ToBool() ? "1" : "0"); break; } break; case DataValue.DataType.TriState: switch (TriStateFormat) { case ColumnTriStateFormat.RAG: if (dataValue.AsTriState() == DataValue.TriStateType.Red) value = "R"; else if (dataValue.AsTriState() == DataValue.TriStateType.Amber) value = "A"; else if (dataValue.AsTriState() == DataValue.TriStateType.Green) value = "G"; else if (dataValue.AsTriState() == DataValue.TriStateType.Blue) value = "B"; else if (dataValue.AsTriState() == DataValue.TriStateType.Purple) value = "P"; else if (dataValue.AsTriState() == DataValue.TriStateType.Yellow) value = "Y"; else if (dataValue.AsTriState() == DataValue.TriStateType.Unknown) value = ""; break; default: if (dataValue.AsTriState() == DataValue.TriStateType.Red) value = "1"; else if (dataValue.AsTriState() == DataValue.TriStateType.Amber) value = "2"; else if (dataValue.AsTriState() == DataValue.TriStateType.Green) value = "3"; else if (dataValue.AsTriState() == DataValue.TriStateType.Blue) value = "4"; else if (dataValue.AsTriState() == DataValue.TriStateType.Purple) value = "5"; else if (dataValue.AsTriState() == DataValue.TriStateType.Yellow) value = "6"; else if (dataValue.AsTriState() == DataValue.TriStateType.Unknown) value = ""; break; } break; default: value = dataValue.ToString(); break; } return value.TrimEnd(' '); } //------------------------------------------------------------------- } //======================================================================= public static class ColumnDfnList { //------------------------------------------------------------------- // C o n t a i n s //------------------------------------------------------------------- /// Determine whether list contains column with Id matching specified enum value (integer comparison). public static bool HasId(this List values, T id) where T : struct, IConvertible { if (!(id is Enum)) throw new ArgumentException("HasId parameter must be an enumerated type. Type supplied: " + id.GetType().Name); return values.HasId((int)(id as IConvertible)); } /// Determine whether list contains column with Id matching specified integer value. public static bool HasId(this List values, int id) { foreach (var column in values) if (column.Id == id) return true; return false; } /// Determine whether list contains column with Db Column matching specified enum value (integer comparison). public static bool HasDbColumn(this List values, T id) where T : struct, IConvertible { if (!(id is Enum)) throw new ArgumentException("HasDbColumn parameter must be an enumerated type. Type supplied: " + id.GetType().Name); return values.HasDbColumn((int)(id as IConvertible)); } /// Determine whether list contains column with Db Column matching specified integer value. public static bool HasDbColumn(this List values, int dbColumn) { foreach (var column in values) if (column.DbColumn == dbColumn) return true; return false; } //------------------------------------------------------------------- // Columns in list. public static int GetPosForId(this List values, T id) where T : struct, IConvertible { if (!(id is Enum)) throw new ArgumentException("HasId parameter must be an enumerated type. Type supplied: " + id.GetType().Name); return values.GetPosForId((int)(id as IConvertible)); } public static int GetPosForId(this List values, int id) { for (int pos = 0; pos < values.Count; pos++) if (values[pos].Id == id) return pos + 1; return 0; } public static List GetColumnsWithNonZeroWidth(this List values) { return values.FindAll(item => item.Width > 0); } //------------------------------------------------------------------- // C o l u m n W i d t h s //------------------------------------------------------------------- // Combined width of all columns. /// Get the width of all non-zero width columns separated by the specified spacing public static int GetFullWidth(this List values, int columnSpacing) { List columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth(); if (columnsNonZeroWidth.Count < 1) return 0; int width = 0; foreach (var column in values) width += column.Width; return width + (columnSpacing * (columnsNonZeroWidth.Count - 1)); } // Column widths are adjusted to keep their relative width constant. /// Expand/contract non-zero width columns to fill the specified width with the specified space in between each. public static bool FillWidth(this List values, long width, int columnSpacing) { List columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth(); if (columnsNonZeroWidth.Count < 1) return false; long fullWidthNoSpacing = values.GetFullWidth(0); if (fullWidthNoSpacing < 1) return false; double columnWidthFactor = ((double)width - (columnSpacing * (columnsNonZeroWidth.Count - 1))) / fullWidthNoSpacing; foreach (ColumnDfn column in columnsNonZeroWidth) column.Width = Math.Max(1, (int)Math.Ceiling(column.Width * columnWidthFactor)); return true; } /// Calculate the left position for each non-zero width column with the specified spacing. public static int[] GetColumnLeftPositions(this List values, int leftPosition, int columnSpacing) { List columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth(); if (columnsNonZeroWidth.Count < 1) return new int[0]; int[] columnPositions = new int[columnsNonZeroWidth.Count]; int columnLeftPos = leftPosition; for (int columnIdx = 0; columnIdx < columnsNonZeroWidth.Count; columnIdx++) { ColumnDfn column = columnsNonZeroWidth[columnIdx]; columnPositions[columnIdx] = columnLeftPos; columnLeftPos += column.Width + columnSpacing; } return columnPositions; } /// /// Calculate the horizontal print position for each non-zero width column with the specified spacing. /// NB Print position will take alignment (left/centre/right) into consideration. /// public static int[] GetColumnPrintPositions(this List values, int leftPosition, int columnSpacing) { List columnsNonZeroWidth = values.GetColumnsWithNonZeroWidth(); if (columnsNonZeroWidth.Count < 1) return new int[0]; int[] columnPositions = new int[columnsNonZeroWidth.Count]; int columnLeftPos = leftPosition; for (int columnIdx = 0; columnIdx < columnsNonZeroWidth.Count; columnIdx++) { ColumnDfn column = columnsNonZeroWidth[columnIdx]; int columnPos = columnLeftPos; if (column.Alignment == ColumnAlignment.Center) columnPos = columnLeftPos + (column.Width / 2); else if (column.Alignment == ColumnAlignment.Right) columnPos = columnLeftPos + column.Width; columnPositions[columnIdx] = columnPos; columnLeftPos += column.Width + columnSpacing; } return columnPositions; } //------------------------------------------------------------------- // E x t r a c t C o n t e n t s //------------------------------------------------------------------- /// Compile list of captions from the columns. public static List GetCaptions(this List values) { List captions = new List(); foreach (ColumnDfn column in values) captions.Add(StringUtil.EmptyOnNull(column.Caption)); return captions; } /// /// Convert column list to strings representing sort columns. /// NB Column name is populated as DataName and may not be the same as used in a database query in all cases. /// public static List GetSortColumns(this List values) { List sortColumns = new List(); foreach (ColumnDfn column in values) sortColumns.Add(column.DataName + (column.SortAscending ? " ASC" : " DESC")); return sortColumns; } //------------------------------------------------------------------- } //======================================================================= }