433 lines
17 KiB
C#
433 lines
17 KiB
C#
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
|
|
{
|
|
/// <summary>Text: 'Y' (true) or 'N' (false).</summary>
|
|
YorN,
|
|
/// <summary>Text: 'Y' (true).</summary>
|
|
Y,
|
|
/// <summary>Text: 'N' (false).</summary>
|
|
N,
|
|
/// <summary>Text: '1' (true) or '0' (false).</summary>
|
|
OneOrZero,
|
|
/// <summary>Text: 'True' or 'False'.</summary>
|
|
TrueFalse,
|
|
/// <summary>Graphic icons: Tick (true) or Cross (false).</summary>
|
|
TickCross,
|
|
/// <summary>Graphic icon: Tick (true).</summary>
|
|
Tick,
|
|
/// <summary>Graphic icon: Cross (false).</summary>
|
|
Cross,
|
|
/// <summary>Graphic icons (inverted): Tick (false) or Cross (true).</summary>
|
|
TickCross_Inv,
|
|
/// <summary>Graphic icon (inverted): Tick (false).</summary>
|
|
Tick_Inv,
|
|
/// <summary>Graphic icon (inverted): Cross (true).</summary>
|
|
Cross_Inv
|
|
}
|
|
public enum ColumnTriStateFormat
|
|
{
|
|
/// <summary>Text: R, A, G etc.</summary>
|
|
RAG,
|
|
/// <summary>Text: 1, 2, 3 etc.</summary>
|
|
Numeric,
|
|
/// <summary>Graphic icons.</summary>
|
|
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;
|
|
/// <summary>Database column identity (DbDfn.ColumnIdx etc).</summary>
|
|
public int DbColumn { get; set; } = 0;
|
|
/// <summary>Column code - eg. for configuration options.</summary>
|
|
public String Code { get; set; } = String.Empty;
|
|
/// <summary>Column header text.</summary>
|
|
public String Caption { get; set; } = String.Empty;
|
|
/// <summary>Column width (device dependent).</summary>
|
|
public int Width { get; set; } = 0;
|
|
public ColumnAlignment Alignment { get; set; } = ColumnAlignment.Default;
|
|
/// <summary>Data value name (may be an alias).</summary>
|
|
public String DataName { get; set; } = String.Empty;
|
|
public byte NumDecimals { get; set; } = 2;
|
|
public char NumThousands { get; set; } = ',';
|
|
/// <summary>Default: Zero values hidden.</summary>
|
|
public bool ShowZeroValues { get; set; } = false;
|
|
public ColumnBoolFormat BoolFormat { get; set; } = ColumnBoolFormat.YorN;
|
|
public ColumnTriStateFormat TriStateFormat { get; set; } = ColumnTriStateFormat.RAG;
|
|
/// <summary>Null date - values on or before are considered invalid.</summary>
|
|
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<CodeDesc> Values { get; } = new List<CodeDesc>();
|
|
/// <summary>User must pick from the supplied list (default: true). Non-exclusive lists (editable) have same value and display members.</summary>
|
|
public bool ExclusiveValues { get; set; } = true;
|
|
/// <summary>User entered values (Exclusive=false) are converted if corresponding value found in list.</summary>
|
|
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.
|
|
/// <summary>Sort mode. Default: 'On'.</summary>
|
|
public ColumnSortMode SortMode { get; set; } = ColumnSortMode.On;
|
|
/// <summary>Data value name for sort (fully qualified).</summary>
|
|
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
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Determine whether list contains column with Id matching specified enum value (integer comparison).</summary>
|
|
public static bool HasId<T>(this List<ColumnDfn> 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));
|
|
}
|
|
/// <summary>Determine whether list contains column with Id matching specified integer value.</summary>
|
|
public static bool HasId(this List<ColumnDfn> values, int id)
|
|
{
|
|
foreach (var column in values)
|
|
if (column.Id == id) return true;
|
|
return false;
|
|
}
|
|
/// <summary>Determine whether list contains column with Db Column matching specified enum value (integer comparison).</summary>
|
|
public static bool HasDbColumn<T>(this List<ColumnDfn> 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));
|
|
}
|
|
/// <summary>Determine whether list contains column with Db Column matching specified integer value.</summary>
|
|
public static bool HasDbColumn(this List<ColumnDfn> values, int dbColumn)
|
|
{
|
|
foreach (var column in values)
|
|
if (column.DbColumn == dbColumn) return true;
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Columns in list.
|
|
public static int GetPosForId<T>(this List<ColumnDfn> 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<ColumnDfn> values, int id)
|
|
{
|
|
for (int pos = 0; pos < values.Count; pos++)
|
|
if (values[pos].Id == id) return pos + 1;
|
|
return 0;
|
|
}
|
|
public static List<ColumnDfn> GetColumnsWithNonZeroWidth(this List<ColumnDfn> 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.
|
|
/// <summary>Get the width of all non-zero width columns separated by the specified spacing</summary>
|
|
public static int GetFullWidth(this List<ColumnDfn> values, int columnSpacing)
|
|
{
|
|
List<ColumnDfn> 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.
|
|
/// <summary>Expand/contract non-zero width columns to fill the specified width with the specified space in between each.</summary>
|
|
public static bool FillWidth(this List<ColumnDfn> values, long width, int columnSpacing)
|
|
{
|
|
List<ColumnDfn> 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;
|
|
}
|
|
/// <summary>Calculate the left position for each non-zero width column with the specified spacing.</summary>
|
|
public static int[] GetColumnLeftPositions(this List<ColumnDfn> values, int leftPosition, int columnSpacing)
|
|
{
|
|
List<ColumnDfn> 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;
|
|
}
|
|
/// <summary>
|
|
/// Calculate the horizontal print position for each non-zero width column with the specified spacing.
|
|
/// <para>NB Print position will take alignment (left/centre/right) into consideration.</para>
|
|
/// </summary>
|
|
public static int[] GetColumnPrintPositions(this List<ColumnDfn> values, int leftPosition, int columnSpacing)
|
|
{
|
|
List<ColumnDfn> 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
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Compile list of captions from the columns.</summary>
|
|
public static List<String> GetCaptions(this List<ColumnDfn> values)
|
|
{
|
|
List<String> captions = new List<String>();
|
|
foreach (ColumnDfn column in values)
|
|
captions.Add(StringUtil.EmptyOnNull(column.Caption));
|
|
return captions;
|
|
}
|
|
/// <summary>
|
|
/// Convert column list to strings representing sort columns.
|
|
/// <para>NB Column name is populated as DataName and may not be the same as used in a database query in all cases.</para>
|
|
/// </summary>
|
|
public static List<String> GetSortColumns(this List<ColumnDfn> values)
|
|
{
|
|
List<String> sortColumns = new List<String>();
|
|
foreach (ColumnDfn column in values)
|
|
sortColumns.Add(column.DataName + (column.SortAscending ? " ASC" : " DESC"));
|
|
return sortColumns;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
|
|
//=======================================================================
|
|
}
|