windev

How to implement a bindable progress indicator (loading dots) for MVVM Windows (8.1) Universal apps

Screenshot (23)

Now that I am on a good way to understand and use the MVVM pattern, I am also finding that there are some times rather simple solutions for every day problems. One of these problems is that we don’t have a global progress indicator for Windows Universal apps. That is a little bit annoying, and so I wrote my own solution. I don’t know if this is good or bad practice, but my solution is making it globally available in a Windows Universal app. The best thing is, you just need to bind to a Boolean property to use it. No Behaviors, just the one base implementation and Binding (Yes, I am a bit excited about it). For your convenience, I attached a demo project at the end of this post.

To get the main work for this done, we are implementing our own class, inherited from the Page class. The latter one is available for Windows as well as Windows Phone, so we can define it in the shared project of our Universal app. To do so, add a new class in the shared project. I named it PageBase (as it is quite common for this scenario, as I found out).

First, we need to inherit our class from the Page class:

public abstract class PageBase : Page

Now that we have done this, we need a global available property that we can bind to. We are using a DependencyProperty to achieve this goal. To make the property reflect our changes also to the UI, we also need to hook into a PropertyChanged callback on it:

        //this DepenedencyProperty is our Binding target to get all the action done!
        public static readonly DependencyProperty IsProgressIndicatorNeededProperty = DependencyProperty.Register(
            "IsProgressIndicatorNeeded", typeof (bool), typeof (PageBase), new PropertyMetadata((bool)false, OnIsProgressIndicatorNeededChanged));

        public static void OnIsProgressIndicatorNeededChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        //get and send the value of our Binding target
        public bool IsProgressIndicatorNeeded
        {
            get { return (bool) GetValue(IsProgressIndicatorNeededProperty); }
            set { SetValue(IsProgressIndicatorNeededProperty, value); }
        }

The next step we need to do is to find the UIElement we want the progress indicator to be in. To do so, we are going through the VisualTree and pick our desired element. This helper method (taken from the MSDN documentation) will enable us to find this element:

        //helper method to find children in the visual tree (taken from MSDN documentation)
        private static void FindChildren<T>(List<T> results, DependencyObject startNode)
          where T : DependencyObject
        {
            int count = VisualTreeHelper.GetChildrenCount(startNode);
            for (int i = 0; i < count; i++)
            {
                var current = VisualTreeHelper.GetChild(startNode, i);
                if ((current.GetType()) == typeof(T) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
                {
                    T asType = (T)current;
                    results.Add(asType);
                }
                FindChildren<T>(results, current);
            }
        }

The method goes through the VisualTree, starting at the point we are throwing in as DependecyObject and gives us a List<T> with all specified Elements. From this List we are going to pick our UIElement that will hold the progress indicator for us. Let’s create a new method that will do all the work for us:

        private void CheckIfProgressIndicatorIsNeeded(DependencyObject currentObject)
        {
        }

Notice the DependencyObject parameter? This makes it easier for us to use the method in different places (which we will, more on that later). Let’s get our list of DependencyObjects from our parameter and pick the first Grid as our desired UIElement to hold the progress indicator:

            if (currentObject == null) return;

            //getting a list of all DependencyObjects in the visual tree
            var children = new List<DependencyObject>();
            FindChildren(children, currentObject);
            if (children.Count == 0) return;

            //getting a reference to the first Grid in the visual tree
            //this can be any other UIElement you define
            var rootGrid = (Grid)children.FirstOrDefault(i => i.GetType() == typeof(Grid));

Now that we have this, we are already at the point where we need to create our progress indicator object.  I declared a class member of type ProgressBar (which needs to be instantiated in the constructor then). This is how I set it up:

            //setting up the ProgressIndicator
            //you can also create a more complex object for this, like a StackPanel with a TextBlock and the ProgressIndicator in it
            _progressIndicator.IsIndeterminate = IsProgressIndicatorNeeded;
            _progressIndicator.Height = 20;
            _progressIndicator.VerticalAlignment = VerticalAlignment.Top;

The final step in the PageBase class is to check if there is already a chikd of type ProgressBar, if not adding it to the Grid and setting it’s Visibility property to Visible if our above attached DependencyProperty has the value ‘true’:

            //showing the ProgressIndicator
            if (IsProgressIndicatorNeeded)
            {
                //only add the ProgressIndicator if there isn't already one in the rootGrid
                if (!rootGrid.Children.Contains(_progressIndicator))
                {
                    rootGrid.Children.Add(_progressIndicator);
                }
                _progressIndicator.Visibility = Visibility.Visible;
            }

If the value is ‘false’, we are setting the Visibility back to collapsed:

            //hiding the ProgressIndicator
            else
            {
                if (rootGrid.Children.Contains(_progressIndicator))
                {
                    _progressIndicator.Visibility = Visibility.Collapsed;
                }
            }

Now that we have this method in place, let’s go back to our callback method we have been hooking into earlier. To reflect the changes that we are throwing into our DependencyProperty,  we are calling our method within the PropertyChanged callback. To do so, we are getting a reference to the PageBase class, which is needed because we are in a static method. Once we have this reference, we are calling our method to show/hide the progress indicator:

        public static void OnIsProgressIndicatorNeededChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //resolving d as PageBase to enable us calling our helper method
            var currentObject = d as PageBase;

            //avoid NullReferenceException
            if (currentObject == null)
            {
                return;
            }
            //call our helper method
            currentObject.CheckIfProgressIndicatorIsNeeded(d);
        }

