Tempos atrás eu escrevi um artigo que mostrava como exibir splash screens estendidas em aplicativos para a Windows Store. O código foi disponibilizado na MSDN Code Gallery e uma das questões que foram levantadas nos comentários foi: como fazer para que essa splash screen estendida seja exibida somente uma vez? E é isso que veremos no artigo de hoje.

Muitas vezes temos que executar diversos procedimentos pesados na inicialização de nossos aplicativos. É justamente por isso que foram criadas as splash screens. Ela é uma tela (ou janela) que é exibida para o usuário enquanto ele espera que o aplicativo fique pronto para o uso. Porém, em algumas situações, somente a primeira inicialização do aplicativo é mais pesada. Por exemplo, se estamos criando a estrutura de um banco de dados local que será utilizado pela aplicação, ou se estamos fazendo alguma configuração específica dependendo de alguma informação do computador em que a aplicação está sendo executada. Esse cenário pode fazer com que queiramos exibir uma splash screen somente na primeira execução do aplicativo.

No caso de aplicativos para a Windows Store, o usuário (e o desenvolvedor) não tem escolha sobre a exibição ou não das splash screens. A engine do Windows exibirá obrigatoriamente a splash screen do aplicativo, que nada mais é do que uma tela com uma cor única de fundo e uma imagem no meio. Porém, ao implementarmos splash screens estendidas, podemos limitar para que elas sejam exibidas somente uma vez. Na primeira execução do aplicativo nós podemos armazenar uma flag nas settings do aplicativo indicando que ele já foi inicializado uma vez. Dessa forma, antes de exibir a splash screen estendida, podemos verificar se essa flag já foi armazenada ou não, e então podemos decidir se exibiremos a splash screen estendida ou não.

Criando a splash screen estendida

Não vou entrar em detalhes sobre como criar a splash screen estendida, uma vez que já detalhei um passo-a-passo no outro artigo. Por incrível que pareça, apesar do artigo ter sido escrito há quase dois anos atrás e após vários updates para o Windows 8 (e sua plataforma de desenvolvimento) já terem sido lançados, o exemplo ainda funciona perfeitamente na versão atual do Windows 8.1.

Com um projeto de “Blank App” para a Windows Store criado, adicione uma nova Page chamada SplashPage. Nela, ajuste o código do Grid conforme apresentado abaixo:

    <Grid Background="#464646">
        <Image x:Name="SplashImage"
               Source="Assets/SplashScreen.png"
               VerticalAlignment="Center"
               HorizontalAlignment="Center"
               Height="300"
               Width="620" />
        <ProgressRing x:Name="SplashProgress"
                      IsActive="True"
                      Height="40"
                      Width="40"
                      Foreground="White"
                      HorizontalAlignment="Center"
                      Margin="0,520,0,230" />
    </Grid>

