using System; using System.Globalization; namespace Connected { /// /// A universal T to string binding converter /// public class DefaultConverter : ToStringConverter { public DefaultConverter() { } protected override string? ConvertValue(T? value) { return ConvertToString(value); } protected override T? ConvertValueBack(string? value) { return ConvertFromString(value); } public string DefaultTimeSpanFormat { get; set; } = "c"; protected virtual T ConvertFromString(string value) { try { // string if (typeof(T) == typeof(string)) return (T)(object)value; // this is important, or otherwise all the TryParse down there might fail. if (string.IsNullOrEmpty(value)) return default(T); // char else if (typeof(T) == typeof(char) || typeof(T) == typeof(char?)) { return (T)(object)value[0]; } // bool else if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?)) { var lowerValue = value.ToLowerInvariant(); if (lowerValue is "true" or "on") return (T)(object)true; if (lowerValue is "false" or "off") return (T)(object)false; TriggerError("Not a valid boolean"); } // sbyte else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?)) { if (sbyte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // byte else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?)) { if (byte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // short else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?)) { if (short.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // ushort else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?)) { if (ushort.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // int else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?)) { if (int.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // uint else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?)) { if (uint.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // long else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?)) { if (long.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // ulong else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?)) { if (ulong.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // float else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?)) { if (float.TryParse(value, NumberStyles.Any, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // double else if (typeof(T) == typeof(double) || typeof(T) == typeof(double?)) { if (double.TryParse(value, NumberStyles.Any, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // decimal else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?)) { if (decimal.TryParse(value, NumberStyles.Any, Culture, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid number"); } // guid else if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?)) { if (Guid.TryParse(value, out var parsedValue)) return (T)(object)parsedValue; TriggerError("Not a valid GUID"); } // enum else if (IsNullableEnum(typeof(T))) { var enum_type = Nullable.GetUnderlyingType(typeof(T)); if (Enum.TryParse(enum_type, value, out var parsedValue)) return (T)parsedValue; TriggerError("Not a value of " + enum_type.Name); } else if (typeof(T).IsEnum) { if (Enum.TryParse(typeof(T), value, out var parsedValue)) return (T)parsedValue; TriggerError("Not a value of " + typeof(T).Name); } // datetime else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?)) { try { return (T)(object)DateTime.ParseExact(value, Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); } catch (FormatException) { TriggerError("Not a valid date time"); } } // timespan else if (typeof(T) == typeof(TimeSpan) || typeof(T) == typeof(TimeSpan?)) { try { return (T)(object)TimeSpan.ParseExact(value, Format ?? DefaultTimeSpanFormat, Culture); } catch (FormatException) { TriggerError("Not a valid time span"); } } else { TriggerError($"Conversion to type {typeof(T)} not implemented"); } } catch (Exception e) { TriggerError("Conversion error: " + e.Message); } return default(T); } protected virtual string ConvertToString(T arg) { if (arg == null) return null; // <-- this catches all nullable values which are null. no nullchecks necessary below! try { // string if (typeof(T) == typeof(string)) return (string)(object)arg; // char if (typeof(T) == typeof(char)) return ((char)(object)arg).ToString(Culture); if (typeof(T) == typeof(char?)) return ((char?)(object)arg).Value.ToString(Culture); // bool if (typeof(T) == typeof(bool)) return ((bool)(object)arg).ToString(CultureInfo.InvariantCulture); if (typeof(T) == typeof(bool?)) return ((bool?)(object)arg).Value.ToString(CultureInfo.InvariantCulture); // sbyte if (typeof(T) == typeof(sbyte)) return ((sbyte)(object)arg).ToString(Format, Culture); if (typeof(T) == typeof(sbyte?)) return ((sbyte?)(object)arg).Value.ToString(Format, Culture); // byte if (typeof(T) == typeof(byte)) return ((byte)(object)arg).ToString(Format, Culture); if (typeof(T) == typeof(byte?)) return ((byte?)(object)arg).Value.ToString(Format, Culture); // short if (typeof(T) == typeof(short)) return ((short)(object)arg).ToString(Format, Culture); if (typeof(T) == typeof(short?)) return ((short?)(object)arg).Value.ToString(Format, Culture); // ushort if (typeof(T) == typeof(ushort)) return ((ushort)(object)arg).ToString(Format, Culture); if (typeof(T) == typeof(ushort?)) return ((ushort?)(object)arg).Value.ToString(Format, Culture); // int else if (typeof(T) == typeof(int)) return ((int)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(int?)) return ((int?)(object)arg).Value.ToString(Format, Culture); // uint else if (typeof(T) == typeof(uint)) return ((uint)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(uint?)) return ((uint?)(object)arg).Value.ToString(Format, Culture); // long else if (typeof(T) == typeof(long)) return ((long)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(long?)) return ((long?)(object)arg).Value.ToString(Format, Culture); // ulong else if (typeof(T) == typeof(ulong)) return ((ulong)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(ulong?)) return ((ulong?)(object)arg).Value.ToString(Format, Culture); // float else if (typeof(T) == typeof(float)) return ((float)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(float?)) return ((float?)(object)arg).Value.ToString(Format, Culture); // double else if (typeof(T) == typeof(double)) return ((double)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(double?)) return ((double?)(object)arg).Value.ToString(Format, Culture); // decimal else if (typeof(T) == typeof(decimal)) return ((decimal)(object)arg).ToString(Format, Culture); else if (typeof(T) == typeof(decimal?)) return ((decimal?)(object)arg).Value.ToString(Format, Culture); // guid else if (typeof(T) == typeof(Guid)) { var value = (Guid)(object)arg; return value.ToString(); } else if (typeof(T) == typeof(Guid?)) { var value = (Guid?)(object)arg; return value.Value.ToString(); } // enum else if (IsNullableEnum(typeof(T))) { var value = (Enum)(object)arg; return value.ToString(); } else if (typeof(T).IsEnum) { var value = (Enum)(object)arg; return value.ToString(); } // datetime else if (typeof(T) == typeof(DateTime)) { var value = (DateTime)(object)arg; return value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); } else if (typeof(T) == typeof(DateTime?)) { var value = (DateTime?)(object)arg; return value.Value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); } // timespan else if (typeof(T) == typeof(TimeSpan)) { var value = (TimeSpan)(object)arg; return value.ToString(Format ?? DefaultTimeSpanFormat, Culture); } else if (typeof(T) == typeof(TimeSpan?)) { var value = (TimeSpan?)(object)arg; return value.Value.ToString(Format ?? DefaultTimeSpanFormat, Culture); } return arg.ToString(); } catch (FormatException e) { TriggerError("Conversion error: " + e.Message); return null; } } public static bool IsNullableEnum(Type t) { var u = Nullable.GetUnderlyingType(t); return (u != null) && u.IsEnum; } } }