Dispenser/DispenserUI/Views/Controls/SettingEditor.axaml.cs

380 lines
13 KiB
C#

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;
/// <summary>
/// 参数配置编辑器
/// </summary>
public partial class SettingEditor : UserControl
{
public event EventHandler<object> OnSaveClicked;
public static readonly StyledProperty<object> ParamProperty =
AvaloniaProperty.Register<SettingEditor, object>(nameof(Param));
public static readonly StyledProperty<string> TitleProperty =
AvaloniaProperty.Register<SettingEditor, string>(nameof(Title));
public static readonly StyledProperty<string?> PathNameProperty =
AvaloniaProperty.Register<SettingEditor, string?>(nameof(PathName));
public SettingEditor()
{
InitializeComponent();
Layout = this.FindControl<ScrollViewer>("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);
}
/// <summary>
/// 渲染UI控件
/// </summary>
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<DescriptionAttribute>();
var tableAttribute = type.GetCustomAttribute<TableAttribute>();
return Title ?? descriptionAttribute?.Description ?? tableAttribute?.Name ?? type.Name;
}
private List<StackPanel> 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<StackPanel> panels = [];
foreach (var property in properties)
{
// 如果标记为忽略的直接跳过
var ignore = property.GetCustomAttributes<IgnoreAttribute>();
if (ignore.Any()) continue;
var hide = property.GetCustomAttributes<Hide>();
if (hide.Any()) continue;
var descriptionAttribute = property.GetCustomAttribute<DescriptionAttribute>();
var columnAttribute = property.GetCustomAttribute<ColumnAttribute>();
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<Property>();
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<DescriptionAttribute>();
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;
}
}
}