using Microsoft.UI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using SQLVision.Core.Enums; using SQLVision.Core.Interfaces; using SQLVision.Core.Models; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text.Json; using System.Threading.Tasks; namespace SQLVision.UI.Controls; public class ControlFactory : IControlFactory { private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; public ControlFactory(IServiceProvider serviceProvider, ILogger logger) { _serviceProvider = serviceProvider; _logger = logger; } public FrameworkElement CreateControl(ScriptParameter parameter, Action onValueChanged) { try { var control = parameter.Type switch { ParameterType.String => CreateTextBox(parameter, onValueChanged), ParameterType.Int => CreateNumberBox(parameter, onValueChanged), ParameterType.Decimal => CreateNumberBox(parameter, onValueChanged, true), ParameterType.DateTime => CreateDatePicker(parameter, onValueChanged), ParameterType.Bool => CreateCheckBox(parameter, onValueChanged), ParameterType.Table => CreateComboBox(parameter, onValueChanged), ParameterType.MultiSelect => CreateListBox(parameter, onValueChanged), ParameterType.Color => CreateColorPicker(parameter, onValueChanged), ParameterType.File => CreateFilePicker(parameter, onValueChanged), ParameterType.Json => CreateJsonEditor(parameter, onValueChanged), _ => CreateTextBox(parameter, onValueChanged) }; // Настройка общих свойств control.Tag = parameter; control.IsEnabled = !parameter.IsRequired || parameter.DefaultValue == null; // Добавление подсказки if (!string.IsNullOrEmpty(parameter.Description)) { ToolTipService.SetToolTip(control, parameter.Description); } return control; } catch (Exception ex) { _logger.LogError(ex, "Error creating control for parameter {Parameter}", parameter.Name); return CreateFallbackControl(parameter, onValueChanged); } } private FrameworkElement CreateTextBox(ScriptParameter parameter, Action onValueChanged) { var textBox = new TextBox { Header = parameter.DisplayName, PlaceholderText = parameter.Watermark ?? $"Введите {parameter.DisplayName.ToLower()}", Text = parameter.DefaultValue?.ToString() ?? string.Empty }; textBox.TextChanged += (s, e) => onValueChanged(textBox.Text); return textBox; } private FrameworkElement CreateNumberBox(ScriptParameter parameter, Action onValueChanged, bool isDecimal = false) { var numberBox = new NumberBox { Header = parameter.DisplayName, PlaceholderText = parameter.Watermark ?? $"Введите {parameter.DisplayName.ToLower()}", SmallChange = isDecimal ? 0.1 : 1, LargeChange = isDecimal ? 1 : 10, SpinButtonPlacementMode = NumberBoxSpinButtonPlacementMode.Inline, AcceptsExpression = false }; if (isDecimal) { numberBox.Value = Convert.ToDouble(parameter.DefaultValue ?? 0); numberBox.ValueChanged += (s, e) => onValueChanged(e.NewValue); } else { numberBox.Value = Convert.ToInt32(parameter.DefaultValue ?? 0); numberBox.ValueChanged += (s, e) => onValueChanged((int)e.NewValue); } // Применение правил валидации if (parameter.ValidationRules != null) { if (parameter.ValidationRules.TryGetValue("min", out var min)) numberBox.Minimum = Convert.ToDouble(min); if (parameter.ValidationRules.TryGetValue("max", out var max)) numberBox.Maximum = Convert.ToDouble(max); } return numberBox; } private FrameworkElement CreateDatePicker(ScriptParameter parameter, Action onValueChanged) { var datePicker = new DatePicker { Header = parameter.DisplayName, Date = parameter.DefaultValue is DateTime defaultDate ? DateTimeOffset.Parse(defaultDate.ToString()) : DateTimeOffset.Now }; datePicker.DateChanged += (s, e) => onValueChanged(e.NewDate.DateTime); return datePicker; } private FrameworkElement CreateCheckBox(ScriptParameter parameter, Action onValueChanged) { var checkBox = new CheckBox { Content = parameter.DisplayName, IsChecked = parameter.DefaultValue is bool defaultBool ? defaultBool : false }; checkBox.Checked += (s, e) => onValueChanged(true); checkBox.Unchecked += (s, e) => onValueChanged(false); return checkBox; } private FrameworkElement CreateComboBox(ScriptParameter parameter, Action onValueChanged) { var comboBox = new ComboBox { Header = parameter.DisplayName, PlaceholderText = parameter.Watermark ?? $"Выберите {parameter.DisplayName.ToLower()}", DisplayMemberPath = parameter.DisplayMember, SelectedValuePath = parameter.ValueMember }; // Загрузка данных асинхронно LoadComboBoxDataAsync(comboBox, parameter).ConfigureAwait(false); comboBox.SelectionChanged += (s, e) => { if (comboBox.SelectedValue != null) onValueChanged(comboBox.SelectedValue); }; return comboBox; } private async Task LoadComboBoxDataAsync(ComboBox comboBox, ScriptParameter parameter) { if (string.IsNullOrEmpty(parameter.TableQuery)) return; try { // Используем сервис выполнения SQL для загрузки данных var executionService = _serviceProvider.GetService(); var result = await executionService.ExecuteAsync( parameter.TableQuery, new Dictionary(), GetConnectionString()); if (result.IsSuccess && result.Data.Tables.Count > 0) { comboBox.ItemsSource = result.Data.Tables[0].DefaultView; // Установка значения по умолчанию if (parameter.DefaultValue != null) { comboBox.SelectedValue = parameter.DefaultValue; } } } catch (Exception ex) { _logger.LogError(ex, "Error loading combo box data for {Parameter}", parameter.Name); } } private FrameworkElement CreateListBox(ScriptParameter parameter, Action onValueChanged) { var listBox = new ListBox { Header = parameter.DisplayName, SelectionMode = ListViewSelectionMode.Multiple }; // Загрузка данных для ListBox // Аналогично ComboBox listBox.SelectionChanged += (s, e) => { var selectedValues = listBox.SelectedItems.Cast() .Select(r => r[parameter.ValueMember]) .ToList(); onValueChanged(selectedValues); }; return listBox; } private FrameworkElement CreateColorPicker(ScriptParameter parameter, Action onValueChanged) { var colorPicker = new ColorPicker { Header = parameter.DisplayName, ColorSpectrumShape = ColorSpectrumShape.Box, IsMoreButtonVisible = true, IsColorSliderVisible = true, IsColorChannelTextInputVisible = true, IsHexInputVisible = true }; if (parameter.DefaultValue is string defaultColor) { if (ColorHelper.TryParse(defaultColor, out var color)) { colorPicker.Color = color; } } colorPicker.ColorChanged += (s, e) => onValueChanged($"#{e.NewColor.R:X2}{e.NewColor.G:X2}{e.NewColor.B:X2}"); return colorPicker; } private FrameworkElement CreateFilePicker(ScriptParameter parameter, Action onValueChanged) { var stackPanel = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8 }; var textBox = new TextBox { Header = parameter.DisplayName, PlaceholderText = "Путь к файлу", Width = 200, IsReadOnly = true }; var button = new Button { Content = "Выбрать", VerticalAlignment = VerticalAlignment.Bottom }; button.Click += async (s, e) => { var openPicker = new FileOpenPicker(); openPicker.ViewMode = PickerViewMode.List; openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; if (parameter.ValidationRules != null && parameter.ValidationRules.TryGetValue("extensions", out var extensions)) { foreach (var ext in extensions.ToString().Split(',')) { openPicker.FileTypeFilter.Add(ext.Trim()); } } else { openPicker.FileTypeFilter.Add("*"); } var file = await openPicker.PickSingleFileAsync(); if (file != null) { textBox.Text = file.Path; onValueChanged(file.Path); } }; stackPanel.Children.Add(textBox); stackPanel.Children.Add(button); return stackPanel; } private FrameworkElement CreateJsonEditor(ScriptParameter parameter, Action onValueChanged) { var textBox = new TextBox { Header = parameter.DisplayName, PlaceholderText = "Введите JSON", AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, Height = 100, FontFamily = new FontFamily("Consolas") }; if (parameter.DefaultValue != null) { textBox.Text = JsonSerializer.Serialize(parameter.DefaultValue, new JsonSerializerOptions { WriteIndented = true }); } textBox.TextChanged += (s, e) => { try { var json = JsonSerializer.Deserialize(textBox.Text); onValueChanged(json); } catch { // Игнорируем ошибки парсинга JSON } }; return textBox; } private FrameworkElement CreateFallbackControl(ScriptParameter parameter, Action onValueChanged) { return new TextBox { Header = parameter.DisplayName, Text = $"Ошибка создания контрола для типа {parameter.Type}", IsReadOnly = true, Foreground = new SolidColorBrush(Colors.Red) }; } public void UpdateControlState(FrameworkElement control, ScriptParameter parameter, Dictionary currentValues) { // Обновление состояния контрола на основе зависимостей if (!string.IsNullOrEmpty(parameter.DependsOn)) { var isEnabled = CheckDependency(parameter, currentValues); control.IsEnabled = isEnabled; if (!isEnabled) { // Сброс значения, если контрол отключен ResetControlValue(control); } } } private bool CheckDependency(ScriptParameter parameter, Dictionary currentValues) { if (!currentValues.TryGetValue(parameter.DependsOn, out var dependencyValue)) return false; if (parameter.DependencyValues != null) { return parameter.DependencyValues .Any(kvp => object.Equals(kvp.Value, dependencyValue)); } return dependencyValue != null && !string.IsNullOrWhiteSpace(dependencyValue.ToString()); } private void ResetControlValue(FrameworkElement control) { switch (control) { case TextBox textBox: textBox.Text = string.Empty; break; case ComboBox comboBox: comboBox.SelectedIndex = -1; break; case CheckBox checkBox: checkBox.IsChecked = false; break; case DatePicker datePicker: datePicker.Date = DateTimeOffset.Now; break; } } private string GetConnectionString() { // Получение строки подключения из конфигурации var configuration = _serviceProvider.GetService(); return configuration.GetConnectionString("Default") ?? configuration["Database:DefaultConnection"]; } }