1

TabStripコントロールを備えたWinformsアプリケーションがあります。実行時に、UserControlsは動的にさまざまなタブにロードされます。

UserControlがロードされる前、およびロードが完全に完了するまで、「User Control xyz isloading」メッセージをユーザーに表示したい(既存のラベルを表示に設定し、そのテキストを変更する)。

これまでの私のアプローチ:

  1. BackgroundWorkerスレッドでユーザーコントロールを読み込もうとしています。UserControlのロード中にGui-Controlsにアクセスする必要があるため、これは失敗します
  2. BackgroundWorkerスレッドでメッセージを表示しようとしています。BackgroundWorkerスレッドはUIスレッドではないため、これは明らかに失敗します;-)
  3. メッセージを表示し、DoEvents()を呼び出し、UserControlをロードします。これにより、UserControlをロードするたびに異なる動作(ちらつきなど)が発生し、いつどのように非表示に戻すかを制御できなくなります。

要約すると、2つの質問があります。

  1. ユーザーコントロールをロードする前に、メッセージが直接表示されるようにする方法
  2. UserControlが完全にロードされた瞬間にメッセージが再び非表示に設定されるようにする方法(すべてのDataBinding、グリッドフォーマットなどを含む)
4

3 に答える 3

3

私たちが使用するものはこれに似ています:

  1. ユーザーに表示したいものをすべて含む新しいフォームを作成し、
  2. メモリリークを防ぐために、このフォームを呼び出して内部に作成できる静的メソッドを実装します
  3. このフォーム内に新しいスレッドを作成して、フォームが分離されたスレッドで実行され、応答性を維持できるようにします。プログレスバーがいっぱいになるのを示すajaxコントロールを使用します。

スレッドを開始するために使用するメソッド内で、そのプロパティを最上位の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);
}
于 2012-08-08T09:49:47.327 に答える
1

2番目のアプローチを再試行してください。

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を更新できます。

于 2012-08-08T09:53:45.640 に答える
1

@wterbeekの例を参考にして、自分の目的のためにクラスを変更しました。

  • ローディングフォームの中央に配置します
  • その不透明度の変更
  • 親のサイズに合わせてサイズを変更する
  • ダイアログとして表示し、すべてのユーザー操作をブロックします
  • スロバーを表示する必要がありました

行でnullエラーを受け取りました:

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();
    }
}
于 2013-03-28T07:27:42.343 に答える