как правильно начать и убить поток формы [duplicate]

Обновление для CMake 3.0 и новее:

продолжение строки возможно с \. см. cmake-3.0-doc

message("\
This is the first line of a quoted argument. \
In fact it is the only line but since it is long \
the source code uses line continuation.\
")

Доступность версий CMake:

Debian Wheezy (2013): 2.8.9 Debian Wheezy-backports: 2.8 .11 Debian Jessy (2015): 3.0.2 Ubuntu 14.04 (LTS): 2.8.12 Ubuntu 15.04: 3.0.2 Mac OSX: cmake-3 доступен через Homebrew , Macports и Fink Windows: cmake-3 доступен через Chocolatey

57
задан Peter Mortensen 15 June 2012 в 12:26
поделиться

12 ответов

Ну, для приложения ClickOnce, которое я развернул в прошлом, мы использовали пространство имен Microsoft.VisualBasic для обработки потока заставки. Вы можете ссылаться и использовать сборку Microsoft.VisualBasic из C # в .NET 2.0, и она предоставляет множество приятных сервисов.

  1. Наследует основную форму от Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. Переопределите метод OnCreateSplashScreen следующим образом:
    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

Очень просто, он показывает вашу SplashForm (которую вы должны создать) во время загрузки, а затем автоматически закрывает ее после того, как основная форма завершила загрузку.

Это действительно делает вещи простыми, и VisualBasic.WindowsFormsApplicationBase, конечно, хорошо протестирован Microsoft и имеет множество функций, которые могут сделать вашу жизнь намного проще в Winforms, даже в приложении, которое является 100% C #.

В конце концов, это все IL и байт-код в любом случае, так почему бы не использовать его?

46
ответ дан Andrei Rînea 26 August 2018 в 13:09
поделиться

Просматривая все решения 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.

13
ответ дан Adam Nofsinger 26 August 2018 в 13:09
поделиться

Хитрость заключается в том, чтобы создать отдельный поток, отвечающий за отображение заставки. При запуске приложения .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();
}
46
ответ дан aku 26 August 2018 в 13:09
поделиться

Мне очень нравится ответ Aku, но код для C # 3.0 и выше, поскольку он использует функцию лямбда. Для людей, нуждающихся в использовании кода в C # 2.0, вот код, использующий анонимный делегат вместо функции лямбда. Вам нужна самая верхняя winform с именем formSplash с 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();
  }
3
ответ дан Christian Specht 26 August 2018 в 13:09
поделиться

Я не согласен с другими ответами, рекомендующими WindowsFormsApplicationBase. По моему опыту, это может замедлить ваше приложение. Если быть точным, в то время как он запускает конструктор формы параллельно с заставкой, он откладывает событие Shown.

Рассмотрите приложение (без экрана всплесков) с конструктором, который занимает 1 секунду, и обработчик события на Показано, что занимает 2 секунды. Это приложение можно использовать через 3 секунды.

Но предположим, что вы устанавливаете заставку с помощью WindowsFormsApplicationBase. Вы можете подумать, что MinimumSplashScreenDisplayTime из 3 секунд разумно и не замедлит ваше приложение. Но попробуйте, ваше приложение теперь займет 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.

2
ответ дан Colonel Panic 26 August 2018 в 13:09
поделиться

Я думаю, что использование некоторого метода, такого как aku's или Guy's , - это путь, но несколько вещей, которые нужно убрать из конкретных примеров:

  1. Основной предпосылкой было бы как можно скорее показать ваш всплеск на отдельном потоке. Это то, как я наклоняюсь, похоже на то, что иллюстрирует аку, так как это то, с чем я больше всего знаком. Я не знал о функции VB, о которой говорил Гай. И даже подумал, что это библиотека VB , он прав - это все ИЛ в конце. Таким образом, даже если он чувствует себя грязным , это не так уж плохо! :) Я думаю, вам нужно быть уверенным, что либо VB предоставляет отдельный поток для этого переопределения, либо тот, который вы сами создаете, - определенно исследуйте это.
  2. Предполагая, что вы создаете другой поток для отображения этого всплеска , вы захотите быть осторожным с обновлением пользовательских интерфейсов. Я рассказываю об этом, потому что вы упомянули о ходе обновления. В принципе, чтобы быть в безопасности, вам нужно вызвать функцию обновления (которую вы создаете) в форме всплеска с помощью делегата. Вы передаете этот делегат функции Invoke на вашем объекте формы заставки. Фактически, если вы вызываете брызговую форму непосредственно для обновления элементов прогресса / пользовательского интерфейса на ней, вы получите исключение, если вы работаете в среде .NET Net CLR. Как правило, любой элемент пользовательского интерфейса в форме должен обновляться потоком, который его создал, - это то, что обеспечивает Form.Invoke.