That’s all, we are already able to use the global progress indicator. To use this class, you need to do a few things. First, go to the code-behind part of your page. Make the class inherit from the PageBase class:

    public sealed partial class MainPage : PageBase

Now, let’s go to the XAML part and add a reference to your class:

    xmlns:common="using:MvvmUniversalProgressIndicator.Common"

Once you have done this, replace the ‘Page’ element with the PageBase class:

<common:PageBase>
//
</common:PageBase>

After you have build the project, you should be able to set the Binding to the IsProgressIndicatorNeeded property:

    IsProgressIndicatorNeeded="{Binding IsProgressIndicatorVisible}">

If you now add two buttons to the mix, binding their Commands to change the value of the Boolean property, you will see that you can switch the loading dots on and off like you wish. That makes it pretty easy to use it in a MVVM driven application.

But what if we need to show the progress indicator as soon as we are coming to the page? No worries, we are already prepared and need only a little more code for that. In the PageBase class constructor, register for the Loaded event:

            Loaded += PageBase_Loaded;

In the Loaded event, we are calling again our main method to show the progress indicator, but this time we use the current window content as reference to start with:

        void PageBase_Loaded(object sender, RoutedEventArgs e)
        {
            //using the DispatcherHelper of MvvmLight to get it running on the UI
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                //Window.Current.Content is our visual root and contains all UIElements of a page
                var visualRoot = Window.Current.Content as DependencyObject;
                CheckIfProgressIndicatorIsNeeded(visualRoot);
            });

        }

As we need to reflect changes on the UI thread, I am using the DispatcherHelper of the MvvmLight Toolkit. You can use your own preferred method as well for that. That’s all, If you now test it with setting the IsProgressIndicatorNeeded property in your page directly to ‘True’ in XAML, you will see the loading dots right from the start.

Screenshot (21)

Like always, I hope this is helpful for some of you.

Happy coding!

Download Sample project

Posted by msicc in Archive, 1 comment

How to easily check the pressed keyboard button with a converted event using MVVM (Windows Universal)

In case you missed it, I lately am deeply diving into MVVM. Earlier today, I wanted to implement the well loved feature that a search is performed by pressing the Enter button. Of course, this would be very easy to achieve in code behind using the KeyUpEvent (or the KeyDownEvent, if you prefer).

However, in MVVM, especially in a Universal app, this is a bit trickier. We need to route the event manually to our matching command. There are surely more ways to achieve it, but I decided to use the Behaviors SDK to achieve my goal. The first step is of course downloading the matching extension (if you haven’t done so before). To do so, click on TOOLS/Extensions and Updates in Visual Studio and install the Behaviors SDK from the list:image

The next step we need to do is to add a new Converter (I added it to the common folder, you may change this to your preferred place). As we are hooking up the KeyUpEventArgs, I called it KeyUpEventArgsConverter. After you created the class, implement the IValueConverter interface. You should now have a Convert and a ConvertBack method. We are just adding two lines of code to the Convert method:

            var args = (KeyRoutedEventArgs)value;
            return args;

That’s it for the Converter. Save the class and build the project. For the next step, we need to go to our View where the Converter should do its work. Before we can use it, we need to give our Converter a key to be identified by the Binding engine. You can do this app wide in App.xaml, or in your page:

<common:KeyUpEventArgsConverter x:Key="KeyUpEventArgsConverter"/>

Also, we need to add two more references to our View (besides the Common folder that holds our converter, that is):

    xmlns:i="using:Microsoft.Xaml.Interactivity" 
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"

The next step is to implement the Behavior to our input control (a TextBox in my case):

<TextBox  Header="enter search terms" PlaceholderText="search terms" Text="{Binding KnowledgeBaseSearchTerms, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
    <i:Interaction.Behaviors>
         <core:EventTriggerBehavior EventName="KeyUp">
             <core:InvokeCommandAction
                   Command="{Binding SearchTermKeyEventArgsCommand}"
                   InputConverter="{StaticResource KeyUpEventArgsConverter}">
             </core:InvokeCommandAction>
          </core:EventTriggerBehavior>
     </i:Interaction.Behaviors>
</TextBox>

With the EventTriggerBehavior, we are able to hook into the desired event of a control. We then only need to bind to a Command in our ViewModel and tell the Behaviors SDK that it should route the “KeyUp” event using our Converter.

Let’s have a final look at the command that handles the event:

        public RelayCommand<KeyRoutedEventArgs> SearchTermKeyEventArgsCommand
        {
            get
            {
                return _searchTermKeyEventArgsCommand
                    ?? (_searchTermKeyEventArgsCommand = new RelayCommand<KeyRoutedEventArgs>(
                    p =>
                    {
                        if (p.Key == Windows.System.VirtualKey.Enter)
                        {
                            //your code here
                        }
                    }));
            }
        }

As you can see, we are using a Command that is able to take a Generic (in my case it comes from the MVVM Light Toolkit, but there are several other version floating around). Because of this, we are finally getting the KeyRoutedEventArgs into our ViewModel and are able to use its data and properties.

The VirtualKey Enumeration holds a reference to a lot of (if not all) keys and works for both hardware and virtual keyboards. This makes this code safe to use in an Universal app.

As I am quite new to MVVM, I am not entirely sure if this way is the “best” way, but it works as expected with little efforts. I hope this will be useful for some of you.

Useful links that helped me on my way to find this solution:

http://blog.galasoft.ch/posts/2014/01/using-the-eventargsconverter-in-mvvm-light-and-why-is-there-no-eventtocommand-in-the-windows-8-1-version/

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868246.aspx

Happy coding, everyone!

Posted by msicc in Archive, 0 comments

How to generate a round image button for your Windows Phone 8.1 app (to use everywhere)

Recently, I experimented a bit because I wanted a round button that contains an image that can be used everywhere where I can add a standard button (and not just in the AppBar). I managed to get a simple style out of these experiments (sample at the end of this post).

First, you should check if you have already installed Syncfusion’s free Metro Studio (we will need it later). It is a powerful helper if you need icons, so if you do not have it, go straight ahead and download it here: http://www.syncfusion.com/downloads/metrostudio

Still here/back? Great! Ok, let’s start. In our project, generate a new button:

<Button Width="72" Height="72"></Button>

If you want your round button to have a smaller size, feel free to adjust the 72 pixels mine has to your preferred value.

The next step is to generate a new Style. Right click on the Button, and select ‘Edit Template’, followed by ‘Edit a Copy’.

Screenshot (407)

 

Set the name of your style in the next window, and save define it as an app-wide Style or on your page:

Screenshot (408)

This should open your App.xaml file and display the button as well as the newly generated style.

We are starting with our custom style modifications right at the top:

image

Set both Doubles to 0 and the Thickness to 0,0.

Next, scroll down to find the Border Element of the Button Template (closing ‘VisualStateManager.VisualStateGroups’ helps a lot).

Click on the Border element and add/adjust the ‘CornerRadius’ property. At a size of 72, the minimum value is 38 for the radius. This should be fine for most cases, but it may be higher/smaller if you are using another size. Don’t worry if your button looks like this at them moment:

image

We are going to fix it right now by setting the Height and Width properties of our Border element:

Height="{Binding Path=Height, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Width="{Binding Path=Width, RelativeSource={RelativeSource Mode=TemplatedParent}}"

This binds the Width and Height properties of our Button to the Style. Now we just need to define the Height and the Width of our Button to make it actually look really round. Setting both to 72 will result in a nice round button.

