using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using HandyControl.Controls;
namespace TeamAAS.Dialogs.Dialogs
{
///
/// 通用插件视图对话框 - 承载插件自定义 UserControl。
/// 三个按钮:执行(可选)、取消、确定。
/// 用法与 PropertyGridDialog 一致,区别是用 ContentControl 替代 PropertyGrid。
///
public partial class PluginViewDialog
{
/// 用户是否点击了确定
public bool Result { get; private set; }
/// 执行回调(对象, 取消令牌),传入则显示执行按钮
public Action ExecuteAction { get; set; }
/// 当前编辑的对象(即视图的 DataContext)
public object DataContext2 { get; private set; }
private CancellationTokenSource _cts;
private bool _isExecuting;
/// 最近一次持久提示(执行结果 CT 等)。悬停提示结束后回落到这里。
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;
}
};
}
///
/// 设置插件视图、标题。
///
/// 插件自定义 UserControl
/// 窗口标题
public void Setup(FrameworkElement view, string title = null)
{
ViewHost.Content = view;
DataContext2 = view.DataContext;
Title = title ?? "插件编辑";
}
///
/// 设置插件视图、标题和执行回调。
///
public void Setup(FrameworkElement view, string title, Action executeAction)
{
ViewHost.Content = view;
DataContext2 = view.DataContext;
Title = title ?? "插件编辑";
ExecuteAction = executeAction;
if (executeAction != null)
BtnExecute.Visibility = Visibility.Visible;
}
///
/// 设置底部提示栏的持久提示(线程安全)。
/// 用于执行结果 CT、状态信息等;悬停提示结束后自动回落到最近一次持久提示。
///
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, System.Windows.Input.MouseEventArgs e)
{
if ((sender as FrameworkElement)?.Tag is string tip && !string.IsNullOrWhiteSpace(tip))
TxtTip.Text = tip;
}
/// 悬停结束:回落到最近一次持久提示
private void BtnTip_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
TxtTip.Text = _lastTip;
}
///
/// 焦点是否落在代码编辑器(ICSharpCode.AvalonEdit —— CodeForge 内部所用)内。
/// 脚本节点里敲回车是换行,不能被窗体的"回车=确定"劫持,否则一换行就关窗。
///
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();
}
}
}