Setting Wallpapers in a Windows 8 Store App with VB

Introduction

Today we’ll find out how we can change the wallpaper of a Windows 8 Store app. Hold on tight, we have a bumpy ride ahead. Let’s get started.

Windows 8 Store Apps

As we know by now, and if you have followed my most recent articles (the last two months or so), Windows 8 Store apps differ quite a bit from desktop apps. While doing today’s app, you will see most of the differences concern timers, and API’s as well as serialization capabilities. If you are new to Windows 8 Store apps, I suggest you read this article.

What Can We Work With?

It seems a silly question, but, you will need to know what tools are available, and what language features we can use. We are able to work with the next few features just to get this basic project to work:

  • XAML features, including XAML controls – I quote Homer Simpson: “doh!”. Obviously we well need these as this is a Windows 8 Store app.
  • Background tasks – This we will use instead of Timer controls, which aren’t available here.
  • SystemParametersInfo API – Yes, it still does work.
  • Asynchronous programming – We will use Async programming methods to accomplish our tasks asynchronously. Have a look at this article I wrote recently.
  • Basic serialization – The reason why I included this point is: It is logical to assume we will need some sort of serialization here, as we will need to keep track of which paper was set and so on. I haven’t gone into much detail here, honestly; perhaps in an updated version of this article (or a future article) I will.

We aren’t able to work with the following features:

  • Timer controls – these controls do not exist in the Windows 8 Store framework
  • Registry – Windows 8 Store apps cannot access the regsitry. We can however use the following two APIs:
    • Windows.Storage.ApplicationDataContainer
    • Windows.Storage.ApplicationDataContainerSettings

Welcome to Windows 8 Store programming!

Our Project

Our project’s aim is to set change the wallpaper at different intervals. Because of the fact that there is no timer control present, we will have to create a Background task. This is not as simple as double clicking on a control. We will have to create a separate project for our background task, register it, and make it work with our main project. This means that we will have two projects in one solution.

Another caveat is that we have limited resources at our disposal. We will only be allowed to access certain folders, which are known to the app, and read their contents asynchronously. Lastly, we do not have access to the registry, so saving info might be hard.

Let us create our first project. Give it a name of Wallpaper Genie 2013, and design it to look like Figure 1.

Our Main form's design
Figure 1 – Our Main form’s design

Add another Page to your project through the Project menu, and design it to resemble Figure 2

Settings page
Figure 2 – Settings page

Mainpage

Add the following Imports to your MainPage:

Imports Windows.Storage
Imports Windows.Storage.Streams
Imports Windows.UI.Xaml.Media.Imaging
Imports System.Runtime.InteropServices
Imports Windows.ApplicationModel.Background
Imports Windows.UI.Core

Imports Windows.UI.Core.CoreWindow

Imports System.Xml
Imports System.Runtime.Serialization
Imports System.IO

Our program in action - isn't Alizee Jacotey just beautiful!!?

Now that we have decent understanding of the need for Background tasks, we have to know how to create one.

Add a new project to your existing solution by clicking File, Add, Project. Select the Class Library project and give it a nice name. I have named mine GenieBackTask. Rename the default class name (Class1) to something more descriptive such as clsBack.vb. Your Solution Explorer should look like Figure 4.

Solution Explorer
Figure 4 – Solution Explorer

Add the following code to clsBack:

Imports Windows.ApplicationModel.Background
Imports Windows.Storage
Imports Windows.UI.Core
Imports Windows.UI.Core.CoreWindow
Imports System.Runtime.InteropServices
Imports System.Xml
Imports System.Runtime.Serialization

Namespace GenieBackTask
    Public NotInheritable Class clsBack
        Implements IBackgroundTask

        <DllImport("user32", setlasterror:=True)> _
        Private Shared Function SystemParametersInfo( _
                ByVal intAction As Integer, _
                ByVal intParam As Integer, _
                ByVal strParam As String, _
                ByVal intWinIniFlag As Integer) As Integer
            ' returns non-zero value if function succeeds
        End Function

        Const SPIF_UPDATEINIFILE = &H1
        Const SPI_SETDESKWALLPAPER = 20
        Const SPIF_SENDWININICHANGE = &H2

        Public Async Sub Run(taskInstance As IBackgroundTaskInstance) Implements IBackgroundTask.Run
            Dim Deferral As BackgroundTaskDeferral = taskInstance.GetDeferral()

            Await UpdateUI()
            Deferral.Complete()

        End Sub



        Private Async Function UpdateUI() As Task
            Dim MyDispatcher = GetForCurrentThread().Dispatcher

            Await MyDispatcher.RunAsync(CoreDispatcherPriority.Normal, Async Function()

                               Dim picker As New Windows.Storage.Pickers.FileOpenPicker

                               picker.SuggestedStartLocation = Pickers.PickerLocationId.PicturesLibrary

                               Dim strPicFileName = Await picker.PickSingleFileAsync

                               Dim x As Integer

                               x = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, strPicFileName.DisplayName, SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)

                               End Function)
        End Function

    End Class