Наконец, я бы предпочел создать splash (если не использовать перегрузку VB) в основном методе вашего кода. Для меня это лучше, чем если бы основная форма выполняла создание объекта и была настолько тесно связана с ним. Если вы примете такой подход, я бы предложил создать простой интерфейс, который реализует экран заставки - что-то вроде IStartupProgressListener - который получает обновления прогона обновления через функцию-член. Это позволит вам легко переключаться в любом классе по мере необходимости и красиво отделять код. Форма всплеска также может знать, когда закрыть себя, если вы сообщите, когда пуск завершен.

6
ответ дан Community 26 August 2018 в 13:09
поделиться

Я рекомендую вызывать Activate(); непосредственно после последнего Show(); в ответе, предоставленном aku.

Цитата MSDN:

Активация формы приводит ее к если это активное приложение, или мигает заголовок окна, если это не активное приложение. Форма должна быть видимой для этого эффекта.

Если вы не активируете свою основную форму, она может отображаться за любыми другими открытыми окнами , что делает его немного глупым.

8
ответ дан DelftRed 26 August 2018 в 13:09
поделиться

На самом деле mutlithreading здесь не требуется.

Пусть ваша бизнес-логика генерирует событие всякий раз, когда вы хотите обновить заставку.

Затем пусть ваша форма обновит заставку соответствующим образом в методе, подключенном к обработчику событий.

Чтобы различать обновления, вы можете запускать разные события или предоставлять данные в классе, унаследованном от EventArgs.

Таким образом, у вас может быть приятный сменный всплеск без какой-либо многопоточной головной боли.

Фактически с этим вы можете даже поддерживать, например, gif-изображение в форме всплеска. Чтобы он работал, вызовите Application.DoEvents () в вашем обработчике:

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}
0
ответ дан Empus 26 August 2018 в 13:09
поделиться

Это старый вопрос, но я все время сталкивался с ним, пытаясь найти решение для 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();
  }
8
ответ дан Jay 26 August 2018 в 13:09
поделиться

Я опубликовал статью о включении заставки в приложении в codeproject. Он многопоточен и может вас заинтересовать

Еще один экран заставки в C #

4
ответ дан K Singh 26 August 2018 в 13:09
поделиться
private void MainForm_Load(object sender, EventArgs e)
{
     FormSplash splash = new FormSplash();
     splash.Show();
     splash.Update();
     System.Threading.Thread.Sleep(3000);
     splash.Hide();
}

Я получил это из Интернета где-то, но не могу найти его снова. Простой, но эффективный.

4
ответ дан LPL 26 August 2018 в 13:09
поделиться

Один простой способ - использовать это как 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 запускает ваше приложение, он создает «основной» поток и начинает выполнение вашего основного () по этой теме. Application.Run (myMainForm) в конце делает две вещи:

  1. Запускает «насос сообщений» Windows, используя поток, который выполнял main () как поток GUI.
  2. Обозначает вашу «основную форму» как «форму выключения» для приложения. Если пользователь закрывает эту форму, то завершение и управление Application.Run () возвращается к вашему main (), где вы можете сделать любое выключение.

Нет необходимости запускать нить, чтобы заботиться о всплывающем окне, и на самом деле это плохая идея, потому что тогда вам придется использовать поточно-безопасные методы для обновления содержимого всплеска из main ().

Если вам нужно другие темы для фоновых операций в вашем приложении, вы можете создать их из main (). Не забудьте установить Thread.IsBackground в значение True, чтобы они погибли, когда поток main / GUI завершился. В противном случае вам придется договориться о завершении всех ваших других потоков самостоятельно или они будут поддерживать ваше приложение в живых (но без графического интерфейса), когда основной поток завершается.

5
ответ дан McKenzieG1 26 August 2018 в 13:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: