using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Threading; using DispenserCommon.Atrributes; using DispenserCommon.Utils; using Masuit.Tools; using ReactiveUI; using SQLite; namespace DispenserUI.Views.Controls; /// /// 参数配置编辑器 /// public partial class SettingEditor : UserControl { public event EventHandler OnSaveClicked; public static readonly StyledProperty ParamProperty = AvaloniaProperty.Register(nameof(Param)); public static readonly StyledProperty TitleProperty = AvaloniaProperty.Register(nameof(Title)); public static readonly StyledProperty PathNameProperty = AvaloniaProperty.Register(nameof(PathName)); public SettingEditor() { InitializeComponent(); Layout = this.FindControl("Layout"); this.GetObservable(ParamProperty).Subscribe(ps => { if (ps == null) return; Dispatcher.UIThread.InvokeAsync(() => { Param = ps; Render(); }); }); } public object Param { get => GetValue(ParamProperty); set => SetValue(ParamProperty, value); } public string Title { get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); } public string? PathName { get => GetValue(PathNameProperty); set => SetValue(PathNameProperty, value); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } /// /// 渲染UI控件 /// private void Render() { if (Param.IsNullOrEmpty()) return; // 外层用一个大的 Border 来装 var itemLayout = new Border { Padding = new Thickness(10), Margin = new Thickness(5), Background = Brush.Parse("#F6F8FA"), BoxShadow = BoxShadows.Parse("0 0 16 0 LightGray"), CornerRadius = new CornerRadius(10) }; var stackPanel = new StackPanel(); var titleBorder = new Border { Height = 40, Padding = new Thickness(20, 0, 20, 0), VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Stretch, BorderBrush = new SolidColorBrush(Colors.Gray), BorderThickness = new Thickness(0, 0, 0, 1) }; var titleGrid = new Grid { RowDefinitions = RowDefinitions.Parse("*") }; var titleText = new TextBlock { Text = GetTitle(), FontSize = 16, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left }; var saveBtn = new Button { Content = "保存", HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center, HorizontalContentAlignment = HorizontalAlignment.Center, Command = ReactiveCommand.Create(() => { OnSaveClicked?.Invoke(this, Param); ToastUtil.Success("保存成功"); }), Foreground = Brushes.White, Background = Brushes.DodgerBlue, Width = 80, Margin = new Thickness(0, 0, 10, 0) }; Grid.SetRow(titleText, 0); Grid.SetRow(saveBtn, 0); titleGrid.Children.Add(titleText); titleGrid.Children.Add(saveBtn); titleBorder.Child = titleGrid; var scrollViewer = new ScrollViewer { MaxHeight = 500, VerticalScrollBarVisibility = ScrollBarVisibility.Auto }; var contentWrapper = new WrapPanel { Orientation = Orientation.Horizontal }; contentWrapper.Children.AddRange(GetParamItems()); scrollViewer.Content = contentWrapper; stackPanel.Children.Add(titleBorder); stackPanel.Children.Add(scrollViewer); itemLayout.Child = stackPanel; Layout.Content = itemLayout; } private string GetTitle() { var type = Param.GetType(); var descriptionAttribute = type.GetCustomAttribute(); var tableAttribute = type.GetCustomAttribute(); return Title ?? descriptionAttribute?.Description ?? tableAttribute?.Name ?? type.Name; } private List GetParamItems() { var context = DataContext; if (context.IsNullOrEmpty()) return []; var type = Param.GetType(); var contextProperties = context! .GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); // 从context 中找到对应的属性 var contextName = PathName ?? contextProperties.FirstOrDefault(p => p.PropertyType == type)?.Name; var properties = type .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); List panels = []; foreach (var property in properties) { // 如果标记为忽略的直接跳过 var ignore = property.GetCustomAttributes(); if (ignore.Any()) continue; var hide = property.GetCustomAttributes(); if (hide.Any()) continue; var descriptionAttribute = property.GetCustomAttribute(); var columnAttribute = property.GetCustomAttribute(); var title = descriptionAttribute?.Description ?? columnAttribute?.Name ?? property.Name; var item = new StackPanel { Orientation = Orientation.Horizontal, MinWidth = 400, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(20, 5, 20, 5) }; var titleBlock = new TextBlock { Text = title, FontSize = 14, Width = 190, Margin = new Thickness(0, 0, 10, 0), TextWrapping = TextWrapping.Wrap, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left }; item.Children.Add(titleBlock); item.Children.Add(GetValueControl(property, contextName)); panels.Add(item); } return panels; } // 根据类型不同,返回不同的控件 private Control GetValueControl(PropertyInfo property, string? contextName) { var type = property.PropertyType.Name; if (type.ToLower().Contains("enum")) { type = "Enum"; } var pro = property.GetCustomAttribute(); switch (type) { case "String": var textBox = new TextBox { Width = 200, Height = 40, IsReadOnly = pro?.IsReadOnly ?? false, TextWrapping = TextWrapping.Wrap, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center }; textBox.Bind(TextBox.TextProperty, new Binding(contextName + "." + property.Name) { Mode = BindingMode.TwoWay }); if (pro != null && pro.Width != 0) { textBox.Width = pro.Width; } if (pro != null && pro.Height != 0) { textBox.Height = pro.Height; textBox.VerticalContentAlignment = VerticalAlignment.Top; } if (pro is { IsPassword: true }) { textBox.PasswordChar = '*'; } return textBox; case "Boolean": var toggleSwitch = new ToggleSwitch { Width = 50, Height = 40, OnContent = "", OffContent = "", IsEnabled = !(pro?.IsReadOnly ?? false), Cursor = new Cursor(StandardCursorType.Hand), VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center }; toggleSwitch.Bind(ToggleButton.IsCheckedProperty, new Binding(contextName + "." + property.Name) { Mode = BindingMode.TwoWay }); return toggleSwitch; case "Enum": var comboBox = new ComboBox { Width = 200, Height = 40, IsEnabled = !(pro?.IsReadOnly ?? false), VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center }; var fields = property.PropertyType.GetFields(); var items = fields.Select(p => { var description = p.GetCustomAttribute(); return description?.Description ?? p.Name; }).Where(p => p != "value__").ToList(); comboBox.ItemsSource = items; comboBox.Bind(SelectingItemsControl.SelectedIndexProperty, new Binding(contextName + "." + property.Name) { Mode = BindingMode.TwoWay }); return comboBox; case "Int32": case "Int64": var numericUpDown = new NumericUpDown { Width = 200, Height = 40, ShowButtonSpinner = false, FormatString = "F0", IsReadOnly = pro?.IsReadOnly ?? false, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center }; numericUpDown.Bind(NumericUpDown.ValueProperty, new Binding(contextName + "." + property.Name) { Mode = BindingMode.TwoWay }); return numericUpDown; case "Float": case "Double": case "Decimal": var format = pro?.Format ?? "0.###"; var @decimal = new NumericUpDown { Width = 200, Height = 40, ShowButtonSpinner = false, FormatString = format, IsReadOnly = pro?.IsReadOnly ?? false, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center }; @decimal.Bind(NumericUpDown.ValueProperty, new Binding(contextName + "." + property.Name) { Mode = BindingMode.TwoWay }); return @decimal; default: var defalutText = new TextBox { Width = 200, Height = 40, IsReadOnly = pro?.IsReadOnly ?? false, TextWrapping = TextWrapping.Wrap, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center }; defalutText.Bind(TextBox.TextProperty, new Binding(contextName + "." + property.Name) { Mode = BindingMode.TwoWay }); return defalutText; } } }