End Namespace

Let us break this code down, piece by piece. Obviously, the first couple of lines are the namespaces that we need.

This part:

Namespace GenieBackTask
    Public NotInheritable Class clsBack
        Implements IBackgroundTask

Creates a namespace (for the project) and creates the class. You will notice that this class implements the IBackgroundTask interface. This interface provides a method to perform the work of a background task.

The next section:

      <DllImport("user32", setlasterror:=True)> _
        Private Shared Function SystemParametersInfo( _
                ByVal intAction As Integer, _
                ByVal intParam As Integer, _
                ByVal strParam As String, _
                ByVal intWinIniFlag As Integer) As Integer
            ' returns non-zero value if function succeeds
        End Function

        Const SPIF_UPDATEINIFILE = &H1
        Const SPI_SETDESKWALLPAPER = 20
        Const SPIF_SENDWININICHANGE = &H2

Is the declaration of our API that will change the wallpapers.

Now things get tricky! The Run sub (which has to be included inside any Background task) is what will run when it has been triggered. The trigger we will set inside our MainPage class when we register the background task. The Run sub looks like:

        Public Async Sub Run(taskInstance As IBackgroundTaskInstance) Implements IBackgroundTask.Run
            Dim Deferral As BackgroundTaskDeferral = taskInstance.GetDeferral()

            Await UpdateUI()
            Deferral.Complete()

        End Sub

What happens here is the following:

  • We again implement the IBackgroundTask interface, but this time its Run method.
  • We obtain a Deferral object as the task will run Async.
  • We call our method that will run.
  • We complete the deferral.

Now the fun part! This…

        Private Async Function UpdateUI() As Task
            Dim MyDispatcher = GetForCurrentThread().Dispatcher

            Await MyDispatcher.RunAsync(CoreDispatcherPriority.Normal, Async Function()

                                  Dim picker As New Windows.Storage.Pickers.FileOpenPicker

                                  picker.SuggestedStartLocation = Pickers.PickerLocationId.PicturesLibrary

                                  Dim strPicFileName = Await picker.PickSingleFileAsync

                                  Dim x As Integer

                                  x = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, strPicFileName.DisplayName, SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)

                                  End Function)
        End Function

…runs another Async function inside of it, and dispatches it to the current thread. We created a FileOpenPicker, which allows the user to select a file inside the PicturesLibrary. Obviously you can customize this further (as this is just an example) to locate the specific picture you need.

Finally, we apply the selected picture as a Wallpaper. Build this project now.

This is all we need for the Background task. We now need to connect it to our main project, and then register and start it from our main project. Connect this task  to the main project now by opening the Package.Manifest file and navigating to the Declarations tab and entering GenieBackTask.clsBack in the Entry Point field, as displayed in Figure 5.

Figure 5 - Page Manifest
Figure 5 – Page Manifest

Now our main output project knows about the task. All that is left now is to register the task in code from our MainPage, and then we’re done. Add the following code to the Mainpage class:

    Private Sub btnSet_Click(sender As Object, e As RoutedEventArgs) Handles btnSet.Click

        Dim x = RegisterBackgroundTask("GenieBackTask.clsBack", "GenieBackTask", New TimeTrigger(PaperDuration, False))

    End Sub


    Public Shared Function RegisterBackgroundTask(TaskEntryPoint As String, TaskName As String, Trigger As IBackgroundTrigger) As BackgroundTaskRegistration
        For Each cur In BackgroundTaskRegistration.AllTasks

            If cur.Value.Name = TaskName Then
                Return DirectCast(cur.Value, BackgroundTaskRegistration)

            End If
        Next

        Dim Builder As New BackgroundTaskBuilder
        Builder.Name = TaskName
        Builder.TaskEntryPoint = TaskEntryPoint
        Builder.SetTrigger(Trigger)

        Dim task As BackgroundTaskRegistration = Builder.Register()

        Return task

    End Function


    Public Shared Sub UnregisterBackgroundTasks(TaskName As String)

        For Each cur In BackgroundTaskRegistration.AllTasks

            If cur.Value.Name = TaskName Then
                cur.Value.Unregister(True)

            End If
        Next

    End Sub

In btnSet we register the background task by supplying the same EntryPoint (as we did in the Mainfest), giving it a name, and when this task should run (PaperDuration).

Lastly, we Unregister the task. This is needed so that it doesn’t take up unnecessary resources when the app is closed.

Conclusion

This was tough, agreed. But things fall into place the more you work with a certain technology. My aim is just to guide you in the right direction, and I try to make your transition from normal desktop apps to Windows Store apps easier. I hope you have learned a thing or two today. Until next time, cheers!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read