/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see .
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using dnSpy.Contracts.DnSpy.Properties;
using dnSpy.Contracts.Utilities;
namespace dnSpy.Contracts.MVVM {
struct CachedValidationError {
readonly Func checkError;
bool errorMsgValid;
string? errorMsg;
public bool HasError {
get {
CheckError();
return !string.IsNullOrEmpty(errorMsg);
}
}
public string? ErrorMessage {
get {
CheckError();
return errorMsg;
}
}
public CachedValidationError(Func checkError) {
this.checkError = checkError ?? throw new ArgumentNullException(nameof(checkError));
errorMsgValid = false;
errorMsg = null;
}
public void Invalidate() => errorMsgValid = false;
void CheckError() {
if (errorMsgValid)
return;
errorMsg = checkError();
errorMsgValid = true;
}
}
///
/// Data field base class
///
public abstract class DataFieldVM : ViewModelBase {
readonly Action onUpdated;
CachedValidationError cachedError;
///
/// Gets/sets the value
///
public abstract object? ObjectValue { get; set; }
///
/// Gets the string representation of the value. This could be an invalid string. Use
/// to check whether it's valid.
///
public string StringValue {
get => stringValue;
set {
if (value is null)
throw new ArgumentNullException(nameof(value));
if (stringValue != value)
ForceWriteStringValue(value);
}
}
string stringValue = string.Empty;
///
/// Must only be called from the constructor
///
/// Initial
protected void WriteStringValueFromConstructor(string value) {
Debug.Assert(stringValue == string.Empty);
stringValue = value;
}
///
/// Force writing a new even if nothing changed
///
/// New value
protected void ForceWriteStringValue(string value) {
stringValue = value;
cachedError.Invalidate();
OnStringValueChanged();
OnPropertyChanged(nameof(StringValue));
onUpdated(this);
}
///
/// Revalidates the field for errors
///
protected void Revalidate() {
cachedError.Invalidate();
HasErrorUpdated();
}
///
/// Called when gets updated
///
protected virtual void OnStringValueChanged() { }
///
/// true if the value is null ( is empty)
///
public bool IsNull => string.IsNullOrWhiteSpace(StringValue);
///
/// Constructor
///
/// Called when value gets updated
protected DataFieldVM(Action onUpdated) {
this.onUpdated = onUpdated ?? throw new ArgumentNullException(nameof(onUpdated));
cachedError = new CachedValidationError(() => Validate());
}
///
/// Validates the data. Returns null or an empty string if there was no error,
/// or an error string that can be shown to the user.
///
///
protected abstract string? Validate();
///
/// Converts the string to the target value. Returns null or an empty string if
/// there were no errors, else an error string that can be shown to the user.
///
/// Result
///
public abstract string? ConvertToObjectValue(out object value);
///
/// Checks the string for errors
///
/// Property name
///
protected override string? Verify(string columnName) {
if (columnName == nameof(StringValue))
return cachedError.ErrorMessage;
return string.Empty;
}
///
/// true if there's at least one error
///
public override bool HasError => cachedError.HasError;
}
///
/// Data field base class
///
/// Type of data
public abstract class DataFieldVM : DataFieldVM {
///
/// Gets/sets the value
///
public override object? ObjectValue {
get => Value!;
set => Value = (T)value!;
}
///
/// Gets/sets the value
///
public T Value {
get {
var s = ConvertToValue(out var value);
if (string.IsNullOrEmpty(s))
return value;
throw new FormatException(s);
}
set => SetValue(value);
}
///
/// Constructor
///
/// Called when value gets updated
protected DataFieldVM(Action onUpdated)
: base(onUpdated) {
}
///
/// Must only be called from the constructor
///
/// Initial value
protected void SetValueFromConstructor(T value) => WriteStringValueFromConstructor(OnNewValue(value));
///
/// Writes a new value
///
/// Value
protected void SetValue(T value) => StringValue = OnNewValue(value);
///
/// Converts to a string
///
/// New value
///
protected abstract string OnNewValue(T value);
///
/// Converts the current string to the real value. Returns null or an empty string if
/// there were no errors, else an error string that can be shown to the user.
///
/// Result
///
protected abstract string? ConvertToValue(out T value);
///
/// Converts the string to the target value. Returns null or an empty string if
/// there were no errors, else an error string that can be shown to the user.
///
/// Result
///
public override string? ConvertToObjectValue(out object value) {
var error = ConvertToValue(out var v);
Debug2.Assert(v is not null);
value = v;
return error;
}
///
/// Validates the data. Returns null or an empty string if there was no error,
/// or an error string that can be shown to the user.
///
///
protected override string? Validate() {
try {
return ConvertToValue(out var value);
}
catch (Exception ex) {
Debug.Fail("Exception caught in Validate(). ConvertToValue() should return an error string instead of throwing for performance reasons! Throwing is SLOOOOW!");
if (!string.IsNullOrEmpty(ex.Message))
return ex.Message;
return string.Format(dnSpy_Contracts_DnSpy_Resources.CouldNotConvert, StringValue);
}
}
}
///
/// Number base class
///
/// Real type
/// If real type is a nullable type, this should be non-nullable type
public abstract class NumberDataFieldVM : DataFieldVM {
///
/// true to always use decimal, false to never use decimal (except if it's just one digit),
/// and null to use decimal or hex depending on what number it is.
///
public bool? UseDecimal {
get => useDecimal;
set {
if (useDecimal != value) {
useDecimal = value;
if (!HasError)
ForceWriteStringValue(OnNewValue(Value));
}
}
}
bool? useDecimal;
///
/// Gets/sets the minimum value
///
public U Min {
get => min;
set {
min = value;
Revalidate();
}
}
U min;
///
/// Gets/sets the maximum value
///
public U Max {
get => max;
set {
max = value;
Revalidate();
}
}
U max;
///
/// Constructor
///
/// Called when value gets updated
/// Minimum value
/// Maximum value
/// true to use decimal, false to use hex, or null if it depends on the value
protected NumberDataFieldVM(Action onUpdated, U min, U max, bool? useDecimal)
: base(onUpdated) {
this.min = min;
this.max = max;
this.useDecimal = useDecimal;
}
}
///
/// Nullable
///
public class NullableGuidVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public NullableGuidVM(Action onUpdated)
: this(null, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public NullableGuidVM(Guid? value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(Guid? value) => value is null ? string.Empty : value.Value.ToString();
///
protected override string? ConvertToValue(out Guid? value) {
string? error = null;
if (IsNull)
value = null;
else
value = ParseGuid(StringValue, out error);
return error;
}
internal static Guid ParseGuid(string s, out string? error) {
if (Guid.TryParse(s, out var res)) {
error = null;
return res;
}
error = dnSpy_Contracts_DnSpy_Resources.InvalidGuid;
return Guid.Empty;
}
}
///
/// Hex string
///
public class HexStringVM : DataFieldVM> {
///
/// Gets/sets whether to use upper case hex digits
///
public bool UppercaseHex {
get => uppercaseHex;
set => uppercaseHex = value;
}
bool uppercaseHex = true;
///
/// Constructor
///
/// Called when value gets updated
public HexStringVM(Action onUpdated)
: this(null, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public HexStringVM(IList? value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value!);// can be null, but we can't use 'T?'
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ByteArrayToString(value, UppercaseHex);
///
protected override string? ConvertToValue(out IList value) {
// It will be null only when there's an error, so force it with '!'. 'value' can't be nullable since it's overriding a generic method
// with a generic parameter that can be a struct or a class.
value = SimpleTypeConverter.ParseByteArray(StringValue, out var error)!;
return error;
}
}
///
/// Nullable
///
public class NullableBooleanVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public NullableBooleanVM(Action onUpdated)
: this(false, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public NullableBooleanVM(bool? value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(bool? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value);
///
protected override string? ConvertToValue(out bool? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseBoolean(StringValue, out error);
return error;
}
}
///
/// Nullable
///
public class NullableSByteVM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableSByteVM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableSByteVM(sbyte? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, sbyte.MinValue, sbyte.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(sbyte? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out sbyte? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseSByte(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableByteVM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableByteVM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableByteVM(byte? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, byte.MinValue, byte.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(byte? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out byte? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseByte(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableInt16VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableInt16VM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableInt16VM(short? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, short.MinValue, short.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(short? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out short? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseInt16(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableUInt16VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableUInt16VM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableUInt16VM(ushort? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, ushort.MinValue, ushort.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(ushort? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out ushort? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseUInt16(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableInt32VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableInt32VM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableInt32VM(int? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, int.MinValue, int.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(int? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out int? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseInt32(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableUInt32VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableUInt32VM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableUInt32VM(uint? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, uint.MinValue, uint.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(uint? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out uint? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseUInt32(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableInt64VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableInt64VM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableInt64VM(long? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, long.MinValue, long.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(long? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out long? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseInt64(StringValue, Min, Max, out error);
return error;
}
}
///
/// Nullable
///
public class NullableUInt64VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableUInt64VM(Action onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public NullableUInt64VM(ulong? value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, ulong.MinValue, ulong.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(ulong? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out ulong? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseUInt64(StringValue, Min, Max, out error);
return error;
}
}
///
///
///
public class BooleanVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public BooleanVM(Action onUpdated)
: this(false, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public BooleanVM(bool value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(bool value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out bool value) {
value = SimpleTypeConverter.ParseBoolean(StringValue, out var error);
return error;
}
}
///
///
///
public class CharVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public CharVM(Action onUpdated)
: this((char)0, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public CharVM(char value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(char value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out char value) {
value = SimpleTypeConverter.ParseChar(StringValue, out var error);
return error;
}
}
///
///
///
public class ByteVM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public ByteVM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public ByteVM(byte value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, byte.MinValue, byte.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(byte value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out byte value) {
value = SimpleTypeConverter.ParseByte(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class UInt16VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt16VM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt16VM(ushort value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, ushort.MinValue, ushort.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(ushort value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out ushort value) {
value = SimpleTypeConverter.ParseUInt16(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class UInt32VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt32VM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt32VM(uint value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, uint.MinValue, uint.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(uint value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out uint value) {
value = SimpleTypeConverter.ParseUInt32(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class UInt64VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt64VM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt64VM(ulong value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, ulong.MinValue, ulong.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(ulong value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out ulong value) {
value = SimpleTypeConverter.ParseUInt64(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class SByteVM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public SByteVM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public SByteVM(sbyte value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, sbyte.MinValue, sbyte.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(sbyte value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out sbyte value) {
value = SimpleTypeConverter.ParseSByte(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class Int16VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int16VM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int16VM(short value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, short.MinValue, short.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(short value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out short value) {
value = SimpleTypeConverter.ParseInt16(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class Int32VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int32VM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int32VM(int value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, int.MinValue, int.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(int value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out int value) {
value = SimpleTypeConverter.ParseInt32(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class Int64VM : NumberDataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int64VM(Action onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int64VM(long value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, long.MinValue, long.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(long value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out long value) {
value = SimpleTypeConverter.ParseInt64(StringValue, Min, Max, out var error);
return error;
}
}
///
///
///
public class SingleVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public SingleVM(Action onUpdated)
: this(0, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public SingleVM(float value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(float value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out float value) {
value = SimpleTypeConverter.ParseSingle(StringValue, out var error);
return error;
}
}
///
///
///
public class DoubleVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public DoubleVM(Action onUpdated)
: this(0, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public DoubleVM(double value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(double value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out double value) {
value = SimpleTypeConverter.ParseDouble(StringValue, out var error);
return error;
}
}
///
///
///
public class StringVM : DataFieldVM {
readonly bool allowNullString;
///
/// Constructor
///
/// Called when value gets updated
/// true to allow null strings
public StringVM(Action onUpdated, bool allowNullString = false)
: this(string.Empty, onUpdated, allowNullString) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to allow null strings
public StringVM(string value, Action onUpdated, bool allowNullString = false)
: base(onUpdated) {
this.allowNullString = allowNullString;
SetValueFromConstructor(value);
}
///
protected override string OnNewValue(string value) => SimpleTypeConverter.ToString(value, allowNullString);
///
protected override string? ConvertToValue(out string value) {
value = SimpleTypeConverter.ParseString(StringValue, allowNullString, out var error)!;
return error;
}
}
///
///
///
public class DecimalVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public DecimalVM(Action onUpdated)
: this(0, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public DecimalVM(decimal value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(decimal value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out decimal value) {
value = SimpleTypeConverter.ParseDecimal(StringValue, out var error);
return error;
}
}
///
///
///
public class DateTimeVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public DateTimeVM(Action onUpdated)
: this(DateTime.Now, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public DateTimeVM(DateTime value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(DateTime value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out DateTime value) {
value = SimpleTypeConverter.ParseDateTime(StringValue, out var error);
return error;
}
}
///
///
///
public class TimeSpanVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public TimeSpanVM(Action onUpdated)
: this(TimeSpan.Zero, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public TimeSpanVM(TimeSpan value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(TimeSpan value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out TimeSpan value) {
value = SimpleTypeConverter.ParseTimeSpan(StringValue, out var error);
return error;
}
}
///
///
///
public class GuidVM : DataFieldVM {
///
/// Constructor
///
/// Called when value gets updated
public GuidVM(Action onUpdated)
: this(new Guid(), onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public GuidVM(Guid value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(Guid value) => value.ToString();
///
protected override string? ConvertToValue(out Guid value) {
value = NullableGuidVM.ParseGuid(StringValue, out var error);
return error;
}
}
///
/// List of s
///
public class BooleanListDataFieldVM : DataFieldVM> {
///
/// Constructor
///
/// Called when value gets updated
public BooleanListDataFieldVM(Action onUpdated)
: this(Array.Empty(), onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public BooleanListDataFieldVM(IList value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseBooleanList(StringValue, out var error)!;
return error;
}
}
///
/// List of s
///
public class CharListDataFieldVM : DataFieldVM> {
///
/// Constructor
///
/// Called when value gets updated
public CharListDataFieldVM(Action onUpdated)
: this(Array.Empty(), onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public CharListDataFieldVM(IList value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseCharList(StringValue, out var error)!;
return error;
}
}
///
/// List of s
///
public class ByteListDataFieldVM : NumberDataFieldVM, byte> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public ByteListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public ByteListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, byte.MinValue, byte.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseByteList(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class UInt16ListDataFieldVM : NumberDataFieldVM, ushort> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt16ListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt16ListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, ushort.MinValue, ushort.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseUInt16List(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class UInt32ListDataFieldVM : NumberDataFieldVM, uint> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt32ListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt32ListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, uint.MinValue, uint.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseUInt32List(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class UInt64ListDataFieldVM : NumberDataFieldVM, ulong> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt64ListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public UInt64ListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, ulong.MinValue, ulong.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseUInt64List(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class SByteListDataFieldVM : NumberDataFieldVM, sbyte> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public SByteListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public SByteListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, sbyte.MinValue, sbyte.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseSByteList(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class Int16ListDataFieldVM : NumberDataFieldVM, short> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int16ListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int16ListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, short.MinValue, short.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseInt16List(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class Int32ListDataFieldVM : NumberDataFieldVM, int> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int32ListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int32ListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, int.MinValue, int.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseInt32List(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class Int64ListDataFieldVM : NumberDataFieldVM, long> {
///
/// Constructor
///
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int64ListDataFieldVM(Action onUpdated, bool? useDecimal = null)
: this(Array.Empty(), onUpdated, useDecimal) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to use decimal, false to use hex, or null if it depends on the value
public Int64ListDataFieldVM(IList value, Action onUpdated, bool? useDecimal = null)
: base(onUpdated, long.MinValue, long.MaxValue, useDecimal) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseInt64List(StringValue, Min, Max, out var error)!;
return error;
}
}
///
/// List of s
///
public class SingleListDataFieldVM : DataFieldVM> {
///
/// Constructor
///
/// Called when value gets updated
public SingleListDataFieldVM(Action onUpdated)
: this(Array.Empty(), onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public SingleListDataFieldVM(IList value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseSingleList(StringValue, out var error)!;
return error;
}
}
///
/// List of s
///
public class DoubleListDataFieldVM : DataFieldVM> {
///
/// Constructor
///
/// Called when value gets updated
public DoubleListDataFieldVM(Action onUpdated)
: this(Array.Empty(), onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public DoubleListDataFieldVM(IList value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseDoubleList(StringValue, out var error)!;
return error;
}
}
///
/// List of s
///
public class StringListDataFieldVM : DataFieldVM> {
readonly bool allowNullString;
///
/// Constructor
///
/// Called when value gets updated
/// true to allow null strings
public StringListDataFieldVM(Action onUpdated, bool allowNullString = true)
: this(Array.Empty(), onUpdated, allowNullString) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
/// true to allow null strings
public StringListDataFieldVM(IList value, Action onUpdated, bool allowNullString = true)
: base(onUpdated) {
this.allowNullString = allowNullString;
SetValueFromConstructor(value);
}
///
protected override string OnNewValue(IList value) => SimpleTypeConverter.ToString(value, allowNullString);
///
protected override string? ConvertToValue(out IList value) {
value = SimpleTypeConverter.ParseStringList(StringValue, allowNullString, out var error)!;
return error;
}
}
///
/// Uses the default converter to convert the type to/from a string
///
/// Type
public class DefaultConverterVM : DataFieldVM {
static readonly TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
static DefaultConverterVM() {
if (!converter.CanConvertTo(null, typeof(string)))
throw new InvalidOperationException($"Converter can't convert a {typeof(T)} to a string");
if (!converter.CanConvertFrom(null, typeof(string)))
throw new InvalidOperationException($"Converter can't convert a string to a {typeof(T)}");
}
///
/// Constructor
///
/// Called when value gets updated
public DefaultConverterVM(Action onUpdated)
: this(default!, onUpdated) {
}
///
/// Constructor
///
/// Initial value
/// Called when value gets updated
public DefaultConverterVM(T value, Action onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
///
protected override string OnNewValue(T value) => (string)converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(string));
///
protected override string? ConvertToValue(out T value) {
string error;
try {
value = (T)converter.ConvertFrom(null, CultureInfo.InvariantCulture, StringValue);
error = string.Empty;
}
catch (Exception ex) {
value = default!;
error = string.Format(dnSpy_Contracts_DnSpy_Resources.ValueMustBeType, typeof(T).FullName, ex.Message);
}
return error;
}
}
}