Like you can imagine, displaying text does not make a lot of sense in this case. Round Buttons should contain an image. You could add one through adding a background, but this will result in a strange looking button when it gets pressed. Also, it does not reflect changes like a color change. To solve this, we are going to add code that is able to draw a shape for us. This is achieved with the Path Class  in XAML. The Path class draws lines into a FrameworkElement like a Canvas or a Border.

To enable our Style to work with Path Data, we need to add some code before the ‘Template’ property Setter in our Style:

<Setter Property="ContentTemplate">
    <Setter.Value>
        <DataTemplate>
            <Path Stretch="Uniform"
                  RenderTransformOrigin="0.5,0.5"
                  Margin="2,6,2,2"
                  Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                  Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"></Path>
        </DataTemplate>
    </Setter.Value>
</Setter>

What does this code do? The ContentTemplate allows us to add rich content to our UIElement, the Button. To make it resuable, we are setting it up in our custom button style. The RenderTransforOrigin property value of 0.5,0.5 centers our Path drawn shape within the border. However, I found out that some shapes do not look good with that alone. That’s why I adjusted the Margin property together with it. This should fit most icon shapes, but you might adjust this for your own needs.

The most important aspects are the Fill property as well as the Data property. Binding the Fill Brush to the Foreground Brush property is necessary to reflect changes like theme changes as well as changes in the VisualState. Only this way it behaves like a native Button. Binding the Data property allows us to enter the Path string into the Content property of a button that uses our Style without any conversion. This makes it very simple to generate a button with our desired icon.

And this is where Syncfusion’s MetroStudio comes in handy. It allows you not only to generate icons as png, but also as shape in XAML. To get the relevant Data, open MetroStudio, search for your icon. Below the icon, there is an Edit Button. Tap it to open the icon settings page. On that settings page, you set up your button. Play around a little bit to get used to it (it’s pretty easy).

Once you have your desired icon on the screen, click on the </>XAML Button. Copy the highlighted part of the XAML code:

image

Back in Visual Studio, add this copied code to the Content property of our Button:

Content="F1M181.003,-1898.78L207.077,-1902.33 207.089,-1877.18 181.027,-1877.03 181.003,-1898.78z M207.065,-1874.28L207.085,-1849.1 181.023,-1852.69 181.022,-1874.45 207.065,-1874.28z M210.226,-1902.79L244.798,-1907.84 244.798,-1877.5 210.226,-1877.22 210.226,-1902.79z M244.807,-1874.04L244.798,-1843.84 210.226,-1848.72 210.177,-1874.1 244.807,-1874.04z" 
Height="72" 
Width="72"
Style="{StaticResource RoundButtonStyle}" 
VerticalAlignment="Center" 
HorizontalAlignment="Center"/>

Which will result in this nice looking round button with a Windows logo on it:

image

If you run the sample project, you can see that the Button behaves like a native Button with text. Download the sample project here.

I am pretty sure this can be improved. I will continue to play around with this, and if I have found enough optimizations, I will write another post about them. Until then, this should help you to get started with your own round button – and the best thing: you can use it like any standard button wherever you want in your Windows (Phone) 8.1 app!

Happy coding, everyone!

Posted by msicc in Archive, 1 comment

Prevent accidentally exit of your Windows Phone SL app (with Telerik RadMessageBox or Toolkit CustomMessageBox)

preventExit

After finally being able to use my dev center account again after three long weeks, I am back into UniShare development. One of the most requested fixes was to not exit the app when users are not at the compose pivot.

