Вопрос

Я хочу, чтобы во время загрузки приложения отображалась заставка.У меня есть форма с привязанным к ней элементом управления на панели задач.Я хочу, чтобы экран-заставка отображался во время загрузки этой формы, что занимает некоторое время, поскольку он обращается к API веб-службы для заполнения некоторых раскрывающихся списков.Я также хочу провести базовое тестирование зависимостей перед загрузкой (то есть веб-сервис доступен, файл конфигурации доступен для чтения).По ходу каждого этапа запуска я хочу обновлять заставку с прогрессом.

Я много читал о многопоточности, но теряюсь в том, откуда этим следует управлять ( main() метод?).мне тоже не хватает как Application.Run() работает, отсюда следует создавать темы для этого?Вот если форма с элементом управления в системном трее является "живой" формой, то заставка должна идти оттуда?Разве он все равно не загрузится, пока форма не будет заполнена?

Мне не нужен раздаточный материал по коду, а скорее алгоритм/подход, чтобы я мог разобраться в этом раз и навсегда :)

Это было полезно?

Решение

Что ж, для приложения 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#.

В конце концов, это все ИЛ и 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

ФормаСплэш.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();
    }
}

Программа.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 Terminal Server основная форма приложения отображалась (после заставки) в фоновом режиме, а панель задач мигала.А выведение окна на передний план/фокус в коде – это совсем другая банка червей, для которой вы можете использовать Google/SO.

Это старый вопрос, но я постоянно сталкивался с ним, пытаясь найти решение для многопоточного экрана-заставки для WPF, которое могло бы включать анимацию.

Вот что я в итоге собрал воедино:

Приложение.XAML:

<Application Startup="ApplicationStart" …

Приложение.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();
  }

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

Цитирую MSDN:

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

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

Я думаю, используя какой-то метод, например аку или Ребята это правильный путь, но из конкретных примеров следует вынести несколько вещей:

  1. Основной предпосылкой было бы как можно скорее показать вашу заставку в отдельной теме.Я бы наклонился именно так, как показано на рисунке Аку, поскольку именно этот способ мне наиболее знаком.Я не знал о функции VB, упомянутой Гаем.И даже подумал, что это ВБ библиотека, он прав — в конце концов, это все ИЛ.Итак, даже если это кажется грязный не все так плохо!:) Я думаю, вам нужно быть уверенным, что либо VB предоставляет отдельный поток для этого переопределения, либо вы создаете его самостоятельно - обязательно изучите это.

  2. Предполагая, что вы создаете еще один поток для отображения этой заставки, вам следует быть осторожным с обновлениями пользовательского интерфейса между потоками.Я поднял этот вопрос, потому что вы упомянули прогресс в обновлении.По сути, на всякий случай вам нужно вызвать функцию обновления (которую вы создаете) в форме-заставке с помощью делегата.Вы передаете этот делегат в Вызов функция на объекте формы вашего экрана-заставки.Фактически, если вы вызываете форму-заставку напрямую, чтобы обновить в ней элементы прогресса/UI, вы получите исключение, если вы работаете в среде .Net 2.0 CLR.Как правило, любой элемент пользовательского интерфейса в форме должен обновляться потоком, который его создал — именно это и гарантирует 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() в этом потоке.Application.Run(myMainForm) в конце выполняет две вещи:

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

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

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

Я опубликовал статью о включении заставки в приложение на сайте 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();
}

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

Мне очень нравится ответ Аку, но код предназначен для C# 3.0 и выше, поскольку он использует лямбда-функцию.Для людей, которым нужно использовать код C# 2.0, вот код, использующий анонимный делегат вместо лямбда-функции.Вам нужна самая верхняя форма Winform под названием formSplash с FormBorderStyle = NoneTopMost = 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 секунду, и обработчиком событий Shown, который занимает 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.

На самом деле многопоточность здесь не нужна.

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

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

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

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

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

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top