using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using HandyControl.Controls; namespace TeamAAS.Dialogs.Dialogs { /// /// 基于 PropertyGridLib 的通用属性编辑对话框。 /// 将任意对象传入 SelectedObject,PropertyGrid 自动生成编辑界面。 /// 可选执行按钮:传入 ExecuteAction 后显示"执行"按钮,点击后台执行不卡UI,执行中变为"停止"可取消。 /// public partial class PropertyGridDialog { /// 用户是否点击了确定 public bool Result { get; private set; } /// 执行回调(对象, 取消令牌),传入则显示执行按钮 public Action ExecuteAction { get; set; } private CancellationTokenSource _cts; private bool _isExecuting; /// 最近一次持久提示。悬停提示结束后回落到这里。 private string _lastTip = string.Empty; /// 当前编辑的对象 public object SelectedObject { get => PropertyGrid.SelectedObject; set { PropertyGrid.SelectedObject = value; Title = $"属性编辑 - {value?.GetType().Name ?? ""}"; } } /// 自定义窗口标题 public string DialogTitle { get => Title; set => Title = value; } public PropertyGridDialog() { InitializeComponent(); UiScaler.Attach(this); // 分辨率/DPI 自适应 Closing += (s, e) => { if (_isExecuting) e.Cancel = true; }; // 回车 = 确认(执行中除外) PreviewKeyDown += (s, e) => { if (e.Key == System.Windows.Input.Key.Enter && !_isExecuting) { BtnOK.RaiseEvent(new RoutedEventArgs(System.Windows.Controls.Button.ClickEvent)); e.Handled = true; } }; } /// /// 设置编辑对象和标题。 /// public void Setup(object obj, string title = null) { PropertyGrid.SelectedObject = obj; Title = title ?? $"属性编辑 - {obj?.GetType().Name ?? ""}"; } /// /// 设置编辑对象、标题和执行回调。 /// public void Setup(object obj, string title, Action executeAction) { PropertyGrid.SelectedObject = obj; Title = title ?? $"属性编辑 - {obj?.GetType().Name ?? ""}"; ExecuteAction = executeAction; if (executeAction != null) BtnExecute.Visibility = Visibility.Visible; } /// /// 设置底部提示栏的持久提示(线程安全)。null/空 = 显示最近一次持久提示。 /// public void ShowTip(string message) { void Apply() { if (string.IsNullOrWhiteSpace(message)) { TxtTip.Text = _lastTip; return; } _lastTip = message; TxtTip.Text = message; } if (Dispatcher.CheckAccess()) Apply(); else Dispatcher.Invoke(Apply); } /// /// 显示鼠标悬停临时提示(线程安全)。message 为 null/空时结束悬停,回落到最近一次持久提示。 /// public void ShowHoverHint(string message) { void Apply() { TxtTip.Text = string.IsNullOrWhiteSpace(message) ? _lastTip : message; } if (Dispatcher.CheckAccess()) Apply(); else Dispatcher.Invoke(Apply); } /// 窗体自带按钮的悬停提示:把 Tag 文本写入底部提示栏 private void BtnTip_MouseEnter(object sender, MouseEventArgs e) { if ((sender as FrameworkElement)?.Tag is string tip && !string.IsNullOrWhiteSpace(tip)) TxtTip.Text = tip; } /// 悬停结束:回落到最近一次持久提示 private void BtnTip_MouseLeave(object sender, MouseEventArgs e) { TxtTip.Text = _lastTip; } private void BtnExecute_Click(object sender, RoutedEventArgs e) { if (_isExecuting) { // 正在执行 → 停止 _cts?.Cancel(); return; } // 开始执行 _cts = new CancellationTokenSource(); _isExecuting = true; BtnExecute.Content = "停止"; BtnExecute.Style = FindResource("ButtonDanger") as System.Windows.Style; PropertyGrid.IsEnabled = false; BtnOK.IsEnabled = false; BtnCancel.IsEnabled = false; var token = _cts.Token; var obj = PropertyGrid.SelectedObject; Task.Run(() => { try { ExecuteAction?.Invoke(obj, token); } catch (OperationCanceledException) { } catch (Exception ex) { Dispatcher.Invoke(() => Growl.Error($"执行失败: {ex.Message}")); } }).ContinueWith(t => { Dispatcher.Invoke(() => { _isExecuting = false; BtnExecute.Content = "执行"; BtnExecute.Style = FindResource("ButtonInfo") as System.Windows.Style; PropertyGrid.IsEnabled = true; BtnOK.IsEnabled = true; BtnCancel.IsEnabled = true; if (t.IsCanceled) Growl.Info("已停止"); }); }); } private void BtnOK_Click(object sender, RoutedEventArgs e) { _cts?.Cancel(); // 强制让 PropertyGrid 中的所有编辑控件提交修改 // 关键:PropertyGridLib 的 ComboBox 使用 LostFocus 作为 UpdateSourceTrigger // 所以需要在关闭前确保所有下拉框关闭且焦点移出 // 1. 关闭所有打开的 ComboBox 下拉框 CloseAllComboBoxes(PropertyGrid); // 2. 临时将焦点移到按钮上,这会触发 PropertyGrid 内部控件的 LostFocus 事件 BtnOK.Focus(); // 3. 等待一个 Dispatcher 帧,让绑定更新生效 Dispatcher.Invoke(() => { }, System.Windows.Threading.DispatcherPriority.Input); Result = true; Close(); } /// /// 递归关闭所有 ComboBox 的下拉框 /// private static void CloseAllComboBoxes(DependencyObject root) { if (root == null) return; int count = VisualTreeHelper.GetChildrenCount(root); for (int i = 0; i < count; i++) { var child = VisualTreeHelper.GetChild(root, i); if (child is System.Windows.Controls.ComboBox combo && combo.IsDropDownOpen) { combo.IsDropDownOpen = false; } CloseAllComboBoxes(child); } } private void BtnCancel_Click(object sender, RoutedEventArgs e) { _cts?.Cancel(); Result = false; Close(); } } }