1726 lines
59 KiB
C#
Raw Normal View History

2021-09-20 18:20:01 +02:00
/*
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 <http://www.gnu.org/licenses/>.
*/
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<string?> checkError;
bool errorMsgValid;
string? errorMsg;
public bool HasError {
get {
CheckError();
return !string.IsNullOrEmpty(errorMsg);
}
}
public string? ErrorMessage {
get {
CheckError();
return errorMsg;
}
}
public CachedValidationError(Func<string?> 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;
}
}
/// <summary>
/// Data field base class
/// </summary>
public abstract class DataFieldVM : ViewModelBase {
readonly Action<DataFieldVM> onUpdated;
CachedValidationError cachedError;
/// <summary>
/// Gets/sets the value
/// </summary>
public abstract object? ObjectValue { get; set; }
/// <summary>
/// Gets the string representation of the value. This could be an invalid string. Use
/// <see cref="Validate()"/> to check whether it's valid.
/// </summary>
public string StringValue {
get => stringValue;
set {
if (value is null)
throw new ArgumentNullException(nameof(value));
if (stringValue != value)
ForceWriteStringValue(value);
}
}
string stringValue = string.Empty;
/// <summary>
/// Must only be called from the constructor
/// </summary>
/// <param name="value">Initial <see cref="StringValue"/></param>
protected void WriteStringValueFromConstructor(string value) {
Debug.Assert(stringValue == string.Empty);
stringValue = value;
}
/// <summary>
/// Force writing a new <see cref="StringValue"/> even if nothing changed
/// </summary>
/// <param name="value">New value</param>
protected void ForceWriteStringValue(string value) {
stringValue = value;
cachedError.Invalidate();
OnStringValueChanged();
OnPropertyChanged(nameof(StringValue));
onUpdated(this);
}
/// <summary>
/// Revalidates the field for errors
/// </summary>
protected void Revalidate() {
cachedError.Invalidate();
HasErrorUpdated();
}
/// <summary>
/// Called when <see cref="StringValue"/> gets updated
/// </summary>
protected virtual void OnStringValueChanged() { }
/// <summary>
/// true if the value is null (<see cref="StringValue"/> is empty)
/// </summary>
public bool IsNull => string.IsNullOrWhiteSpace(StringValue);
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
protected DataFieldVM(Action<DataFieldVM> onUpdated) {
this.onUpdated = onUpdated ?? throw new ArgumentNullException(nameof(onUpdated));
cachedError = new CachedValidationError(() => Validate());
}
/// <summary>
/// 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.
/// </summary>
/// <returns></returns>
protected abstract string? Validate();
/// <summary>
/// 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.
/// </summary>
/// <param name="value">Result</param>
/// <returns></returns>
public abstract string? ConvertToObjectValue(out object value);
/// <summary>
/// Checks the string for errors
/// </summary>
/// <param name="columnName">Property name</param>
/// <returns></returns>
protected override string? Verify(string columnName) {
if (columnName == nameof(StringValue))
return cachedError.ErrorMessage;
return string.Empty;
}
/// <summary>
/// true if there's at least one error
/// </summary>
public override bool HasError => cachedError.HasError;
}
/// <summary>
/// Data field base class
/// </summary>
/// <typeparam name="T">Type of data</typeparam>
public abstract class DataFieldVM<T> : DataFieldVM {
/// <summary>
/// Gets/sets the value
/// </summary>
public override object? ObjectValue {
get => Value!;
set => Value = (T)value!;
}
/// <summary>
/// Gets/sets the value
/// </summary>
public T Value {
get {
var s = ConvertToValue(out var value);
if (string.IsNullOrEmpty(s))
return value;
throw new FormatException(s);
}
set => SetValue(value);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
protected DataFieldVM(Action<DataFieldVM> onUpdated)
: base(onUpdated) {
}
/// <summary>
/// Must only be called from the constructor
/// </summary>
/// <param name="value">Initial value</param>
protected void SetValueFromConstructor(T value) => WriteStringValueFromConstructor(OnNewValue(value));
/// <summary>
/// Writes a new value
/// </summary>
/// <param name="value">Value</param>
protected void SetValue(T value) => StringValue = OnNewValue(value);
/// <summary>
/// Converts <paramref name="value"/> to a string
/// </summary>
/// <param name="value">New value</param>
/// <returns></returns>
protected abstract string OnNewValue(T value);
/// <summary>
/// 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.
/// </summary>
/// <param name="value">Result</param>
/// <returns></returns>
protected abstract string? ConvertToValue(out T value);
/// <summary>
/// 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.
/// </summary>
/// <param name="value">Result</param>
/// <returns></returns>
public override string? ConvertToObjectValue(out object value) {
var error = ConvertToValue(out var v);
Debug2.Assert(v is not null);
value = v;
return error;
}
/// <summary>
/// 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.
/// </summary>
/// <returns></returns>
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);
}
}
}
/// <summary>
/// Number base class
/// </summary>
/// <typeparam name="T">Real type</typeparam>
/// <typeparam name="U">If real type is a nullable type, this should be non-nullable type</typeparam>
public abstract class NumberDataFieldVM<T, U> : DataFieldVM<T> {
/// <summary>
/// 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.
/// </summary>
public bool? UseDecimal {
get => useDecimal;
set {
if (useDecimal != value) {
useDecimal = value;
if (!HasError)
ForceWriteStringValue(OnNewValue(Value));
}
}
}
bool? useDecimal;
/// <summary>
/// Gets/sets the minimum value
/// </summary>
public U Min {
get => min;
set {
min = value;
Revalidate();
}
}
U min;
/// <summary>
/// Gets/sets the maximum value
/// </summary>
public U Max {
get => max;
set {
max = value;
Revalidate();
}
}
U max;
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="min">Minimum value</param>
/// <param name="max">Maximum value</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
protected NumberDataFieldVM(Action<DataFieldVM> onUpdated, U min, U max, bool? useDecimal)
: base(onUpdated) {
this.min = min;
this.max = max;
this.useDecimal = useDecimal;
}
}
/// <summary>
/// Nullable <see cref="Guid"/>
/// </summary>
public class NullableGuidVM : DataFieldVM<Guid?> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public NullableGuidVM(Action<DataFieldVM> onUpdated)
: this(null, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public NullableGuidVM(Guid? value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(Guid? value) => value is null ? string.Empty : value.Value.ToString();
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Hex string
/// </summary>
public class HexStringVM : DataFieldVM<IList<byte>> {
/// <summary>
/// Gets/sets whether to use upper case hex digits
/// </summary>
public bool UppercaseHex {
get => uppercaseHex;
set => uppercaseHex = value;
}
bool uppercaseHex = true;
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public HexStringVM(Action<DataFieldVM> onUpdated)
: this(null, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public HexStringVM(IList<byte>? value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value!);// can be null, but we can't use 'T?'
/// <inheritdoc/>
protected override string OnNewValue(IList<byte> value) => SimpleTypeConverter.ByteArrayToString(value, UppercaseHex);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<byte> 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;
}
}
/// <summary>
/// Nullable <see cref="bool"/>
/// </summary>
public class NullableBooleanVM : DataFieldVM<bool?> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public NullableBooleanVM(Action<DataFieldVM> onUpdated)
: this(false, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public NullableBooleanVM(bool? value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(bool? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value);
/// <inheritdoc/>
protected override string? ConvertToValue(out bool? value) {
string? error = null;
if (IsNull)
value = null;
else
value = SimpleTypeConverter.ParseBoolean(StringValue, out error);
return error;
}
}
/// <summary>
/// Nullable <see cref="sbyte"/>
/// </summary>
public class NullableSByteVM : NumberDataFieldVM<sbyte?, sbyte> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableSByteVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableSByteVM(sbyte? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, sbyte.MinValue, sbyte.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(sbyte? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="byte"/>
/// </summary>
public class NullableByteVM : NumberDataFieldVM<byte?, byte> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableByteVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableByteVM(byte? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, byte.MinValue, byte.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(byte? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="short"/>
/// </summary>
public class NullableInt16VM : NumberDataFieldVM<short?, short> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableInt16VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableInt16VM(short? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, short.MinValue, short.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(short? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="ushort"/>
/// </summary>
public class NullableUInt16VM : NumberDataFieldVM<ushort?, ushort> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableUInt16VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableUInt16VM(ushort? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, ushort.MinValue, ushort.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(ushort? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="int"/>
/// </summary>
public class NullableInt32VM : NumberDataFieldVM<int?, int> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableInt32VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableInt32VM(int? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, int.MinValue, int.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(int? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="uint"/>
/// </summary>
public class NullableUInt32VM : NumberDataFieldVM<uint?, uint> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableUInt32VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableUInt32VM(uint? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, uint.MinValue, uint.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(uint? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="long"/>
/// </summary>
public class NullableInt64VM : NumberDataFieldVM<long?, long> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableInt64VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableInt64VM(long? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, long.MinValue, long.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(long? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// Nullable <see cref="ulong"/>
/// </summary>
public class NullableUInt64VM : NumberDataFieldVM<ulong?, ulong> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableUInt64VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(null, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public NullableUInt64VM(ulong? value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, ulong.MinValue, ulong.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(ulong? value) => value is null ? string.Empty : SimpleTypeConverter.ToString(value.Value, Min, Max, UseDecimal);
/// <inheritdoc/>
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;
}
}
/// <summary>
/// <see cref="bool"/>
/// </summary>
public class BooleanVM : DataFieldVM<bool> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public BooleanVM(Action<DataFieldVM> onUpdated)
: this(false, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public BooleanVM(bool value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(bool value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out bool value) {
value = SimpleTypeConverter.ParseBoolean(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="char"/>
/// </summary>
public class CharVM : DataFieldVM<char> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public CharVM(Action<DataFieldVM> onUpdated)
: this((char)0, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public CharVM(char value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(char value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out char value) {
value = SimpleTypeConverter.ParseChar(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="byte"/>
/// </summary>
public class ByteVM : NumberDataFieldVM<byte, byte> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public ByteVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public ByteVM(byte value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, byte.MinValue, byte.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(byte value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out byte value) {
value = SimpleTypeConverter.ParseByte(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="ushort"/>
/// </summary>
public class UInt16VM : NumberDataFieldVM<ushort, ushort> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt16VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt16VM(ushort value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, ushort.MinValue, ushort.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(ushort value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out ushort value) {
value = SimpleTypeConverter.ParseUInt16(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="uint"/>
/// </summary>
public class UInt32VM : NumberDataFieldVM<uint, uint> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt32VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt32VM(uint value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, uint.MinValue, uint.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(uint value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out uint value) {
value = SimpleTypeConverter.ParseUInt32(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="ulong"/>
/// </summary>
public class UInt64VM : NumberDataFieldVM<ulong, ulong> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt64VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt64VM(ulong value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, ulong.MinValue, ulong.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(ulong value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out ulong value) {
value = SimpleTypeConverter.ParseUInt64(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="sbyte"/>
/// </summary>
public class SByteVM : NumberDataFieldVM<sbyte, sbyte> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public SByteVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public SByteVM(sbyte value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, sbyte.MinValue, sbyte.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(sbyte value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out sbyte value) {
value = SimpleTypeConverter.ParseSByte(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="short"/>
/// </summary>
public class Int16VM : NumberDataFieldVM<short, short> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int16VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int16VM(short value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, short.MinValue, short.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(short value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out short value) {
value = SimpleTypeConverter.ParseInt16(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="int"/>
/// </summary>
public class Int32VM : NumberDataFieldVM<int, int> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int32VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int32VM(int value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, int.MinValue, int.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(int value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out int value) {
value = SimpleTypeConverter.ParseInt32(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="long"/>
/// </summary>
public class Int64VM : NumberDataFieldVM<long, long> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int64VM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(0, onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int64VM(long value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, long.MinValue, long.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(long value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out long value) {
value = SimpleTypeConverter.ParseInt64(StringValue, Min, Max, out var error);
return error;
}
}
/// <summary>
/// <see cref="float"/>
/// </summary>
public class SingleVM : DataFieldVM<float> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public SingleVM(Action<DataFieldVM> onUpdated)
: this(0, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public SingleVM(float value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(float value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out float value) {
value = SimpleTypeConverter.ParseSingle(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="double"/>
/// </summary>
public class DoubleVM : DataFieldVM<double> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public DoubleVM(Action<DataFieldVM> onUpdated)
: this(0, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public DoubleVM(double value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(double value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out double value) {
value = SimpleTypeConverter.ParseDouble(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="string"/>
/// </summary>
public class StringVM : DataFieldVM<string> {
readonly bool allowNullString;
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="allowNullString">true to allow null strings</param>
public StringVM(Action<DataFieldVM> onUpdated, bool allowNullString = false)
: this(string.Empty, onUpdated, allowNullString) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="allowNullString">true to allow null strings</param>
public StringVM(string value, Action<DataFieldVM> onUpdated, bool allowNullString = false)
: base(onUpdated) {
this.allowNullString = allowNullString;
SetValueFromConstructor(value);
}
/// <inheritdoc/>
protected override string OnNewValue(string value) => SimpleTypeConverter.ToString(value, allowNullString);
/// <inheritdoc/>
protected override string? ConvertToValue(out string value) {
value = SimpleTypeConverter.ParseString(StringValue, allowNullString, out var error)!;
return error;
}
}
/// <summary>
/// <see cref="decimal"/>
/// </summary>
public class DecimalVM : DataFieldVM<decimal> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public DecimalVM(Action<DataFieldVM> onUpdated)
: this(0, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public DecimalVM(decimal value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(decimal value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out decimal value) {
value = SimpleTypeConverter.ParseDecimal(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="DateTime"/>
/// </summary>
public class DateTimeVM : DataFieldVM<DateTime> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public DateTimeVM(Action<DataFieldVM> onUpdated)
: this(DateTime.Now, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public DateTimeVM(DateTime value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(DateTime value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out DateTime value) {
value = SimpleTypeConverter.ParseDateTime(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="TimeSpan"/>
/// </summary>
public class TimeSpanVM : DataFieldVM<TimeSpan> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public TimeSpanVM(Action<DataFieldVM> onUpdated)
: this(TimeSpan.Zero, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public TimeSpanVM(TimeSpan value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(TimeSpan value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out TimeSpan value) {
value = SimpleTypeConverter.ParseTimeSpan(StringValue, out var error);
return error;
}
}
/// <summary>
/// <see cref="Guid"/>
/// </summary>
public class GuidVM : DataFieldVM<Guid> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public GuidVM(Action<DataFieldVM> onUpdated)
: this(new Guid(), onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public GuidVM(Guid value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(Guid value) => value.ToString();
/// <inheritdoc/>
protected override string? ConvertToValue(out Guid value) {
value = NullableGuidVM.ParseGuid(StringValue, out var error);
return error;
}
}
/// <summary>
/// List of <see cref="bool"/>s
/// </summary>
public class BooleanListDataFieldVM : DataFieldVM<IList<bool>> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public BooleanListDataFieldVM(Action<DataFieldVM> onUpdated)
: this(Array.Empty<bool>(), onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public BooleanListDataFieldVM(IList<bool> value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<bool> value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<bool> value) {
value = SimpleTypeConverter.ParseBooleanList(StringValue, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="char"/>s
/// </summary>
public class CharListDataFieldVM : DataFieldVM<IList<char>> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public CharListDataFieldVM(Action<DataFieldVM> onUpdated)
: this(Array.Empty<char>(), onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public CharListDataFieldVM(IList<char> value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<char> value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<char> value) {
value = SimpleTypeConverter.ParseCharList(StringValue, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="byte"/>s
/// </summary>
public class ByteListDataFieldVM : NumberDataFieldVM<IList<byte>, byte> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public ByteListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<byte>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public ByteListDataFieldVM(IList<byte> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, byte.MinValue, byte.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<byte> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<byte> value) {
value = SimpleTypeConverter.ParseByteList(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="ushort"/>s
/// </summary>
public class UInt16ListDataFieldVM : NumberDataFieldVM<IList<ushort>, ushort> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt16ListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<ushort>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt16ListDataFieldVM(IList<ushort> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, ushort.MinValue, ushort.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<ushort> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<ushort> value) {
value = SimpleTypeConverter.ParseUInt16List(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="uint"/>s
/// </summary>
public class UInt32ListDataFieldVM : NumberDataFieldVM<IList<uint>, uint> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt32ListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<uint>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt32ListDataFieldVM(IList<uint> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, uint.MinValue, uint.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<uint> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<uint> value) {
value = SimpleTypeConverter.ParseUInt32List(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="ulong"/>s
/// </summary>
public class UInt64ListDataFieldVM : NumberDataFieldVM<IList<ulong>, ulong> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt64ListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<ulong>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public UInt64ListDataFieldVM(IList<ulong> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, ulong.MinValue, ulong.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<ulong> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<ulong> value) {
value = SimpleTypeConverter.ParseUInt64List(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="sbyte"/>s
/// </summary>
public class SByteListDataFieldVM : NumberDataFieldVM<IList<sbyte>, sbyte> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public SByteListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<sbyte>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public SByteListDataFieldVM(IList<sbyte> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, sbyte.MinValue, sbyte.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<sbyte> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<sbyte> value) {
value = SimpleTypeConverter.ParseSByteList(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="short"/>s
/// </summary>
public class Int16ListDataFieldVM : NumberDataFieldVM<IList<short>, short> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int16ListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<short>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int16ListDataFieldVM(IList<short> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, short.MinValue, short.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<short> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<short> value) {
value = SimpleTypeConverter.ParseInt16List(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="int"/>s
/// </summary>
public class Int32ListDataFieldVM : NumberDataFieldVM<IList<int>, int> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int32ListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<int>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int32ListDataFieldVM(IList<int> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, int.MinValue, int.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<int> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<int> value) {
value = SimpleTypeConverter.ParseInt32List(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="long"/>s
/// </summary>
public class Int64ListDataFieldVM : NumberDataFieldVM<IList<long>, long> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int64ListDataFieldVM(Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: this(Array.Empty<long>(), onUpdated, useDecimal) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="useDecimal">true to use decimal, false to use hex, or null if it depends on the value</param>
public Int64ListDataFieldVM(IList<long> value, Action<DataFieldVM> onUpdated, bool? useDecimal = null)
: base(onUpdated, long.MinValue, long.MaxValue, useDecimal) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<long> value) => SimpleTypeConverter.ToString(value, Min, Max, UseDecimal);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<long> value) {
value = SimpleTypeConverter.ParseInt64List(StringValue, Min, Max, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="float"/>s
/// </summary>
public class SingleListDataFieldVM : DataFieldVM<IList<float>> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public SingleListDataFieldVM(Action<DataFieldVM> onUpdated)
: this(Array.Empty<float>(), onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public SingleListDataFieldVM(IList<float> value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<float> value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<float> value) {
value = SimpleTypeConverter.ParseSingleList(StringValue, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="double"/>s
/// </summary>
public class DoubleListDataFieldVM : DataFieldVM<IList<double>> {
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public DoubleListDataFieldVM(Action<DataFieldVM> onUpdated)
: this(Array.Empty<double>(), onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public DoubleListDataFieldVM(IList<double> value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(IList<double> value) => SimpleTypeConverter.ToString(value);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<double> value) {
value = SimpleTypeConverter.ParseDoubleList(StringValue, out var error)!;
return error;
}
}
/// <summary>
/// List of <see cref="string"/>s
/// </summary>
public class StringListDataFieldVM : DataFieldVM<IList<string>> {
readonly bool allowNullString;
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="allowNullString">true to allow null strings</param>
public StringListDataFieldVM(Action<DataFieldVM> onUpdated, bool allowNullString = true)
: this(Array.Empty<string>(), onUpdated, allowNullString) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
/// <param name="allowNullString">true to allow null strings</param>
public StringListDataFieldVM(IList<string> value, Action<DataFieldVM> onUpdated, bool allowNullString = true)
: base(onUpdated) {
this.allowNullString = allowNullString;
SetValueFromConstructor(value);
}
/// <inheritdoc/>
protected override string OnNewValue(IList<string> value) => SimpleTypeConverter.ToString(value, allowNullString);
/// <inheritdoc/>
protected override string? ConvertToValue(out IList<string> value) {
value = SimpleTypeConverter.ParseStringList(StringValue, allowNullString, out var error)!;
return error;
}
}
/// <summary>
/// Uses the default converter to convert the type to/from a string
/// </summary>
/// <typeparam name="T">Type</typeparam>
public class DefaultConverterVM<T> : DataFieldVM<T> {
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)}");
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="onUpdated">Called when value gets updated</param>
public DefaultConverterVM(Action<DataFieldVM> onUpdated)
: this(default!, onUpdated) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="value">Initial value</param>
/// <param name="onUpdated">Called when value gets updated</param>
public DefaultConverterVM(T value, Action<DataFieldVM> onUpdated)
: base(onUpdated) => SetValueFromConstructor(value);
/// <inheritdoc/>
protected override string OnNewValue(T value) => (string)converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(string));
/// <inheritdoc/>
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;
}
}
}