Feito isso, ajuste o code-behind dessa página conforme a listagem a seguir:

    public sealed partial class SplashPage : Page
    {
        public SplashPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Atributo que armazena uma referência à SplashScreen da aplicacao. Esse atributo é inicializado no construtor da SplashPage.
        /// </summary>
        private Windows.ApplicationModel.Activation.SplashScreen _splash;

        /// <summary>
        /// Handler para o evento SizeChanged da SplashPage. Aqui nós chamamos o método PosicionarImagem, que vai posicionar apropriadamente a SplashImage
        /// de acordo com o estado visual corrente (snapped, full, etc).
        /// </summary>
        /// <param name="sender">O objeto que invocou o evento SizeChanged.</param>
        /// <param name="e">Informacoes sobre o evento SizeChanged.</param>
        void SplashPage_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
        {
            PosicionarImagem();
        }

        /// <summary>
        /// Posiciona a SplashImage apropriadamente de acordo com o estado visual corrente (snapped, full, etc).
        /// </summary>
        void PosicionarImagem()
        {
            if (_splash != null)
            {
                Rect splashImageRect = _splash.ImageLocation;

                SplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
                SplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
                SplashImage.Height = splashImageRect.Height;
                SplashImage.Width = splashImageRect.Width;
            }
        }

        /// <summary>
        /// Frame que será criado no construtor da SplashPage para ser utilizado posteriormente na navegação à página inicial do aplicativo.
        /// </summary>
        Frame rootFrame;

        /// <summary>
        /// Construtor que adicionalmente recebe a SplashScreen da aplicação.
        /// </summary>
        /// <param name="splash">A SplashScreen da aplicação.</param>
        public SplashPage(Windows.ApplicationModel.Activation.SplashScreen splash)
            : this()
        {
            Window.Current.SizeChanged += SplashPage_SizeChanged;

            _splash = splash;
            if (_splash != null)
            {
                _splash.Dismissed += DismissedEventHandler;
                PosicionarImagem();
            }

            // Criando um Frame no construtor para ser utilizado na navegação para a página principal do aplicativo.
            rootFrame = new Frame();
        }

        /// <summary>
        /// Handler assíncrono que fará o trabalho de carregamento dos recursos da aplicação e navegará para a página principal quando tiver concluído.
        /// </summary>
        /// <param name="sender">A SplashScreen da aplicação.</param>
        /// <param name="e">Informações sobre o evento Dismissed da SplashScreen.</param>
        async void DismissedEventHandler(Windows.ApplicationModel.Activation.SplashScreen sender, object e)
        {
            // Neste ponto você deve fazer o carregamento de recursos da sua aplicação.
            // Como se trata de um exemplo, estamos somente fazendo a thread esperar 3 segundos antes de prosseguir.
            new System.Threading.ManualResetEvent(false).WaitOne(3000);

            // Quando o carregamento estiver concluído, navegamos para a página principal do aplicativo.
            await rootFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                rootFrame.Navigate(typeof(MainPage), true);
                Window.Current.Content = rootFrame;
            });
        }
    }

Por fim, vá até o arquivo App.xaml.cs e substitua o código do método OnLaunched seguindo o modelo abaixo:

        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            if (args.PreviousExecutionState != ApplicationExecutionState.Running)
            {
                bool loadState = (args.PreviousExecutionState == ApplicationExecutionState.Terminated);
                SplashPage extendedSplash = new SplashPage(args.SplashScreen);
                Window.Current.Content = extendedSplash;
            }

            Window.Current.Activate();
        }

Execute a aplicação e veja que a splash screen estendida é exibida por três segundos antes da MainPage ser exibida. Como mencionei anteriormente, para encontrar maiores informações sobre esse procedimento, confira o artigo original onde eu abordei esse código.

Configurando para que a splash screen seja exibida somente uma vez

Agora que temos o exemplo da splash screen estendida funcionando corretamente, vamos alterá-lo para que essa splash screen estendida seja exibida somente na primeira vez que o aplicativo for executado. Para isso, vamos checar se a flagAplicativoJaInicializado” se encontra presente nas settings do aplicativo. Caso positivo, nós não fazemos a inicialização. Caso negativo, fazemos a inicialização e configuramos essa flag nas settings locais do aplicativo.

Veja como fica o código do método DismissedEventHandler da SplashPage após essas alterações:

        async void DismissedEventHandler(Windows.ApplicationModel.Activation.SplashScreen sender, object e)
        {
            // Conferindo se a splash screen já não foi exibida alguma outra vez anteriormente.
            // Fazemos isso checando se a flag "AplicativoJaInicializado" está presente nas settings da aplicação.
            // Só faremos a inicialização se ela ainda não tiver ocorrido, caso contrário, navegaremos diretamente para a MainPage.
            if (!Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey("AplicativoJaInicializado"))
            {
                // Neste ponto você deve fazer o carregamento de recursos da sua aplicação.
                // Como se trata de um exemplo, estamos somente fazendo a thread esperar 3 segundos antes de prosseguir.
                new System.Threading.ManualResetEvent(false).WaitOne(3000);

                // E então, após ter feito a inicialização da aplicação, vamos adicionar a setting "AplicativoJaInicializado" para que a inicialização
                // não ocorra novamente nas próximas execuções da aplicação.
                Windows.Storage.ApplicationData.Current.LocalSettings.Values["AplicativoJaInicializado"] = true;
            }

            // Quando o carregamento estiver concluído, navegamos para a página principal do aplicativo.
            await rootFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                rootFrame.Navigate(typeof(MainPage), true);
                Window.Current.Content = rootFrame;
            });
        }