Of course I am listening and I found a solution that should fit nearly all possible scenarios. Here is what I did:

        //handle back key press to prevent accidentally exit of the app
        protected async override void OnBackKeyPress(CancelEventArgs e)
        {
            //get the base handler
            base.OnBackKeyPress(e);

            //go back to the first pivot on all other pivots
            if (MainPivot.SelectedIndex != 0)
            {
                MainPivot.SelectedIndex = 0;
                e.Cancel = true;
            }
            //handle app exit
            else if (MainPivot.SelectedIndex == 0)
            {
                //cancel the back button press
                e.Cancel = true;

                //check if bool isExitQuestionCheckBoxChecked is false and the message needs to be displayed
                if (isExitQuestionCheckBoxChecked == false)
                {
                    //show the message box
                    MessageBoxClosedEventArgs args = await RadMessageBox.ShowAsync("Do you really want to exit the app?", MessageBoxButtons.YesNo, null, "don't ask me again, just exit next time", false, true);
                    if (args.ClickedButton == null)
                    {
                        //save check box value (use IsolatedStorage on earlier versions of Windows Phone)
                        App.SettingsStore.isExitQuestionCheckBoxChecked = isExitQuestionCheckBoxChecked;
                        //go back to the app
                        return;
                        
                    }
                    if (args.ButtonIndex == 0)
                    {
                        //set the check box value
                        isExitQuestionCheckBoxChecked = args.IsCheckBoxChecked;
                        //save check box value (use IsolatedStorage on earlier versions of Windows Phone)
                        App.SettingsStore.isExitQuestionCheckBoxChecked = isExitQuestionCheckBoxChecked;
                        //exit the app
                        Application.Current.Terminate();
                        
                    }
                    if (args.ButtonIndex == 1)
                    {
                        //set the check box value
                        isExitQuestionCheckBoxChecked = args.IsCheckBoxChecked;
                        //save check box value (use IsolatedStorage on earlier versions of Windows Phone)
                        App.SettingsStore.isExitQuestionCheckBoxChecked = isExitQuestionCheckBoxChecked;
                        //go back to the app
                        return;
                    }
                }
                //check if bool isExitQuestionCheckBoxChecked is false and the app should be exited
                else if (isExitQuestionCheckBoxChecked == true)
                {
                    Application.Current.Terminate();
                }
            }
        }

 

Let me explain. The first thing I am checking is if the current PivotItem is the one I want to display the message. If not, I am moving the Pivot to it.

The next step is to show the MessageBox and handle the buttons and the CheckBox. If no button is pressed, the codes saves the value false and goes back to the app.

If the Yes-button is pressed, the code saved the value of the CheckBox and exits. Same happens for the No-button.

If the user presses the back button the next time, the isExitQuestionCheckBoxChecked boolean gets checked – if the user does not want the message and checked it, the app exits as expected.

The above snippet uses the RadMessageBox.

When we use Toolkit’s CustomMessageBox, we need a slightly different approach. First, we do not override the OnBackKeyPress event – instead, we declare a new BackKeyPress event in the page’s constructor:

public MainPage()
{
    InitializeComponent();     
    BackKeyPress += MainPage_BackKeyPress;
}

 

Then, add this code to the newly generated event handler:

        //handle back key press to prevent accidentally exit of the app
        void MainPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            //go back to the first pivot on all other pivots
            if (MainPivot.SelectedIndex != 0)
            {
                MainPivot.SelectedIndex = 0;
                e.Cancel = true;
            }
            //handle app exit
            else if (MainPivot.SelectedIndex == 0)
            {   
                ////check if bool isExitQuestionCheckBoxChecked is false and the message needs to be displayed
                if (isExitQuestionCheckBoxChecked == false)
                {
                //generate a CheckBox as Content
                CheckBox chkbox = new CheckBox()
                {
                    Content = "don't ask me again, just exit next time",
                    Margin = new Thickness(0, 14, 0, -2)
                };

                //generate msg and handle the result
                CustomMessageBox msg = new CustomMessageBox()
                {
                    Title = "Attention",
                    Message = "Do you really want to exit the app?",
                    Content = chkbox,
                    LeftButtonContent = "yes",
                    RightButtonContent = "no"
                    
                };

                msg.Dismissed += (s1, e1) =>
                    {
                        switch (e1.Result)
                        {
                            case CustomMessageBoxResult.LeftButton:
                                //save check box value
                                isExitQuestionCheckBoxChecked = (bool)chkbox.IsChecked;

                                //save the check box value
                                if (IsolatedStorageSettings.ApplicationSettings.Contains("isExitQuestionCheckBoxChecked"))
                                {
                                    IsolatedStorageSettings.ApplicationSettings.Remove("isExitQuestionCheckBoxChecked");
                                }
                                IsolatedStorageSettings.ApplicationSettings.Add("isExitQuestionCheckBoxChecked", isExitQuestionCheckBoxChecked);

                                IsolatedStorageSettings.ApplicationSettings.Save();

                                //exit the app
                                Application.Current.Terminate();
                                break;
                            case CustomMessageBoxResult.RightButton:
                                //save check box value
                                isExitQuestionCheckBoxChecked = (bool)chkbox.IsChecked;

                                //save the check box value
                                if (IsolatedStorageSettings.ApplicationSettings.Contains("isExitQuestionCheckBoxChecked"))
                                {
                                    IsolatedStorageSettings.ApplicationSettings.Remove("isExitQuestionCheckBoxChecked");
                                }
                                IsolatedStorageSettings.ApplicationSettings.Add("isExitQuestionCheckBoxChecked", isExitQuestionCheckBoxChecked);

                                IsolatedStorageSettings.ApplicationSettings.Save();

                                //go back to the app
                                break;
                            case CustomMessageBoxResult.None:
                                break;
                            default:
                                break;
                        }
                    };
                    //show message
                    msg.Show();

                    //cancel all BackKey events
                    e.Cancel = true;

                }
                //check if bool isExitQuestionCheckBoxChecked is false and the app should be exited
                else if (isExitQuestionCheckBoxChecked == true)
                {
                    Application.Current.Terminate();
                }
            }
        }

 

