加载用户控件时显示“用户控件正在加载”消息

时间:2012-08-08 09:37:51

标签: c# winforms multithreading

我有一个带TabStrip控件的Winforms应用程序。在运行时,UserControls将动态加载到不同的选项卡中。

我想在加载UserControl之前向用户呈现“User Control xyz正在加载”消息(将现有标签设置为可见并更改其文本),直到加载完成。

到目前为止我的方法:

  1. 尝试在BackgroundWorker线程中加载用户控件。这失败了,因为我必须在加载UserControl期间访问Gui-Controls
  2. 尝试在BackgroundWorker线程中显示消息。这显然是失败的,因为BackgroundWorker线程不是UI线程; - )
  3. 显示消息,调用DoEvents(),加载UserControl。每次加载UserControl时都会导致不同的行为(闪烁,......),我无法控制何时以及如何将其设置为不可见。
  4. 总结一下,我有两个问题:

    1. 如何确保在加载用户控件之前直接显示消息
    2. 如何确保消息再次设置为不可见,就在用户控件完全加载的那一刻(包括所有数据绑定,网格格式等)。

3 个答案:

答案 0 :(得分:2)

我们使用的与此类似:

  1. 创建一个新表单,其中包含您要向用户显示的内容,
  2. 实现一个静态方法,您可以在其中调用此表单以在其内部创建,以防止内存泄漏
  3. 在此表单中创建一个新线程,以便表单在一个单独的线程中运行并保持响应;我们使用ajax控件来显示进度条填满。
  4. 在用于启动线程的方法中,将其属性设置为最高,以确保它保持在最顶层。

    例如,

    以您的主要形式执行此操作:

    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);
    }
    

答案 1 :(得分:1)

再试一次你的第二种方法:

  

尝试在BackgroundWorker线程中显示消息。这显然是失败的,因为BackgroundWorker线程不是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。

答案 2 :(得分:1)

从@ 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();
    }
}