using System; using System.Collections.ObjectModel; using Avalonia.Controls; using Avalonia.Media.Imaging; using Material.Dialog.Bases; using Material.Dialog.Enums; using Material.Dialog.Icons; using Material.Dialog.Interfaces; using Material.Dialog.ViewModels; using Material.Dialog.ViewModels.Elements; using Material.Dialog.ViewModels.Elements.Header.Icons; using Material.Dialog.ViewModels.Elements.TextField; using Material.Dialog.Views; namespace Material.Dialog { public class DialogHelper { public const string DIALOG_RESULT_OK = "ok"; public const string DIALOG_RESULT_CANCEL = "cancel"; public const string DIALOG_RESULT_YES = "yes"; public const string DIALOG_RESULT_NO = "no"; public const string DIALOG_RESULT_ABORT = "abort"; private static bool _disableTransitions; public static bool DisableTransitions { get => _disableTransitions; set => _disableTransitions = value; } /// /// Create some dialog buttons for use. /// /// Which dialog buttons group case you need. public static DialogButton[] CreateSimpleDialogButtons(DialogButtonsEnum cases) { switch (cases) { default: case DialogButtonsEnum.Ok: return new[] { new DialogButton {Result = DIALOG_RESULT_OK, Content = "OK"} }; case DialogButtonsEnum.OkAbort: return new[] { new DialogButton {Result = DIALOG_RESULT_ABORT, Content = "ABORT"}, new DialogButton {Result = DIALOG_RESULT_OK, Content = "OK"} }; case DialogButtonsEnum.OkCancel: return new[] { new DialogButton {Result = DIALOG_RESULT_CANCEL, Content = "CANCEL"}, new DialogButton {Result = DIALOG_RESULT_OK, Content = "OK"} }; case DialogButtonsEnum.YesNo: return new[] { new DialogButton {Result = DIALOG_RESULT_NO, Content = "NO"}, new DialogButton {Result = DIALOG_RESULT_YES, Content = "YES"} }; case DialogButtonsEnum.YesNoAbort: return new[] { new DialogButton {Result = DIALOG_RESULT_ABORT, Content = "ABORT"}, new DialogButton {Result = DIALOG_RESULT_NO, Content = "NO"}, new DialogButton {Result = DIALOG_RESULT_YES, Content = "YES"} }; case DialogButtonsEnum.YesNoCancel: return new[] { new DialogButton {Result = DIALOG_RESULT_CANCEL, Content = "CANCEL"}, new DialogButton {Result = DIALOG_RESULT_NO, Content = "NO"}, new DialogButton {Result = DIALOG_RESULT_YES, Content = "YES"} }; } } public static IDialogWindow CreateAlertDialog(AlertDialogBuilderParams @params) { var window = new AlertDialog(); var context = new AlertDialogViewModel(window); ApplyBaseParams(context, @params); context.DialogButtons ??= new ObservableCollection( CreateObsoleteButtonArray(context, CreateSimpleDialogButtons(DialogButtonsEnum.Ok))); window.DataContext = context; SetupWindowParameters(window, @params); return new DialogWindowBase(window); } public static IDialogWindow CreateTextFieldDialog(TextFieldDialogBuilderParams @params) { var window = new TextFieldDialog(); var context = new TextFieldDialogViewModel(window); context.TextFields = new ObservableCollection(TextFieldsBuilder(context, @params.TextFields)); ApplyBaseParams(context, @params); var positiveButtonApplied = false; var buttons = CreateObsoleteButtonArray(context, @params.DialogButtons); foreach (var button in buttons) { if (!button.IsPositiveButton) continue; button.Command = context.SubmitCommand; positiveButtonApplied = true; } context.DialogButtons = new ObservableCollection(buttons); // TODO: Remove compatibility API with PositiveButton and NegativeButton on future update. if (!positiveButtonApplied) { var positiveButton = @params.PositiveButton; if (positiveButton != null) { context.DialogButtons.Add( new ObsoleteDialogButtonViewModel(context, positiveButton.Content, positiveButton.Result) { Command = context.SubmitCommand }); } } context.BindValidateHandler(); window.DataContext = context; SetupWindowParameters(window, @params); return new DialogWindowBase(window); } /// /// Create time picker dialog. /// /// Parameters of building dialog /// Instance of picker. public static IDialogWindow CreateTimePicker(TimePickerDialogBuilderParams @params) { var window = new TimePickerDialog(); var context = new TimePickerDialogViewModel(window) { PositiveButton = @params.PositiveButton, NegativeButton = @params.NegativeButton, FirstField = (ushort) @params.ImplicitValue.Hours, SecondField = (ushort) @params.ImplicitValue.Minutes, }; ApplyBaseParams(context, @params); context.DialogButtons = new ObservableCollection(CreateObsoleteButtonArray(context, @params.NegativeButton, @params.PositiveButton)); if (context.Width is null || context.Width < 320) context.Width = 320; window.AttachViewModel(context); SetupWindowParameters(window, @params); return new DialogWindowBase(window); } /// /// Create date picker dialog. /// /// Parameters of building dialog /// Instance of picker. //[Obsolete("This feature is still not ready for use! Please come back later!")] public static IDialogWindow CreateDatePicker(DatePickerDialogBuilderParams @params) { var window = new DatePickerDialog(); var context = new DatePickerDialogViewModel(window) { PositiveButton = @params.PositiveButton, NegativeButton = @params.NegativeButton, DateTime = @params.ImplicitValue }; ApplyBaseParams(context, @params); context.DialogButtons = new ObservableCollection(CreateObsoleteButtonArray(context, @params.NegativeButton, @params.PositiveButton)); if (context.Width is null || context.Width < 320) context.Width = 320; window.AttachViewModel(context); SetupWindowParameters(window, @params); return new DialogWindowBase(window); } /// /// Create an dialog with custom content or dummy dialog. /// /// Parameters of building dialog /// Instance of dialog. public static IDialogWindow CreateCustomDialog(CustomDialogBuilderParams @params) { var window = new CustomDialog(); var context = new CustomDialogViewModel(window) { Content = @params.Content, ContentTemplate = @params.ContentTemplate }; ApplyBaseParams(context, @params); window.DataContext = context; SetupWindowParameters(window, @params); return new DialogWindowBase(window); } private static void ApplyBaseParams(T input, DialogWindowBuilderParamsBase @params) where T : DialogWindowViewModel { input.MaxWidth = @params.MaxWidth; input.WindowTitle = @params.WindowTitle; input.Width = @params.Width; input.ContentHeader = @params.ContentHeader; input.ContentMessage = @params.SupportingText; input.Borderless = @params.Borderless; input.WindowStartupLocation = @params.StartupLocation; switch (@params.DialogIcon) { case Bitmap bitmap: { input.DialogIcon = new ImageIconViewModel { Bitmap = bitmap }; } break; case Image _: throw new ArgumentException("Do not wrap Bitmap object with Image control for now."); case Control _: throw new ArgumentException("Custom view icon feature is currently unavailable."); case DialogIconKind kind: { if (@params.DialogHeaderIcon != null) { input.DialogIcon = new DialogIconViewModel { Kind = kind }; } } break; case null: break; default: throw new ArgumentException($"{@params.DialogIcon.GetType()} is a unknown or unsupported type."); } // Rollback API Compatibility if (@params.DialogHeaderIcon != null) { if (input.DialogIcon == null && @params.DialogHeaderIcon != null) { input.DialogIcon = new DialogIconViewModel { Kind = @params.DialogHeaderIcon.Value }; } } if (@params.DialogButtons != null) input.DialogButtons = new ObservableCollection( CreateObsoleteButtonArray(input, @params.DialogButtons)); if (@params.NeutralDialogButtons != null) input.NeutralDialogButton = new ObservableCollection( CreateObsoleteButtonArray(input, @params.NeutralDialogButtons)); input.ButtonsStackOrientation = @params.ButtonsOrientation; } private static void SetupWindowParameters(Window window, DialogWindowBuilderParamsBase @params) { window.SystemDecorations = @params.Borderless ? SystemDecorations.None : SystemDecorations.Full; (window as IHasNegativeResult)?.SetNegativeResult(@params.NegativeResult); } private static DialogButtonViewModel[] CreateObsoleteButtonArray(DialogWindowViewModel parent, params DialogButton[] buttons) { var len = buttons.Length; var result = new DialogButtonViewModel[buttons.Length]; for (var i = 0; i < len; i++) { var button = buttons[i]; if (button is null) continue; result[i] = new ObsoleteDialogButtonViewModel(parent, button.Content, button.Result) { IsPositiveButton = button.IsPositive }; } return result; } private static TextFieldViewModel[] TextFieldsBuilder(TextFieldDialogViewModel parent, params TextFieldBuilderParams[] @params) { var len = @params.Length; var result = new TextFieldViewModel[len]; for (var i = 0; i < len; i++) { var param = @params[i]; try { var model = new TextFieldViewModel(parent, param.DefaultText, param.Validater) { // but... I implemented an setter to TextFieldDialog for apply classes when showing dialog. // Currently AvaloniaUI are not supported to binding classes. Classes = param.Classes, PlaceholderText = param.PlaceholderText, MaxCountChars = param.MaxCountChars, Label = param.Label, AssistiveText = param.HelperText }; switch (param.FieldKind) { case TextFieldKind.Masked: model.MaskChar = param.MaskChar; model.Classes += " revealPasswordButton"; break; case TextFieldKind.WithClear: model.Classes += " clearButton"; break; case TextFieldKind.Normal: break; default: throw new ArgumentOutOfRangeException(); } result[i] = model; } catch (Exception e) { Console.WriteLine(e.Message); // ignored } } return result; } } }