From 18174b404cf42e1477a6677e375afae1ec160997 Mon Sep 17 00:00:00 2001 From: ShrlAlgo Date: Mon, 23 Feb 2026 16:49:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86ColorPicker=EF=BC=8C=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E3=80=82=E4=BF=AE=E5=A4=8DUI=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controls/ColorPicker/ColorPanel.xaml.cs | 10 + Melskin/Controls/Expander.xaml | 265 +++++++++++++++++- Melskin/Controls/SearchableComboBox.cs | 20 +- MelskinTest/MainWindow.xaml | 4 +- ShrlAlgo.Addin.Test/ProgressManager.cs | 74 +++++ ShrlAlgo.Addin.Test/ProgressReporter.cs | 96 +++++++ ShrlAlgo.Addin.Test/ProgressWindow.xaml | 55 ++++ ShrlAlgo.Addin.Test/ProgressWindow.xaml.cs | 52 ++++ ShrlAlgo.Addin.Test/RevitAddin.cs | 49 +++- .../Common/Controls/ColorPickerViewModel.cs | 41 --- .../Common/Controls/ColorPickerWin.xaml | 59 ---- .../Common/Controls/ColorPickerWin.xaml.cs | 13 - .../Common/Controls/ProcessEventHandler.cs | 128 --------- .../Common/Controls/ProgressBarManager.cs | 168 ----------- .../Common/Controls/ProgressManager.cs | 75 +++++ .../Common/Controls/ProgressMonitorView.xaml | 36 --- .../Controls/ProgressMonitorView.xaml.cs | 25 -- .../Controls/ProgressMonitorViewModel.cs | 30 -- .../Common/Controls/ProgressReporter.cs | 38 +++ .../Common/Controls/ProgressViewModel.cs | 64 +++++ .../Common/Controls/ProgressWindow.xaml | 58 ++++ .../Common/Controls/ProgressWindow.xaml.cs | 53 ++++ .../Common/Converters/Rv2WinColorConverter.cs | 9 +- .../DrawSheet/SystemDisplayCmd.cs | 4 +- .../DrawSheet/SystemDisplayView.xaml | 15 +- .../DrawSheet/SystemDisplayViewModel.cs | 73 ++--- .../FamMaster/UpgradeFamilyCmd.cs | 39 ++- .../ShrlAlgoToolkit.RevitAddins.csproj | 5 +- .../Extensions/TransactionExtensions.cs | 46 +++ 29 files changed, 1028 insertions(+), 576 deletions(-) create mode 100644 ShrlAlgo.Addin.Test/ProgressManager.cs create mode 100644 ShrlAlgo.Addin.Test/ProgressReporter.cs create mode 100644 ShrlAlgo.Addin.Test/ProgressWindow.xaml create mode 100644 ShrlAlgo.Addin.Test/ProgressWindow.xaml.cs delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ColorPickerViewModel.cs delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ColorPickerWin.xaml delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ColorPickerWin.xaml.cs delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProcessEventHandler.cs delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressBarManager.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressManager.cs delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressMonitorView.xaml delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressMonitorView.xaml.cs delete mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressMonitorViewModel.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressReporter.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressViewModel.cs create mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressWindow.xaml create mode 100644 ShrlAlgoToolkit.RevitAddins/Common/Controls/ProgressWindow.xaml.cs diff --git a/Melskin/Controls/ColorPicker/ColorPanel.xaml.cs b/Melskin/Controls/ColorPicker/ColorPanel.xaml.cs index 353a802..3a94b2f 100644 --- a/Melskin/Controls/ColorPicker/ColorPanel.xaml.cs +++ b/Melskin/Controls/ColorPicker/ColorPanel.xaml.cs @@ -229,6 +229,8 @@ public class ColorPanel : Control { SelectPresetColorCommand = new RelayCommand(ExecuteSelectPresetColor); Loaded += OnPanelLoaded; + // 拦截面板内所有未处理的左键点击事件,防止穿透给其他控件(如DataGrid)导致焦点切换问题 + this.MouseLeftButtonDown += (s, e) => e.Handled = true; } /// @@ -317,8 +319,15 @@ public class ColorPanel : Control Debug.Assert(colorCanvas != null, nameof(colorCanvas) + " != null"); if (colorCanvas == null) return; colorCanvas.CaptureMouse(); + // 先解绑再绑定,防止重复点击时引发的多次订阅问题 + colorCanvas.MouseMove -= ColorCanvas_MouseMove; + colorCanvas.MouseUp -= ColorCanvas_MouseUp; + colorCanvas.MouseMove += ColorCanvas_MouseMove; colorCanvas.MouseUp += ColorCanvas_MouseUp; + + // 标记事件为已处理,防止冒泡触发 DataGrid 的焦点切换 + //e.Handled = true; } private void ColorCanvas_MouseUp(object sender, MouseButtonEventArgs e) @@ -329,6 +338,7 @@ public class ColorPanel : Control colorCanvas.ReleaseMouseCapture(); colorCanvas.MouseMove -= ColorCanvas_MouseMove; colorCanvas.MouseUp -= ColorCanvas_MouseUp; + //e.Handled = true; } private void ColorCanvas_MouseMove(object sender, MouseEventArgs e) diff --git a/Melskin/Controls/Expander.xaml b/Melskin/Controls/Expander.xaml index 54bcdf3..a69c2cf 100644 --- a/Melskin/Controls/Expander.xaml +++ b/Melskin/Controls/Expander.xaml @@ -23,6 +23,7 @@ + + + @@ -108,6 +112,7 @@ + + + @@ -179,6 +187,7 @@ + + + @@ -254,6 +266,7 @@ + + + @@ -298,7 +314,7 @@ - + + \ No newline at end of file diff --git a/Melskin/Controls/SearchableComboBox.cs b/Melskin/Controls/SearchableComboBox.cs index c7bb7cc..0852f0b 100644 --- a/Melskin/Controls/SearchableComboBox.cs +++ b/Melskin/Controls/SearchableComboBox.cs @@ -13,8 +13,8 @@ namespace Melskin.Controls /// public class SearchableComboBox : ComboBox { - private TextBox _editableTextBox; - private bool _isInternalOperation; + private TextBox editableTextBox; + private bool isInternalOperation; static SearchableComboBox() { @@ -41,30 +41,30 @@ namespace Melskin.Controls protected override void OnSelectionChanged(SelectionChangedEventArgs e) { - if (_isInternalOperation) return; + if (isInternalOperation) return; - _isInternalOperation = true; + isInternalOperation = true; base.OnSelectionChanged(e); // 重点 1:选中项改变后,必须彻底清空过滤器,否则下次打开下拉框只剩下一项 this.Items.Filter = null; - _isInternalOperation = false; + isInternalOperation = false; } private void OnEditableTextBoxTextChanged(object sender, TextChangedEventArgs e) { - if (_isInternalOperation) return; + if (isInternalOperation) return; - string searchText = _editableTextBox.Text; + string searchText = editableTextBox.Text; string selectedText = GetItemDisplayText(SelectedItem); // 重点 2:断开锁定逻辑 // 如果文本框的内容和当前选中项的文本不一致,说明用户正在打字替换内容 if (SelectedItem != null && searchText != selectedText) { - _isInternalOperation = true; + isInternalOperation = true; SelectedItem = null; // 必须将 SelectedItem 设为 null,否则 WPF 会强行还原文本 - _isInternalOperation = false; + isInternalOperation = false; } // 重点 3:执行过滤 @@ -79,7 +79,7 @@ namespace Melskin.Controls }; // 自动打开下拉框 - if (!IsDropDownOpen && _editableTextBox.IsFocused && !string.IsNullOrEmpty(searchText)) + if (!IsDropDownOpen && editableTextBox.IsFocused && !string.IsNullOrEmpty(searchText)) { IsDropDownOpen = true; } diff --git a/MelskinTest/MainWindow.xaml b/MelskinTest/MainWindow.xaml index d68bac0..bdbb8d7 100644 --- a/MelskinTest/MainWindow.xaml +++ b/MelskinTest/MainWindow.xaml @@ -632,7 +632,9 @@ - + + + diff --git a/ShrlAlgo.Addin.Test/ProgressManager.cs b/ShrlAlgo.Addin.Test/ProgressManager.cs new file mode 100644 index 0000000..171e812 --- /dev/null +++ b/ShrlAlgo.Addin.Test/ProgressManager.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace ShrlAlgo.Addin.Test +{ + public static class ProgressManager + { + /// + /// 启动带进度条的 Revit 主线程任务 + /// + /// 进度条窗口标题 + /// 需要在 Revit 主线程执行的任务逻辑 + public static void Run(string title, Action revitTask) + { + var cts = new CancellationTokenSource(); + var viewModel = new ProgressViewModel(title, cts); + var reporter = new ProgressReporter(viewModel); + + // 使用 ManualResetEvent 等待 WPF 窗口初始化完成 + using (var viewReadyEvent = new ManualResetEvent(false)) + { + // 1. 创建独立的 STA 线程运行 WPF UI + var uiThread = new Thread(() => + { + viewModel.UIDispatcher = Dispatcher.CurrentDispatcher; + var window = new ProgressWindow(viewModel); + window.Show(); + + viewReadyEvent.Set(); // 通知主线程 UI 已就绪 + + Dispatcher.Run(); // 开启消息循环 + }); + + uiThread.SetApartmentState(ApartmentState.STA); // 必须是 STA + uiThread.IsBackground = true; + uiThread.Start(); + + viewReadyEvent.WaitOne(); // 阻塞主线程,直到 WPF 窗体显示 + + // 2. 在 Revit 主线程中执行耗时任务 + try + { + revitTask(reporter, cts.Token); + } + catch (OperationCanceledException) + { + // 捕获取消异常,静默处理即可 + } + catch (Exception ex) + { + Autodesk.Revit.UI.TaskDialog.Show("Task Error", ex.Message); + } + finally + { + // 3. 任务结束,通知 WPF 线程关闭窗口并停止调度器 + if (viewModel.UIDispatcher != null && !viewModel.UIDispatcher.HasShutdownStarted) + { + viewModel.UIDispatcher.InvokeAsync(() => + { + viewModel.CloseAction?.Invoke(); + Dispatcher.CurrentDispatcher.InvokeShutdown(); + }); + } + uiThread.Join(1000); // 等待 UI 线程彻底退出 + cts.Dispose(); + } + } + } + } +} diff --git a/ShrlAlgo.Addin.Test/ProgressReporter.cs b/ShrlAlgo.Addin.Test/ProgressReporter.cs new file mode 100644 index 0000000..f79e75c --- /dev/null +++ b/ShrlAlgo.Addin.Test/ProgressReporter.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace ShrlAlgo.Addin.Test +{ + // 用于给外界调用的报告器 + public class ProgressReporter + { + private readonly ProgressViewModel _viewModel; + private DateTime _lastUpdate = DateTime.MinValue; + + public ProgressReporter(ProgressViewModel viewModel) + { + _viewModel = viewModel; + } + + /// + /// 更新进度 (自带节流控制,防止频繁更新导致UI卡顿) + /// + public void Report(int current, int total, string message = null) + { + // 限制 UI 刷新频率(每 50 毫秒最多刷新一次) + if ((DateTime.Now - _lastUpdate).TotalMilliseconds < 50 && current != total) + return; + + _lastUpdate = DateTime.Now; + _viewModel.Update(current, total, message); + } + + public void ReportMessage(string message) => _viewModel.UpdateMessage(message); + } + + // WPF 的 ViewModel + public class ProgressViewModel : INotifyPropertyChanged + { + private readonly CancellationTokenSource _cts; + public Dispatcher UIDispatcher { get; set; } + public Action CloseAction { get; set; } + + public ProgressViewModel(string title, CancellationTokenSource cts) + { + Title = title; + _cts = cts; + } + + private string _title; + public string Title { get => _title; set => SetProperty(ref _title, value); } + + private string _message = "正在准备..."; + public string Message { get => _message; set => SetProperty(ref _message, value); } + + private double _progressValue; + public double ProgressValue { get => _progressValue; set => SetProperty(ref _progressValue, value); } + + private double _maximum = 100; + public double Maximum { get => _maximum; set => SetProperty(ref _maximum, value); } + + public void Cancel() + { + if (!_cts.IsCancellationRequested) + { + Message = "正在取消任务,请稍候..."; + _cts.Cancel(); + } + } + + // 跨线程更新 UI + public void Update(int current, int total, string message) + { + UIDispatcher?.InvokeAsync(() => + { + ProgressValue = current; + Maximum = total; + if (!string.IsNullOrEmpty(message)) Message = message; + }); + } + + public void UpdateMessage(string message) + { + UIDispatcher?.InvokeAsync(() => Message = message); + } + + public event PropertyChangedEventHandler PropertyChanged; + protected void SetProperty(ref T field, T value, [CallerMemberName] string propertyName = null) + { + field = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/ShrlAlgo.Addin.Test/ProgressWindow.xaml b/ShrlAlgo.Addin.Test/ProgressWindow.xaml new file mode 100644 index 0000000..af1f06e --- /dev/null +++ b/ShrlAlgo.Addin.Test/ProgressWindow.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + +