清理ColorPicker,进度条。修复UI问题
This commit is contained in:
74
ShrlAlgo.Addin.Test/ProgressManager.cs
Normal file
74
ShrlAlgo.Addin.Test/ProgressManager.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动带进度条的 Revit 主线程任务
|
||||
/// </summary>
|
||||
/// <param name="title">进度条窗口标题</param>
|
||||
/// <param name="revitTask">需要在 Revit 主线程执行的任务逻辑</param>
|
||||
public static void Run(string title, Action<ProgressReporter, CancellationToken> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
ShrlAlgo.Addin.Test/ProgressReporter.cs
Normal file
96
ShrlAlgo.Addin.Test/ProgressReporter.cs
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新进度 (自带节流控制,防止频繁更新导致UI卡顿)
|
||||
/// </summary>
|
||||
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<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
field = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
55
ShrlAlgo.Addin.Test/ProgressWindow.xaml
Normal file
55
ShrlAlgo.Addin.Test/ProgressWindow.xaml
Normal file
@@ -0,0 +1,55 @@
|
||||
<Window
|
||||
x:Class="ShrlAlgo.Addin.Test.ProgressWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ShrlAlgo.Addin.Test"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{Binding Title}"
|
||||
Width="450"
|
||||
Height="180"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="ToolWindow"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 状态文本 -->
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,10"
|
||||
FontSize="14"
|
||||
Text="{Binding Message}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<!-- 进度条 -->
|
||||
<ProgressBar
|
||||
Grid.Row="1"
|
||||
Height="25"
|
||||
Maximum="{Binding Maximum}"
|
||||
Value="{Binding ProgressValue}" />
|
||||
|
||||
<!-- 进度数字与取消按钮 -->
|
||||
<Grid Grid.Row="2" Margin="0,15,0,0">
|
||||
<TextBlock VerticalAlignment="Center" Foreground="Gray">
|
||||
<Run Text="{Binding ProgressValue, Mode=OneWay, StringFormat={}{0:0}}" />
|
||||
<Run Text=" / " />
|
||||
<Run Text="{Binding Maximum, Mode=OneWay, StringFormat={}{0:0}}" />
|
||||
</TextBlock>
|
||||
|
||||
<Button
|
||||
Width="80"
|
||||
Height="30"
|
||||
HorizontalAlignment="Right"
|
||||
Click="BtnCancel_Click"
|
||||
Content="取 消" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
52
ShrlAlgo.Addin.Test/ProgressWindow.xaml.cs
Normal file
52
ShrlAlgo.Addin.Test/ProgressWindow.xaml.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ShrlAlgo.Addin.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// ProgressWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ProgressWindow : Window
|
||||
{
|
||||
private readonly ProgressViewModel _viewModel;
|
||||
|
||||
public ProgressWindow(ProgressViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
_viewModel = viewModel;
|
||||
DataContext = _viewModel;
|
||||
|
||||
// 绑定关闭委托
|
||||
_viewModel.CloseAction = this.Close;
|
||||
}
|
||||
|
||||
private void BtnCancel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.Cancel();
|
||||
(sender as System.Windows.Controls.Button).IsEnabled = false;
|
||||
}
|
||||
|
||||
// 屏蔽系统的 Alt+F4 关闭,强制必须等主线程任务结束
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
if (!_viewModel.UIDispatcher.HasShutdownStarted)
|
||||
{
|
||||
e.Cancel = true;
|
||||
_viewModel.Cancel(); // 尝试取消
|
||||
}
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,24 +9,55 @@ using Autodesk.Revit.UI.Selection;
|
||||
using Nice3point.Revit.Toolkit.External;
|
||||
|
||||
using ShrlAlgoToolkit.Core.Assists;
|
||||
using ShrlAlgoToolkit.RevitCore.Base;
|
||||
|
||||
|
||||
namespace ShrlAlgo.Addin.Test;
|
||||
|
||||
[Transaction(TransactionMode.Manual)]
|
||||
[Regeneration(RegenerationOption.Manual)]
|
||||
public class RevitAddin : ExternalCommand
|
||||
public class RevitAddin : BaseCommand
|
||||
{
|
||||
|
||||
public override void Execute()
|
||||
protected override void ExecuteCommand()
|
||||
{
|
||||
try
|
||||
// 获取文档中所有的墙
|
||||
var walls = new FilteredElementCollector(Document)
|
||||
.OfClass(typeof(Wall))
|
||||
.ToElements();
|
||||
|
||||
if (walls.Count == 0) return;
|
||||
|
||||
// 直接调用我们的进度条管理器!
|
||||
ProgressManager.Run("批量修改墙参数", (reporter, token) =>
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
// 利用之前封装的 TransactionExtensions 开启事务
|
||||
Document.InvokeInTransaction(() =>
|
||||
{
|
||||
for (int i = 0; i < walls.Count; i++)
|
||||
{
|
||||
// 1. 检查用户是否点击了“取消”
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var wall = walls[i];
|
||||
|
||||
// 2. 你的 Revit API 操作 (例如修改参数)
|
||||
var commentsParam = wall.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);
|
||||
if (commentsParam != null && !commentsParam.IsReadOnly)
|
||||
{
|
||||
commentsParam.Set($"自动写入 - {i}");
|
||||
}
|
||||
|
||||
// 3. 报告进度 (内置了防卡顿机制,每秒调用几万次都不会卡)
|
||||
reporter.Report(i + 1, walls.Count, $"正在处理墙: {wall.Name}");
|
||||
|
||||
// 如果逻辑非常非常耗时,可以考虑在这里短暂休眠让出一点点CPU(通常不用加)
|
||||
// Thread.Sleep(1);
|
||||
}
|
||||
}, "Batch Update Walls");
|
||||
});
|
||||
|
||||
// 任务结束(无论完成还是取消),后续代码继续执行
|
||||
TaskDialog.Show("提示", "操作结束或已被取消!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user