ProgramingTip

C #의 다중 시작 화면?

bestdevel 2020. 12. 3. 08:17
반응형

C #의 다중 시작 화면?


응용 프로그램이로드되는 동안 시작 화면을 표시하고 싶습니다. 시스템 트레이 컨트롤이 양식이 있습니다. 이 양식이로드되는 동안 스플래시 화면이 표시되기를 원합니다. 이는 일부 드롭 다운을 채우기 위해 웹 서비스 API에 액세스하기 때문에 약간의 시간이 있습니다. 또한로드하기 전에 몇 가지 기본 테스트를 수행하고 싶습니다 (즉, 웹 서비스를 사용할 수있는 구성 파일을 읽을 수 있음). 시작의 각 단계가 진행됨에 따라 스플래시 화면을 진행률로 업데이트하고 싶습니다.

나는 스레딩에 대해 많이 읽었다는 것이 어디에서 제어되어야하는지 ( main()방법?) 에서 길을 잃고 있습니다 . 나는 또한 Application.Run()작동 작동 하는지 여부를 나타냅니다. 이제 시스템 트레이 컨트롤이있는 양식이 "살아있는"양식 인 경우 스플래시가 거기에서 나와야를 가지고 있습니까? 어쨌든 양식이 완료 될 때까지되지 않았습니까?

나는 코드 유인물, 더 많은 알고리즘을 찾을 수 있습니다. :)


이전에 배포 한 ClickOnce 앱의 경우 Microsoft.VisualBasic네임 스페이스를 사용하여 스플래시 화면 스레딩을 처리했습니다. Microsoft.VisualBasic.NET 2.0에서 C # 어셈블리를 참조하고 사용할 수 있으며 많은 멋진 서비스를 제공합니다.

  1. 기본 양식을 상속받습니다. Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. 다음과 같이 "OnCreateSplashScreen"메서드를 재정의합니다.

    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

매우 간단하게 로딩이 진행되는 동안 SplashForm (생성해야하는)을 보여주고 메인 폼이 로딩을 완료하면 자동으로 닫습니다.

이것은 정말 일을 간단하게 만들고 VisualBasic.WindowsFormsApplicationBaseMicrosoft에서 잘 테스트하고 100 % C # 인 응용 프로그램에서 Winforms에서 삶을 쉽게 만들 수있는 많은 기능을 가지고 있습니다.

하루가 모든 것이 IL이고 bytecode어쨌든 사용하지 않는 이유는 무엇입니까?


트릭은 스플래시 화면 표시를 담당하는 부가를 만드는 것입니다.
실행하면 앱 .net이 메인을 생성하고 지정된 (메인) 양식을로드합니다. 어려운 작업을 숨기려면 기본로드가 완료 될 때까지 기본 양식을 숨길 수 있습니다.

Form1이 기본 양식이고 SplashForm이 최상위 수준이라고 가정하면 멋진 스플래시 양식이 있습니다.

private void Form1_Load(object sender, EventArgs e)
{
    Hide();
    bool done = false;
    ThreadPool.QueueUserWorkItem((x) =>
    {
        using (var splashForm = new SplashForm())
        {
            splashForm.Show();
            while (!done)
                Application.DoEvents();
            splashForm.Close();
        }
    });

    Thread.Sleep(3000); // Emulate hardwork
    done = true;
    Show();
}

솔루션을 찾기 위해 Google과 SO 전체를 본 후 가장 좋아하는 것입니다. http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs :

public partial class FormSplash : Form
{
    private static Thread _splashThread;
    private static FormSplash _splashForm;

    public FormSplash() {
        InitializeComponent();
    }

    /// <summary>
    /// Show the Splash Screen (Loading...)
    /// </summary>
    public static void ShowSplash()
    {
        if (_splashThread == null)
        {
            // show the form in a new thread
            _splashThread = new Thread(new ThreadStart(DoShowSplash));
            _splashThread.IsBackground = true;
            _splashThread.Start();
        }
    }

    // called by the thread
    private static void DoShowSplash()
    {
        if (_splashForm == null)
            _splashForm = new FormSplash();

        // create a new message pump on this thread (started from ShowSplash)
        Application.Run(_splashForm);
    }

    /// <summary>
    /// Close the splash (Loading...) screen
    /// </summary>
    public static void CloseSplash()
    {
        // need to call on the thread that launched this splash
        if (_splashForm.InvokeRequired)
            _splashForm.Invoke(new MethodInvoker(CloseSplash));

        else
            Application.ExitThread();
    }
}

Program.cs :

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // splash screen, which is terminated in FormMain
        FormSplash.ShowSplash();

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        // this is probably where your heavy lifting is:
        Application.Run(new FormMain());
    }
}

FormMain.cs

    ...

    public FormMain()
    {
        InitializeComponent();            

        // bunch of database access, form loading, etc
        // this is where you could do the heavy lifting of "loading" the app
        PullDataFromDatabase();
        DoLoadingWork();            

        // ready to go, now close the splash
        FormSplash.CloseSplash();
    }

