| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- using System;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows;
- using HandyControl.Controls;
- namespace TeamAAS.Dialogs.Dialogs
- {
- /// <summary>
- /// 通用插件视图对话框 - 承载插件自定义 UserControl。
- /// 三个按钮:执行(可选)、取消、确定。
- /// 用法与 PropertyGridDialog 一致,区别是用 ContentControl 替代 PropertyGrid。
- /// </summary>
- public partial class PluginViewDialog
- {
- /// <summary>用户是否点击了确定</summary>
- public bool Result { get; private set; }
- /// <summary>执行回调(对象, 取消令牌),传入则显示执行按钮</summary>
- public Action<object, CancellationToken> ExecuteAction { get; set; }
- /// <summary>当前编辑的对象(即视图的 DataContext)</summary>
- public object DataContext2 { get; private set; }
- private CancellationTokenSource _cts;
- private bool _isExecuting;
- /// <summary>最近一次持久提示(执行结果 CT 等)。悬停提示结束后回落到这里。</summary>
- private string _lastTip = string.Empty;
- public PluginViewDialog()
- {
- 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 && !IsFocusInCodeEditor(e.OriginalSource))
- {
- BtnOK.RaiseEvent(new RoutedEventArgs(System.Windows.Controls.Button.ClickEvent));
- e.Handled = true;
- }
- };
- }
- /// <summary>
- /// 设置插件视图、标题。
- /// </summary>
- /// <param name="view">插件自定义 UserControl</param>
- /// <param name="title">窗口标题</param>
- public void Setup(FrameworkElement view, string title = null)
- {
- ViewHost.Content = view;
- DataContext2 = view.DataContext;
- Title = title ?? "插件编辑";
- }
- /// <summary>
- /// 设置插件视图、标题和执行回调。
- /// </summary>
- public void Setup(FrameworkElement view, string title, Action<object, CancellationToken> executeAction)
- {
- ViewHost.Content = view;
- DataContext2 = view.DataContext;
- Title = title ?? "插件编辑";
- ExecuteAction = executeAction;
- if (executeAction != null)
- BtnExecute.Visibility = Visibility.Visible;
- }
- /// <summary>
- /// 设置底部提示栏的持久提示(线程安全)。
- /// 用于执行结果 CT、状态信息等;悬停提示结束后自动回落到最近一次持久提示。
- /// </summary>
- 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);
- }
- /// <summary>
- /// 显示鼠标悬停临时提示(线程安全)。message 为 null/空时结束悬停,回落到最近一次持久提示。
- /// </summary>
- public void ShowHoverHint(string message)
- {
- void Apply()
- {
- TxtTip.Text = string.IsNullOrWhiteSpace(message) ? _lastTip : message;
- }
- if (Dispatcher.CheckAccess()) Apply(); else Dispatcher.Invoke(Apply);
- }
- /// <summary>窗体自带按钮的悬停提示:把 Tag 文本写入底部提示栏</summary>
- private void BtnTip_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
- {
- if ((sender as FrameworkElement)?.Tag is string tip && !string.IsNullOrWhiteSpace(tip))
- TxtTip.Text = tip;
- }
- /// <summary>悬停结束:回落到最近一次持久提示</summary>
- private void BtnTip_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
- {
- TxtTip.Text = _lastTip;
- }
- /// <summary>
- /// 焦点是否落在代码编辑器(ICSharpCode.AvalonEdit —— CodeForge 内部所用)内。
- /// 脚本节点里敲回车是换行,不能被窗体的"回车=确定"劫持,否则一换行就关窗。
- /// </summary>
- private static bool IsFocusInCodeEditor(object originalSource)
- {
- var dep = originalSource as DependencyObject
- ?? System.Windows.Input.Keyboard.FocusedElement as DependencyObject;
- int guard = 0;
- while (dep != null && guard++ < 40)
- {
- var ns = dep.GetType().Namespace;
- if (!string.IsNullOrEmpty(ns) && ns.StartsWith("ICSharpCode.AvalonEdit", StringComparison.Ordinal))
- return true;
- try
- {
- dep = System.Windows.Media.VisualTreeHelper.GetParent(dep)
- ?? LogicalTreeHelper.GetParent(dep);
- }
- catch { break; }
- }
- return false;
- }
- 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 Style;
- ViewHost.IsEnabled = false;
- BtnOK.IsEnabled = false;
- BtnCancel.IsEnabled = false;
- var token = _cts.Token;
- var obj = DataContext2;
- 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 Style;
- ViewHost.IsEnabled = true;
- BtnOK.IsEnabled = true;
- BtnCancel.IsEnabled = true;
- if (t.IsCanceled)
- Growl.Info("已停止");
- });
- });
- }
- private void BtnOK_Click(object sender, RoutedEventArgs e)
- {
- _cts?.Cancel();
- Result = true;
- Close();
- }
- private void BtnCancel_Click(object sender, RoutedEventArgs e)
- {
- _cts?.Cancel();
- Result = false;
- Close();
- }
- }
- }
|