-
方法一:BackgroundWorker
解释参考MSDN和百度百科:BackgroundWorker是·net里用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面 (UI) 始终处于停止响应状态。如果您需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用BackgroundWorker类方便地解决问题。
意思就是:可以做一些非用户界面的工作,缺点就是里面不能有UI的活(结束后可以)。添加UI控件会报“单线程STA***”错误。
【2024/8/26】Dispatcher.BeginInvoke?
下面实际使用示例代码:
var waiting = new Waiting();// 动画,自定义
if (Application.Current != null && Application.Current.MainWindow != null)
{
waiting.Owner = Application.Current.MainWindow;
}
waiting.backgroundWorker1.DoWork += BackgroundWorker_DoWork;// 后台的活(非UI)
waiting.ShowDialog();
waiting.Close();
BackgroundWorker简洁使用,检索很多例子:
backgroundWorker1 = new BackgroundWorker();
//backgroundWorker1 .DoWork += backgroundWorker1_DoWork;
//backgroundWorker1 .WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
if (!this.backgroundWorker1.IsBusy)
{
this.backgroundWorker1.RunWorkerAsync();
}
Waiting交互完整代码:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[2024/8/26] 中间想要停止,可以使用ProgressChanged,配合CancellationPending属性使用。
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = (BackgroundWorker)sender;
while (!worker.CancellationPending)
{
Thread.Sleep(500);
if (_caches == null) continue;
foreach (var ri in _caches)
{
var cachelist = _dataLogic.Service.GetRealtime(ri.CachesId);
if (cachelist == null || cachelist.Rows.Count == 0) continue;
cachelist.DefaultView.Sort = "TriggerTime desc";
DataTable maxTriggerTimes = cachelist.DefaultView.ToTable();
if (maxTriggerTimes == null || maxTriggerTimes.Rows.Count <= 0) continue;
foreach (RowColumnInfo rc in ri.RowColumnInfos)
{
Thread.Sleep(10);
Dispatcher.BeginInvoke(new Action<int, int, object>(
delegate (int row, int column, object value)
{
if (_view != null)
{
_view.SetCellValueInISheet(_activeWorksheet, value, row, column);
_view.GridModelList[_activeWorksheet].InvalidateCell(GridRangeInfo.Cell(row, column));
}
}), new object[] { rc.RowIndex, rc.ColumnIndex, maxTriggerTimes.Rows[0]["Value"] });
}
Thread.Sleep(10);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public BackgroundWorker backgroundWorker1;
public Waiting()
{
InitializeComponent();
SolidColorBrush solid = new SolidColorBrush();
solid.Color = Colors.White;
solid.Opacity = 0.5;
Header = IOResource.Testing;
this.Background = solid;
this.DataContext = this;
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
if (!this.backgroundWorker1.IsBusy)
{
this.backgroundWorker1.RunWorkerAsync();
}
}
/// <summary>
/// Gets or sets the Header of the waiting
/// </summary>
/// <value>
/// The default value is null.
/// </value>
public object Header
{
get { return (object)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
///<summary>
/// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(object), typeof(Waiting), new PropertyMetadata(null));
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}
public void SetProgressText(string desc)
{
this.Dispatcher.Invoke(new Action(() =>
{
Header = desc;
}));
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (!this.backgroundWorker1.IsBusy)
{
this.backgroundWorker1.RunWorkerAsync();
}
}
带入UI元素的截图忘记保留,以后再加上。
-
方法二:自定义等待动画方式
等待动画的要义就是动画占用UI线程,时间较长的活放在后台做,自己个人做的等待动画,不会报“单线程STA***”错误。和上面比较各有优缺点。可以多次调用不用担心,在遇见多次弹窗的情况。
主要方法:
public static void BusyIndicatorWindow(Action actualAction, string header)
{
try
{
Action action = (() =>
{
try
{
actualAction.Invoke();
}
catch (Exception)
{ }
BusyWindow.BusyIndicator.EndCloseBusyIndicator();
});
Action busyaction = (() =>
{
BusyWindow.BusyIndicator.OpenBusyIndicator(true, header);
});
System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(busyaction, System.Windows.Threading.DispatcherPriority.Normal);
System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(action, System.Windows.Threading.DispatcherPriority.Background);
}
catch (Exception ex)
{
Action action = (() =>
{
BusyWindow.BusyIndicator.EndCloseBusyIndicator();
});
System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(action, System.Windows.Threading.DispatcherPriority.Background);
}
}
下面是使用示例代码:
var view = new View(model);
// 等待动画
Action action = (() =>
{
// 长时间任务
});
BusyWindow.BusyIndicator.BusyIndicatorWindow(action, Resources.Resource.ProjectLoading);// 动画等待,执行完action主体
BusyIndicator主要代码:
public static void OpenBusyIndicator(bool isBusy, string header, AnimationTypes animationType = AnimationTypes.SingleCircle)
{
_isBusy = isBusy;
_header = header;
_animationType = animationType;
_busyStart = true;
ShowBusyIndicator();
}
/// <summary>
/// 打开动画
/// </summary>
public static void ShowBusyIndicator()
{
if (_busyStart)
{
try
{
if (_busyWindows != null)
{
CloseBusyIndicator();
if (_busyWindows != null)
{
return;
}
}
if (thread == null)
{
thread = new Thread(() =>
{
try
{
if (_busyWindows == null)
{
_busyWindows = new BusyWindows(_isBusy, _header, null, _animationType);
_busyWindows.ShowDialog();
}
}
catch()
{
_busyWindows = null;
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (_busyWindows == null) // 这个一定要有
{
Thread.Sleep(1);
}
}
}
catch (Exception ex)
{
Logger.Warn("BusyIndicator ShowBusyIndicator:" + ex.Message + Environment.NewLine + ex.StackTrace);
CloseBusyIndicator();
}
}
}
/// <summary>
/// 结束动画
/// </summary>
public static void EndCloseBusyIndicator()
{
try
{
CloseBusyIndicator();
}
catch (Exception) { }
_busyStart = false;
}
以上个人总结,仅供参考。