我有一个带有TabStrip控件的Winforms应用程序。在运行时,用户控件将动态加载到不同的选项卡中。
我想在加载用户控件之前向用户显示"用户控件 xyz 正在加载"消息(将现有标签设置为可见并更改其文本),直到加载完全完成。
到目前为止,我的方法:
- 尝试在后台工作线程中加载用户控件。这失败了,因为我必须在加载用户控件期间访问 Gui-Controls
- 尝试在后台工作线程中显示消息。这显然失败了,因为后台工作线程不是 UI 线程;-)
- 显示消息,调用 DoEvents(),加载用户控件。这会导致每次加载UserControl时的不同行为(闪烁等),并且我无法控制何时以及如何将其再次设置为不可见。
总结一下,我有两个问题:
- 如何在加载用户控件之前确保消息直接可见
- 如何确保消息再次设置为不可见,就在用户控件完全加载的那一刻(包括所有数据绑定、网格格式等)
我们使用的类似于这样:
- 创建一个新表单,其中包含要向用户显示的任何内容,
- 实现一个静态方法,您可以在其中调用此表单以在自身内部创建,以防止内存泄漏
- 在此窗体中创建一个新线程,以便窗体在单独的线程中运行并保持响应;我们使用一个 Ajax 控件来显示进度条填满。
在用于启动线程的方法中,将其属性设置为 topmost true,以确保它保持在顶部。
例如,在您的主窗体中执行此操作:
loadingForm.ShowLoadingScreen("usercontrollname");
//do something
loadingform.CloseLoadingScreen();
在加载表单类中;
public LoadingScreen()
{
InitializeComponent();
}
public static void ShowLoadingScreen(string usercontrollname)
{
// do something with the usercontroll name if desired
if (_LoadingScreenThread == null)
{
_LoadingScreenThread = new Thread(new ThreadStart(DoShowLoadingScreen));
_LoadingScreenThread.IsBackground = true;
_LoadingScreenThread.Start();
}
}
public static void CloseLoadingScreen()
{
if (_ls.InvokeRequired)
{
_ls.Invoke(new MethodInvoker(CloseLoadingScreen));
}
else
{
Application.ExitThread();
_ls.Dispose();
_LoadingScreenThread = null;
}
}
private static void DoShowLoadingScreen()
{
_ls = new LoadingScreen();
_ls.FormBorderStyle = FormBorderStyle.None;
_ls.MinimizeBox = false;
_ls.ControlBox = false;
_ls.MaximizeBox = false;
_ls.TopMost = true;
_ls.StartPosition = FormStartPosition.CenterScreen;
Application.Run(_ls);
}
再试一次你的第二种方法:
尝试在后台工作线程中显示消息。这显然失败了,因为后台工作线程不是 UI 线程;-)
但这一次,请在后台线程中使用以下代码来更新标签:
label.Invoke((MethodInvoker) delegate {
label.Text = "User Control xyz is loading";
label.Visible = true;
});
// Load your user control
// ...
label.Invoke((MethodInvoker) delegate {
label.Visible = false;
});
Invoke
允许您在另一个线程中更新 UI。
从@wterbeek的例子出发,我出于自己的目的修改了类:
- 将其居中置于加载表单上
- 修改其不透明度
- 将其大小调整为父大小
- 将其显示为对话框并阻止所有用户交互
- 我被要求表现出悸动
我在线收到空错误:
if (_ls.InvokeRequired)
所以我添加了一个_shown条件(如果动作完成得太快以至于_LoadingScreenThread线程甚至没有运行)来检查表单是否存在。
此外,如果未启动_LoadingScreenThread,Application.Exit 将关闭主线程。
我想发布它可能会帮助其他人。代码中的注释将解释更多。
public partial class LoadingScreen : Form {
private static Thread _LoadingScreenThread;
private static LoadingScreen _ls;
//condition required to check if the form has been loaded
private static bool _shown = false;
private static Form _parent;
public LoadingScreen() {
InitializeComponent();
}
//added the parent to the initializer
//CHECKS FOR NULL HAVE NOT BEEN IMPLEMENTED
public static void ShowLoadingScreen(string usercontrollname, Form parent) {
// do something with the usercontroll name if desired
_parent = parent;
if (_LoadingScreenThread == null) {
_LoadingScreenThread = new Thread(new ThreadStart(DoShowLoadingScreen));
_LoadingScreenThread.SetApartmentState(ApartmentState.STA);
_LoadingScreenThread.IsBackground = true;
_LoadingScreenThread.Start();
}
}
public static void CloseLoadingScreen() {
//if the operation is too short, the _ls is not correctly initialized and it throws
//a null error
if (_ls!=null && _ls.InvokeRequired) {
_ls.Invoke(new MethodInvoker(CloseLoadingScreen));
} else {
if (_shown)
{
//if the operation is too short and the thread is not started
//this would close the main thread
_shown = false;
Application.ExitThread();
}
if (_LoadingScreenThread != null)
_LoadingScreenThread.Interrupt();
//this check prevents the appearance of the loader
//or its closing/disposing if shown
//have not found the answer
//if (_ls !=null)
//{
_ls.Close();
_ls.Dispose();
//}
_LoadingScreenThread = null;
}
}
private static void DoShowLoadingScreen() {
_ls = new LoadingScreen();
_ls.FormBorderStyle = FormBorderStyle.None;
_ls.MinimizeBox = false;
_ls.ControlBox = false;
_ls.MaximizeBox = false;
_ls.TopMost = true;
//get the parent size
_ls.Size = _parent.Size;
//get the location of the parent in order to show the form over the
//target form
_ls.Location = _parent.Location;
//in order to use the size and the location specified above
//we need to set the start position to "Manual"
_ls.StartPosition =FormStartPosition.Manual;
//set the opacity
_ls.Opacity = 0.5;
_shown = true;
//Replaced Application.Run with ShowDialog to show as dialog
//Application.Run(_ls);
_ls.ShowDialog();
}
}