Microsoft.VisualBasic솔루션에 문제가 . XP에서 찾기 작업을했지만 Windows 2003 터미널 서버에서는 기본 응용 프로그램 표시 양식이 백그라운드에 표시되고 작업을 줄이 깜박입니다. 그리고 코드에서 창을 전경 / 포커스로 가져 오는 것은 Google / SO에서 사용할 수있는 완전히 다른 웜입니다.


이건 오래된 질문이지만 애니메이션을 포함 할 수있는 WPF 용 스플래시 화면 솔루션을 설치하고 할 때 계속 해서이 문제를 발견했습니다.

내가 함께 모은 것은 다음과 같다.

App.XAML :

<Application Startup="ApplicationStart" …

App.XAML.cs :

void ApplicationStart(object sender, StartupEventArgs e)
{
        var thread = new Thread(() =>
        {
            Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
            Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        // call synchronous configuration process
        // and declare/get reference to "main form"

        thread.Abort();

        mainForm.Show();
        mainForm.Activate();
  }

aku가 제공하는 답변 Activate();에서 마지막 바로 전화하는 것이 좋습니다 Show();.

MSDN 인용 :

활성 응용 프로그램 인 경우 양식을 활성화하면 맨 앞으로 가져오고 활성 응용 프로그램이 아닌 경우 창 캡션이 깜박입니다. 이 방법이 적용 되려면 양식이 필요합니다.

기본 양식을 활성화하지 열려있는 다른 창 에서 표시 되어 약간의 어리석게 보일 수 있습니다.


나는 aku 또는 Guy 와 같은 방법을 사용 하는 것이 갈 길이 라고 생각 하지만 특정 예제에서 몇 가지를 제거해야합니다.

  1. 기본 전 가능한 한 빨리 별도의 문법에 스플래시를 표시하는 것입니다. 그것은 내가 가장 익숙한 방식이기 때문에 방식으로 방식으로 기울이는 방식입니다. 나는 Guy가 언급 한 VB 기능을 몰랐습니다. 그리고 그것이 VB 라이브러리 라고 생각했을 때 그는 옳습니다. 결국 모든 IL입니다. 그래서 더럽다고 생각지 편안하게 나쁘게 생각합니다! :) VB가 해당 오버라이드에 대해 생성의 구체적인 내용을 제공하는지 또는 직접 확인하고 싶을 것입니다. 확실히 연구하십시오.

  2. 이 스플래시를 표시하기 위해 다른 작성을 생성합니다. 업데이트 진행 상황을 언급했기 때문에 문제를 제기했습니다. 기본적으로 안전을 위해 델리게이트를 사용하여 스플래시 폼에서 업데이트 함수 (생성 한)를 호출해야합니다. 스플래시 화면의 양식 개체에 있는 Invoke 함수에 해당 대리 전달 합니다. 실제로 스플래시 양식을 직접 호출하여 진행률 / UI 요소를 업데이트하면 .Net 2.0 CLR에서 실행중인 경우 예외가 발생합니다. 경험상 폼의 모든 UI 요소는 폼을 만든 모든에서 업데이트해야합니다. 이것이 바로 Form.Invoke가 보장하는 것입니다.

마지막으로 코드의 기본 메소드에서 스플래시 (VB 오버로드를 사용하지 않는 경우)를 생성 할 가능성이 있습니다. 나에게 이것은 메인 폼이 생성의 생성을 수행하고 너무 묶이는 것보다 낫습니다. 기존 접근 방식을 취한다면 멤버 함수를 통해 시작 진행률 업데이트를 수신하는 스플래시 화면이 구현하는 간단한 인터페이스 (예 : IStartupProgressListener)를 만드는 것이 좋습니다. 이렇게하면 필요에 따라 두 클래스 중 하나를 쉽게 교체하고 코드를 멋지게 분리 할 수 ​​있습니다. 시작이 완료되면 알림을 보내면 스플래시 양식이 자동으로 닫히는 알림을 알 수 있습니다.


한 가지 간단한 방법은 다음과 같이 main ()으로 사용하는 것입니다.

<STAThread()> Public Shared Sub Main()

    splash = New frmSplash
    splash.Show()

    ' Your startup code goes here...

    UpdateSplashAndLogMessage("Startup part 1 done...")

    ' ... and more as needed...

    splash.Hide()
    Application.Run(myMainForm)
End Sub

.NET CLR이 응용 프로그램을 시작하면 'main'스레드를 만들고 해당 스레드에서 main () 실행을 시작합니다. 마지막에있는 Application.Run (myMainForm)은 두 가지 작업을 수행합니다.

  1. main ()을 GUI 스레드로 실행 한 스레드를 사용하여 Windows '메시지 펌프'를 시작합니다.
  2. 애플리케이션의 '종료 양식'으로 '기본 양식'을 지정합니다. 사용자가 해당 양식을 닫으면 Application.Run ()이 종료되고 제어가 main ()으로 돌아갑니다. 여기서 원하는 종료를 수행 할 수 있습니다.

스플래시 창을 처리하기 위해 스레드를 생성 할 필요가 없습니다. 실제로 이것은 나쁜 생각입니다. 왜냐하면 main ()에서 스플래시 내용을 업데이트하려면 스레드로부터 안전한 기술을 사용해야하기 때문입니다.

애플리케이션에서 백그라운드 작업을 수행하기 위해 다른 스레드가 필요한 경우 main ()에서 생성 할 수 있습니다. Thread.IsBackground를 True로 설정하는 것을 잊지 마십시오. 그러면 메인 / GUI 스레드가 종료 될 때 죽게됩니다. 그렇지 않으면 다른 모든 스레드를 직접 종료해야합니다. 그렇지 않으면 메인 스레드가 종료 될 때 응용 프로그램을 활성 상태로 유지해야합니다 (GUI없이).


codeproject의 응용 프로그램에서 스플래시 화면 통합에 대한 기사를 게시했습니다. 다중 스레드이며 관심이있을 수 있습니다.

C #의 또 다른 시작 화면


private void MainForm_Load(object sender, EventArgs e)
{
     FormSplash splash = new FormSplash();
     splash.Show();
     splash.Update();
     System.Threading.Thread.Sleep(3000);
     splash.Hide();
}

인터넷 어딘가에서 이것을 받았지만 다시 찾을 수없는 것 같습니다. 간단하지만 효과적입니다.


나는 Aku의 대답을 많이 좋아하지만 람다 함수를 사용하기 때문에 코드는 C # 3.0 이상입니다. C # 2.0에서 코드를 사용해야하는 사람들을 위해 다음은 람다 함수 대신 익명 대리자를 사용하는 코드입니다. formSplash와 함께 호출되는 최상위 winform이 필요합니다 FormBorderStyle = None. TopMost = True양식 매개 변수는 스플래시 화면이 나타나는 것처럼 보일 수 있고 맨 위에 있지 않으면 빠르게 사라질 수 있으므로 중요합니다. 또한 StartPosition=CenterScreen전문 앱이 스플래시 화면에서 수행하는 작업처럼 보이도록 선택합니다 . 더 시원한 효과를 원한다면 TrasparencyKey속성을 사용하여 불규칙한 모양의 스플래시 화면을 만들 수 있습니다.

private void formMain_Load(object sender, EventArgs e)
  {

     Hide();
     bool done = false;
     ThreadPool.QueueUserWorkItem(delegate
     {
       using (formSplash splashForm = new formSplash())
       {
           splashForm.Show();
           while (!done)
              Application.DoEvents();
           splashForm.Close();
       }
     }, null);

     Thread.Sleep(2000);
     done = true;
     Show();
  }

추천하는 다른 답변에 동의하지 않습니다 WindowsFormsApplicationBase. 내 경험상 앱 속도가 느려질 수 있습니다. 정확히 말하면 스플래시 화면과 병렬로 양식의 생성자를 실행하는 동안 양식의 Shown 이벤트를 연기합니다.

1 초가 소요되는 생성자와 2 초가 소요되는 Shown의 이벤트 핸들러가있는 앱 (스플래시 화면 없음)을 고려하십시오. 이 앱은 3 초 후에 사용할 수 있습니다.

그러나을 사용하여 스플래시 화면을 설치한다고 가정합니다 WindowsFormsApplicationBase. MinimumSplashScreenDisplayTime3 초가 합리적이고 앱 속도를 늦추지 않을 것이라고 생각할 수 있습니다 . 하지만 시도해보세요. 이제 앱을로드하는 데 5 초가 걸립니다.


class App : WindowsFormsApplicationBase
{
    protected override void OnCreateSplashScreen()
    {
        this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
        this.SplashScreen = new Splash();
    }

    protected override void OnCreateMainForm()
    {
        this.MainForm = new Form1();
    }
}

public Form1()
{
    InitializeComponent();
    Shown += Form1_Shown;
    Thread.Sleep(TimeSpan.FromSeconds(1));
}

void Form1_Shown(object sender, EventArgs e)
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
    Program.watch.Stop();
    this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}

결론 : WindowsFormsApplicationBase앱에 Slown 이벤트에 대한 핸들러가있는 경우 사용하지 마십시오 . 생성자와 Shown 이벤트 모두에 대해 병렬로 스플래시를 실행하는 더 나은 코드를 작성할 수 있습니다.


실제로 여기서 다중 스레딩은 필요하지 않습니다.

스플래시 화면을 업데이트 할 때마다 비즈니스 로직이 이벤트를 생성하도록하십시오.

그런 다음 양식이 eventhandler에 연결된 메서드에 따라 스플래시 화면을 업데이트하도록합니다.

업데이트를 구별하기 위해 다른 이벤트를 발생 시키거나 EventArgs에서 상속 된 클래스에 데이터를 제공 할 수 있습니다.

이렇게하면 멀티 스레딩 문제없이 멋진 스플래시 화면을 변경할 수 있습니다.

실제로 이것으로 스플래시 형태의 gif 이미지를 지원할 수도 있습니다. 작동하려면 핸들러에서 Application.DoEvents ()를 호출하십시오.

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}

참고 URL : https://stackoverflow.com/questions/48916/multi-threaded-splash-screen-in-c

반응형