This code is taken from a Windows Phone 8 project and does the same as the first snippet by using the Windows Phone Toolkit.

As always, I hope this is helpful for some of you.

Happy coding, everyone!

Posted by msicc in Archive, 0 comments

How to create an Extended Splashscreen for Universal Apps

Recently, I started to create a real world app to demonstrate the usage of my WordPressUniversal library. While I was working on it, I also decided to extend the Splahscreen which is mandatory for universal apps.There are several reasons to extend the splash screen, the most obvious is to hide initial data loading.

The only sample I found had a separate solution for the Windows and the Windows Phone application, and I decided to put them together to use the advantages of the Shared project that comes with every universal app.

The first step is to add your Splashscreen images to both projects. To do so, open their Package.appxmanifest files and go to the ‘Visual Assets’ tab. Add your images:

Screenshot (371)

Now add a UserControl to your shared project and rename the control type to “Grid” in the XAML file. After that, let us design the UI that is shown to the user when he starts our app:

<Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition Height="180"/>
</Grid.RowDefinitions>

<!--Windows needs a Canvas-->
<Canvas x:Name="Win_Splash_Img" 
        Grid.Row="0" 
        Grid.RowSpan="2" 
        Visibility="Collapsed">
    <Image x:Name="extendedSplashImage_Win" 
           Source="Assets/SplashScreen.png"/>
</Canvas>

<!--Windows Phone needs a Viewbox-->
<Viewbox 
    x:Name="WP_Splash_Img"
    Grid.Row="0" Grid.RowSpan="2" 
    Visibility="Collapsed">
    <Image x:Name="extendedSplashImage_WP" 
           Source="Assets/SplashWindowsPhone.png"/>
</Viewbox>

<!--this StackPanel holds our ProgressRing that is telling the user we are doing some work-->
<StackPanel 
    Grid.Row="1" 
    HorizontalAlignment="Center">
    <ProgressRing x:Name="progressRing" 
                  IsActive="True" Margin="0,0,0,12" 
                  Foreground="White" 
                  Background="Black">            
    </ProgressRing>
    <TextBlock x:Name="progressText" 
               Style="{StaticResource TitleTextBlockStyle}" 
               TextAlignment="Center" 
               HorizontalAlignment="Center">
 </TextBlock>
</StackPanel>

This XAML code is the base for our extended Splashscreen. It defines a row height of 180 to place the StackPanel that holds the ProgressRing at the bottom of our control. The main difference here is that Windows Apps use a Canvas while Windows Phone uses Viewbox to hold our Splashscreen Image. We need to set the Visibility on both to collapsed as our code decides which one will be displayed on Launch.

In our code page, we have a bit more work to do. Let’s go through it step by step:

Make sure you have added the following Namespaces:

using Windows.ApplicationModel.Activation;
using Windows.UI.Core;
using ExtendedSplash.Common;

Note: if you do not have a Common folder in your Shared Project, you can add it manually and copy the classes from the demo project attached to this project. Don’t forget to change the Namespaces of the classes to your project name after you added them.

The next step is to declare some objects that we need for the extended Splashscreen:

internal Rect splashImageRect; 
internal bool dismissed = false; 
internal Frame rootFrame;
private SplashScreen splash;

The Rect is used to place the Splashscreen Image in it and will be sized by our code which we will add later. The dismissed bool is used to determine when the system Splashscreen gets dismissed. The rootFrame is used for the navigation to our first application page, while the splash is used to read the needed values for placing our image into the Rect.

After we have declared those objects, we need to overload the main class of our control:

public ExtendedSplash(SplashScreen splashcreen, bool loadstate)