Observe que, para lidarmos com as settings locais de um aplicativo para a Windows Store, basta utilizarmos a propriedade Windows.Storage.ApplicationData.Current.LocalSettings. Nesse nosso caso é importante que utilizemos as settings locais (e não as settings de roamingRoamingSettings), uma vez que o aplicativo deve ser inicializado pelo menos uma vez em cada um dos computadores que ele for instalado. Caso utilizemos erroneamente as RoamingSettings, não obteremos o resultado esperado.

Execute o aplicativo uma vez e veja que a splash screen estendida será exibida. Feche-o e execute novamente e veja que ela será somente exibida por uma fração de segundos. Isso se deve ao fato que implementamos o código para checar essa flag na própria SplashPage. Caso queiramos que realmente a splash screen estendida não seja exibida de forma alguma (nem por uma fração de segundos), então temos que implementar essa lógica no método OnLaunched da classe App. Veja como ficaria nesse caso:

        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            if (args.PreviousExecutionState != ApplicationExecutionState.Running)
            {
                // Navegando para a SplashPage caso o aplicativo ainda não tenha sido inicializado anteriormente.
                if (!Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey("AplicativoJaInicializado"))
                {
                    bool loadState = (args.PreviousExecutionState == ApplicationExecutionState.Terminated);
                    SplashPage extendedSplash = new SplashPage(args.SplashScreen);
                    Window.Current.Content = extendedSplash;

                    // E então, vamos adicionar a setting "AplicativoJaInicializado" para que a splash screen estendida não seja exibida novamente nas próximas execuções da aplicação.
                    Windows.Storage.ApplicationData.Current.LocalSettings.Values["AplicativoJaInicializado"] = true;
                }
                // Caso contrário (aplicação já foi inicializada anteriormente em uma execução passada), prosseguimos com o processo normal de mostrar a MainPage diretamente.
                else
                {
                    Frame rootFrame = Window.Current.Content as Frame;

                    // Do not repeat app initialization when the Window already has content,
                    // just ensure that the window is active
                    if (rootFrame == null)
                    {
                        // Create a Frame to act as the navigation context and navigate to the first page
                        rootFrame = new Frame();
                        // Set the default language
                        rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

                        rootFrame.NavigationFailed += OnNavigationFailed;

                        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                        {
                            //TODO: Load state from previously suspended application
                        }

                        // Place the frame in the current Window
                        Window.Current.Content = rootFrame;
                    }

                    if (rootFrame.Content == null)
                    {
                        // When the navigation stack isn't restored navigate to the first page,
                        // configuring the new page by passing required information as a navigation
                        // parameter
                        rootFrame.Navigate(typeof(MainPage), args.Arguments);
                    }
                }
            }

            Window.Current.Activate();
        }

Porém, caso você queira utilizar essa segunda alternativa do código no método OnLaunched, é importante que você desfaça as alterações na SplashPage para que o código funcione conforme o esperado.

Testando a splash screen estendida outra vez

Um problema que você pode ter ao implementar essa solução é que só é possível testá-la uma vez. Isso se deve ao fato que, uma vez adicionada a flagAplicativoJaInicializado“, a splash screen estendida nunca mais será exibida. Para fazer com que essa flag seja resetada, basta ir até a tela inicial do Windows, acessar a lista com todas as aplicações e escolher para que ela seja desinstalada. Por exemplo, caso o nome do seu projeto seja “App12“, basta clicar com o botão direito nesse item e escolher a opção “Uninstall“:

Concluindo

A funcionalidade de splash screen estendida em aplicativos para a Windows Store muitas vezes se torna necessária. Ao executarmos passos pesados na inicialização do aplicativo, é essencial que o aplicativo exiba essa extensão da splash screen original (mesmo porque o aplicativo não passará na aprovação para a Windows Store caso contrário). Porém, se a inicialização só ocorre uma vez, faz sentido querermos que a splash screen estendida seja exibida também só uma vez. E com o tutorial desse artigo você aprendeu como ajustar o exemplo de splash screen estendida para que ela se comporte dessa forma.

Semana que vem eu volto com mais um artigo relacionado ao desenvolvimento de aplicativos para a Windows Store. Convido você a acessar o meu site caso queira acompanhar outros artigos sobre temas gerais voltados ao desenvolvimento de aplicativos com a plataforma Microsoft.

Até a próxima!

André Lima