diff --git a/AssetEntry.cs b/AssetEntry.cs index 7bf75f5..e504547 100644 --- a/AssetEntry.cs +++ b/AssetEntry.cs @@ -12,6 +12,9 @@ namespace HashCalculator.GUI public IEnumerable DisplayLabels { get; } public uint Hash => SageHash.CalculateLowercaseHash(Name); + public string IdString => $"{Type}:{Name}"; + public string HashString => $"{Hash:0X} ({Hash})"; + public AssetEntry(XElement element) { if (element == null) @@ -26,12 +29,7 @@ namespace HashCalculator.GUI Type = element.Name.LocalName; var id = element.Attribute("id")?.Value; - if (id == null) - { - throw new NotSupportedException(); - } - - Name = id; + Name = id ?? throw new NotSupportedException(); var labels = from name in element.Elements(ModXml.EalaAsset + "DisplayName") select name.Value; diff --git a/Command.cs b/Command.cs new file mode 100644 index 0000000..209ce4e --- /dev/null +++ b/Command.cs @@ -0,0 +1,131 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; + +namespace HashCalculator.GUI +{ + internal class Command : ICommand + { + private readonly Func _action; + private readonly Action _onAsyncException; + private bool _canExecute = true; + + public bool CanExecuteValue + { + get => _canExecute; + set + { + _canExecute = value; + CanExecuteChanged?.Invoke(this, new EventArgs()); + } + } + + public event EventHandler? CanExecuteChanged; + + public Command(Func action, Action? onAsyncException = null) + { + _action = action; + _onAsyncException = onAsyncException ?? (exception => + { + MessageBox.Show($"Unhandled async exception in {nameof(Command)}: {exception}"); + Application.Current.Shutdown(1); + }); + } + + public bool CanExecute(object parameter) + { + return _canExecute; + } + + [SuppressMessage("Globalization", "CA1303:请不要将文本作为本地化参数传递", Justification = "<挂起>")] + public void Execute(object parameter) + { + if (!_canExecute) + { + return; + } + if(!(parameter is T typed)) + { + throw new ArgumentException($"{nameof(parameter)} wrong type"); + } + + ExecuteTaskInternal(ExecuteTask(typed)); + } + + public Task ExecuteTask(T parameter) => _action(parameter); + + [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] + private async void ExecuteTaskInternal(Task task) + { + try + { + await task.ConfigureAwait(true); + } + catch (Exception exception) + { + _onAsyncException(exception); + } + } + } + + internal class Command : ICommand + { + private readonly Func _action; + private readonly Action _onAsyncException; + private bool _canExecute = true; + + public bool CanExecuteValue + { + get => _canExecute; + set + { + _canExecute = value; + CanExecuteChanged?.Invoke(this, new EventArgs()); + } + } + + public event EventHandler? CanExecuteChanged; + + public Command(Func action, Action? onAsyncException = null) + { + _action = action; + _onAsyncException = onAsyncException ?? (exception => + { + MessageBox.Show($"Unhandled async exception in {nameof(Command)}: {exception}"); + Application.Current.Shutdown(1); + }); + } + + public bool CanExecute(object parameter) + { + return _canExecute; + } + + public void Execute(object parameter) + { + if (!_canExecute) + { + return; + } + + ExecuteTaskInternal(ExecuteTask()); + } + + public Task ExecuteTask() => _action(); + + [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] + private async void ExecuteTaskInternal(Task task) + { + try + { + await task.ConfigureAwait(true); + } + catch (Exception exception) + { + _onAsyncException(exception); + } + } + } +} diff --git a/Converters/BooleanInvertConverter.cs b/Converters/BooleanInvertConverter.cs new file mode 100644 index 0000000..cf6b4a4 --- /dev/null +++ b/Converters/BooleanInvertConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace HashCalculator.GUI.Converters +{ + [ValueConversion(typeof(bool), typeof(bool))] + internal class BooleanInvertConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + bool original = (bool)value; + return !original; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + bool original = (bool)value; + return !original; + } + } +} \ No newline at end of file diff --git a/Converters/NullToBooleanConverter.cs b/Converters/NullToBooleanConverter.cs new file mode 100644 index 0000000..9b27cca --- /dev/null +++ b/Converters/NullToBooleanConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace HashCalculator.GUI.Converters +{ + [ValueConversion(typeof(object), typeof(bool))] + public class NullToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is null) + { + return false; + } + else if(value is string s) + { + return !string.IsNullOrEmpty(s); + } + return true; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/ValidInputEntryTypeToBooleanConverter.cs b/Converters/ValidInputEntryTypeToBooleanConverter.cs new file mode 100644 index 0000000..2dc5785 --- /dev/null +++ b/Converters/ValidInputEntryTypeToBooleanConverter.cs @@ -0,0 +1,21 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace HashCalculator.GUI.Converters +{ + [ValueConversion(typeof(InputEntry), typeof(bool))] + internal class ValidInputEntryTypeToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var entry = value as InputEntry; + return entry?.IsValid == true; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/NullToVisibilityConverter.cs b/Converters/ValueConverterAggregate.cs similarity index 55% rename from NullToVisibilityConverter.cs rename to Converters/ValueConverterAggregate.cs index a2c2976..ac1afcf 100644 --- a/NullToVisibilityConverter.cs +++ b/Converters/ValueConverterAggregate.cs @@ -1,17 +1,16 @@ using System; +using System.Collections.Generic; using System.Globalization; -using System.Windows; +using System.Linq; using System.Windows.Data; -namespace HashCalculator.GUI +namespace HashCalculator.GUI.Converters { - [ValueConversion(typeof(string), typeof(Visibility))] - public class NullToVisibilityConverter : IValueConverter + internal class ValueConverterAggregate : List, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var text = value as string; - return string.IsNullOrEmpty(text) ? Visibility.Visible : Visibility.Collapsed; + return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture)); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/CopyableBox.xaml b/CopyableBox.xaml new file mode 100644 index 0000000..c2044cc --- /dev/null +++ b/CopyableBox.xaml @@ -0,0 +1,50 @@ + + + + + + + + + +