The splashcreen tells us the coordinates and the height of the splash image. You can use the loadstate bool to determine data that needs to be restored by the SuspensionManager, if needed.

As users are snapping our apps or rotating the device, we need to handle this. To do so, we need to add the following line to our constructor:

Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);

Add this code to handle the resizing:

if (splash != null)
{
    splashImageRect = splash.ImageLocation;
    PositionImage();
}

If the user causes the WindowSizeChanged event to fire, the splash will be loaded again and submits new coordinates to our splash object. To properly handle the positioning, we are using the PositionImage() method:

        void PositionImage()
        {
#if WINDOWS_PHONE_APP
            extendedSplashImage_WP.SetValue(Viewbox.HeightProperty, splashImageRect.Height);
            extendedSplashImage_WP.SetValue(Viewbox.WidthProperty, splashImageRect.Width);
#else
            extendedSplashImage_Win.SetValue(Canvas.LeftProperty, splashImageRect.X);
            extendedSplashImage_Win.SetValue(Canvas.TopProperty, splashImageRect.Y);
            extendedSplashImage_Win.Height = splashImageRect.Height;
            extendedSplashImage_Win.Width = splashImageRect.Width;
#endif
        }

As you can see, we use the preprocessor directive WINDOWS_PHONE_APP to declare the height and width of the Viewbox. If it is not running on Windows Phone, we are setting the canvas coordinates and size via this method. This is not the only point where we need this method, as you will see later in this post.

Let’s go back to our constructor and add this line to load the system’s Splashscreen values into our object:

splash = splashcreen;

Now that we have these values, we are finally able to receive the coordinates and declare which Splashscreen will be used:

            if (splash != null)
            {
                //handle the dismissing of the system's splash 
                splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);

                //get the system's splashscreen values
                splashImageRect = splash.ImageLocation;

                //decide which control is used to display the splashscreen image and size the progressRing
#if WINDOWS_PHONE_APP
                Win_Splash_Img.Visibility = Visibility.Collapsed;
                WP_Splash_Img.Visibility = Visibility.Visible;
                progressRing.Height = 50;
                progressRing.Width = 50;

#else
                Win_Splash_Img.Visibility = Visibility.Visible;
                WP_Splash_Img.Visibility = Visibility.Collapsed;
                progressRing.Height = 70;
                progressRing.Width = 70;
#endif
                //position and size the image properly
                PositionImage();

            }

 

Let me explain what I have done here. First we need to check if our splash object has a value. Then, we need to handle the Dismissing of the system’s Splashscreen with this TypedEventHandler:

private void DismissedEventHandler(SplashScreen sender, object args)
{
    dismissed = true;
}

After that, I am declaring when to show Windows Phone’s Viewbox and Windows’ Canvas as well as setting the size of the ProgressRing. The last step is to position and size the image again with our PositionImage() method. This will cause the app to switch to our extended Splashscreen.

Now we have to declare a new Frame instance and begin to load our data:

// this frame acts as navigation context
rootFrame = new Frame();
//start loading your data:
LoadData();

For this demo, I used a delay to keep the ProgressRing spinning:

async void LoadData()
{
    progressText.Text = "sleeping, please wait until I wake up...";
    //using a delay to keep the progress ring spinning for this demo
    await Task.Delay(TimeSpan.FromSeconds(5));
    //Navigate to the first application page after all work is done
    rootFrame.Navigate(typeof(MainPage));
    Window.Current.Content = rootFrame;
}

That’s all of the code we need inside our ExtendSplash control. If you now build the project, it will compile, but nothing will happen. other than going directly to the MainPage.

We need to add some additional code to our App.xaml.cs in the OnLaunched event:

//only show the splash if the app wasn't running before
if (e.PreviousExecutionState != ApplicationExecutionState.Running)
{
    //check the loadstate if necessary
    bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated);
    //create a new instance of our ExtendedSplash
    ExtendedSplash extendedSplash = new ExtendedSplash(e.SplashScreen, loadState);
    //declare our extendedSplash as root content
    rootFrame.Content = extendedSplash;
}

If you hit the debug button, you should have similar results to these:

Screenshot (374)

Screenshot (375)

For your convenience, I created a small demo project. Download it here.

As always, I hope this blog post is helpful for some of you. If you have questions or feedback, feel free to leave a comment below.

Posted by msicc in Archive, 8 comments