Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

ACS PD Board gets and RSS feed

One of the initiatives that I've been pushing within the Professional Development board of the Australian Computer Society is trying to establish better communication channels.  To this end, we have put together a blog that will be used to introduce the PD Board, who's involved, what activities are being run and any other relevant information.  If you want to see some of the activities that the ACS runs, feel free to subscribe to the rss feed!

Time and Expense - basic db design

Taking the requirements from my previous post I set about to put together a simple database structure that would serve as the backend for my prototype T&E application. Below I have indicated to what extent the functionality is supported by the proposed structure:

- Timesheet entry
      primary functionality
- Client / project / job management (both billable and non-billable)
      project/work item management is primary functionality
      client management is not part of this app

- Staff management including the ability to analyse planned staff availability and setup schedule of rates
      not part of this app
- Expense management
      primary functionality
- Decent reporting / charting / analysis / modelling functionality
      support provided through reporting services
- Ability to export into csv, xml etc
      support provided through reporting services
- Invoicing / billing management
      not part of this app
- Email notifications (timesheets due, upcoming staff availability, billing/invoicing due etc)
      not part of this app (initially)
- Be able to track Time and Expenses OFFLINE
      primary functionality
- Be able to integrate with TFS
      primary functionality

See the following image for an overview of the initial database which should cater for most of these requirements.  I say initial because like most projects, I'm sure I will want to change the scope as I go along.....

 

 

 

ActiveSync 4.2 - perhaps this might work

For anyone with a mobile device, I would recommend downloading the latest in the ongoing connectivity saga, ActiveSync.  If you are running Vista, DO NOT install ActiveSync.  Vista comes with the Windows Mobile Device Center which is the replacement. 

Vista pointers.....

- Although Beta 2 doesn't allow you to set up partnerships, there is an optional update that you can install (run Windows Update and review optional updates) which will allow you to sync with your device.

- To install the WM5 SDKs you need to disable User Access Control (under user settings in the Control Panel).

Time and Expense reporting

I was attempting to catch up on the backlog of unread emails from the Stanski mailing list when I came across a brief off-topic discussion on Timesheet / Timetacking software.  Now you would have thought that such software would be available by the handful, or better yet, integrated into existing packages.  I'm guessing that most accounting packages have some facility to do timesheeting, but it was probably written by accountants, rather than from a user point of view - result is that they are not widely used.  This discussion also got me thinking back to a WF/WCF example application on expense reporting and whether I could combine this with the fantastic work that the patterns and practices group at MS did on the Mobile Client Software Factory.  Over a series of posts I will attempt to put together a "simple" T&E reporting tool, based around the following requirements (cut and pasted from the discussion on the mailing list):

- Timesheet entry (preferably over the internet)
- Client / project / job management (both billable and non-billable)
- Staff management including the ability to analyse planned staff availability and setup schedule of rates
- Expense management
- Decent reporting / charting / analysis / modelling functionality
- Ability to export into csv, xml etc
- Invoicing / billing management
- Email notifications (timesheets due, upcoming staff availability, billing/invoicing due etc)
- Be able to track Time and Expenses OFFLINE
- Be able to integrate with TFS

You will note that I have added my own requirements which are that the application should be able to operate offline and should be able to source project and workitem information from TFS.  As we are increasingly mobile (laptops, pdas, phones) we are more than likely to want to record this information when we are away from our desks.  For example, when you take a client to lunch, you want to be able to add that expense when you pay for the meal, instead of remembering to claim it later in the month.

So, the rough architecture of this application will be a core database (SQL Server 2005) which publishes data (filtered by user) using replication to a client database (SQL Server Everywhere).  The client application will be a WinForms application that works on both PDAs (Windows Mobile), Tablets, Laptops and Desktops (Windows XP).  Timesheet information will simply replicate between the client and server when a connection is available, while Expense information will use a queued (Mobile Client Software Factory) web service (Windows Communication Foundation) call to submit a new expense report.  Each expense report will initiate a workflow (Windows Workflow Foundation) that will require approval for expenses over a certain amount. 

Ambitious you say..... well you might be right, lets see how it goes ;-)

Some frozen thoughts about mobile technology

One of the incentives for accepting a job in Wellington is that it is only 4 hours from Mt Ruapehu. Why is this significant? Well, for those who don't know me that well, one of my passions is of course snowboarding. Anyhow, its been 3 weeks since arriving in NZ and I figured it was time to head to the snow. I booked into the cheapest backpackers I could find, rented a car and off I went.

Most times when I travel I take my laptop so that I can stay in touch with the rest of the world. Normally this involves paying some outrageous fee to a hotel, that has already charged through the roof for a room, just to get a couple of hours of Internet access. This time I figured that I would leave the laptop at home as I was unlikely to use it as connectivity was highly unlikely. Or so I thought. I had forgotten that we are in a new era of FlashPackers - travellers who live on a shoestring, yet have all the devices of your average executive. Not only does the Mountain View Motel have Internet, it's free!

The next bit of technology I wanted to comment on are the scanners that the mountain staff use to monitor punters. In the lift queues the staff scan everyone lift ticket (barcode scanner) to validate them. I restisted the urge to ask "Is that a Pocket PC?". This said, I would be interested to know what devices they are as they have to handle some pretty average conditions. Today was sub-zero and raining, all day.

Intilecta's starting to grow!

For the last couple of weeks the initial team here at Intilecta have been busy and, like most good startups, we are already looking to expand the team.  If you think you have what it takes and are keen to join the team, check out the oppportunities on the Intilecta website.

Why didn't I think of this..... use Crossbow to control Blackberries

Ok, so there are two points to this post:

1) Daniel Moth points out that instead of complaining that the WM5 SDK doesn't install properly on Vista Beta 2 I should of just temporarily disable User Access Control while installing the SDK - damn, why didn't I think of doing that.  I guess I assumed that when I told it to "Run as Administrator" it would have the same effect.

2) Anyone watching the Windows Mobile space should be aware that the code name for the next version of Windows Mobile is "Crossbow".  A quick google on Crossbow and Blackberry (for example if you were looking for a comparison) might lead you to the following page for an Australian company that sells a product called Crossbow Herbicide which is explicitly used "For the control of blackberries....." - how funny is that.

How good is this - defer connection to TFS on VS load

Check out this to see how you can tell VS not to connect to TFS when it loads.  One of my biggest complains about VS is that if you had Team Explorer open when you closed it previously it will try to connect the next time you open VS.  If you don't have a connection to the server it throws a wobbly, or worse, just seems to hang.  Anyhow, "add a DWORD value called AutoLoadServer under HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\TeamFoundation and set the value to 0."

SoftTeq represented at the ECU Early Career Awards

About a month ago I was nominated for the ECU Early Career Awards as recognition for the work I have done with the local IT community.  My involvement with the ACS and the Perth .NET Community of Practice saw me progress to becoming one of the finalists for 2006.  Unfortunately as I'm currently in Wellington, NZ, I won't be able to attend the awards ceremony so will have to wait and see who takes out the honours (cross fingers...) 

Bored sitting on the bus/train on the way to work - try being a Caveman for an hour or so...

This is a bit old news now as Frank had the scoop a couple of days ago, but since I've only just had the time to download the demo and play with it I thought I'd blog about it.  The guys from Lightworks have finished their first game, Cavemen, and its a ripper.  I must admit that I couldn't bring myself to put it down after playing the first couple of levels.  Although I haven't got around to buying the next levels (saving that for a rainy day) I would recommend anyone who wants a distraction for a couple of hours to try it now!

Off to NZ to work at Intilecta

For those who haven't heard through the grapevine I have been fortunate enough to be involved with an exciting new startup venture here in sunny (not) Wellington, NZ.  Intilecta is set to be a major player in the future of BI software and it has been an awesome experience to date.  With initial funding secured the CTO, Greg Martin, has put together a crack development team, with Steve Sim as the Development Manager, former ISA Technologies architect, David Gardner (also a Perthite) and of course myself. 

After flying into Wellington last week I have almost acclimatised to the time, cold and wind.  These are all good things, especially the cold when you put it in perspective that one of my passions is snowboarding and that I am the proud owner of a season lift ticket to the local mountain.

So, how long am I in NZ for - well at this stage it is a 6 month stint to get the development process underway.  Afterwhich I will be returning to sunny (not joking this time) Perth.  In the meantime my colleague Alastair will be running the user group. 

If you are in Wellington, or feel like coming to NZ for a holiday, please make sure you look me up.  My new contact information should be on the contacts page of the SoftTeq website in the next couple of day.

Vista Update, VMWare and still no Windows Mobile 5 SDK

Mid-last week I spat the dummy and re-paved my machine back to WinXP.  After having Vista and Office running quite smoothly for a couple of weeks the straw that broke the camels back was the inability to connect my laptop to a number of wireless networks.  I must admit I find the new interface for network connections difficult to use - in fact, coupled with the continual need to Ok administrative prompts, it SUX.  But that's another story...

One of the other things that I found frustrating with Vista Beta2 was that I couldn't synchronise my WM5 device using the new Windows Mobile Device Center.  I could explore files, but not able to set up a partnership.  The story is that we had to wait for an update before we could setup a partnership, or for that matter use VS to debug mobile applications.  Anyhow, the update has arrived, but of course if arrived a day or so after I had converted my laptop back to WinXP - talk about frustrating.

So I figured I'd set up a VPC image with Vista - easily done!! and sure enough there was the update.  Once installed I figured this would fix the issues preventing the Windows Mobile 5 SDK from installing properly, but alas it still doesn't work.  The next thing I wanted to check was whether I could set up a partnership.  I ran VS2005 and used the Device Emulator Manager to run up the PPC2003SE emulator.  I then set the emulator to Cradle, which under WinXP would allow activesync to connect to the emulator as if it were a true device.  Unfortunately no joy - does this mean that there is only support for partnering with WM5 devices or is there an issue with the Device Emulator Manager??? 

I then cursed because although I have a real WM5 device there is no way to get it to connect to my Vista VPC because Virtual PC still does not have USB support - urgh when will this be fixed.

A while later I bit the bullet and downloaded a trial edition of VMWare Workstation.  Not only is the interface much richer than Virtual PC (including support for USB) it already has support for building a Vista image.  I wish that the Virtual PC team would learn something from the functionality of VMWare.

Finally, after many hours of installing Vista and Visual Studio I have an image that my WM5 device can connect to.  The new partnership setup is much better than activesync, and seems to work.  Unfortunately this STILL doesn't fix the issues with A) cradling an emulator or B) installing the WM5 SDKs

My experiences with Vista all seem to be 2 steps forward followed by 1 step back ;-) At least we're making progress forward.....

SQL Everywhere CTP - get it while it's hot....

A while ago Microsoft made the announcement that there was to be a change in the SQL product line to include a new product, SQL Server Everywhere.  In fact this is essentially a rebranding exercise around the SQL Mobile product (previously SQL Server CE).  There are going to be a couple of changes made, making it possible to run SQL/e (Steve's cute abbreviation) on Windows XP and hopefully a nice deployment model (for example ClickOnce).

Anyhow, if you want the goods you can get an early version by downloading the CTP

 

"How to build an annoying mobile application 101"

Even wondered how you can stop your device from accidentally calling people?  Or wanted to stop people drawing all over you application?  Perhaps you even have a legitimate reason to stop people using the touch display - like they are supposed to use the keyboard or hardware keys to navigate (like a smartphone)?  Well, look no further, here's a neat trick for disabling the touch screen on your device....

<System.Runtime.InteropServices.DllImport("touch")> _
Private Shared Sub TouchPanelDisable()
End Sub

This code snippet was brought to you curtesy of a fellow .NET CF MVP, Peter Foot.

Wanna see what Exchange SP2/WM5 has that your mobile application needs?

The Messaging and Security Feature pack combined with Exchange SP2 gives us mobile users direct-push email.  So how does this work, well a blog article by the exchange team gives a detailed insight into how it all works.

Why do we want to know how it works?  Well, the same technique can be used in our applications to make them respond immediately as information on the server changes.  Typically most mobile applications currently use some form of polling or SMS notification to initiate a sync.  This can both be expensive and not particularly effective.

Using the technique described by the exchange team, Alex Yakhnin shows how you too can have an application that is always up to date!

 

 

 

 

Mobile Client Software Factory.....we're almost there - Drop 11

In case you haven't seen, drop 11 of the Mobile Client Software Factory has hit the web site.  This includes a number of bug fixes and improvements in documentation.  You will also notice that support within Visual Studio has been added for creating disconnected service agent classes using a guidance package.

IMPORTANT - Feedback is urgently required by this team as they are rapidly approaching code freeze, please send feedback via the gotdotnet website ASAP.

.NET Micro Framework Whitepaper

A couple of people have started talking about the .NET Micro Framework which is, as the name suggests, an extremely cut down version of the .NET Framework.  Unlike the .NET Compact Framework which was designed to run on top of an existing OS, the .NET Micro Framework can operate without an underlying OS.  As such it has provisions for a HAL that is specific to the device onto which it is to be run.  The best news is that this is all going to have rich developer support through Visual Studio.  Check out the new whitepaper that provides early information about this framework.
Building a Flickr Viewer App with BuildIt.Lifecycle

Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Building a Flickr Viewer App with BuildIt.Lifecycle

Last week at the Mobile .NET User Group I presented on the BuildIt.Lifecycle for the first time. The main motivation wasn’t to try and get people to start using it (although I’d definitely welcome feedback from anyone who does), it was really to try to get developers to start thinking differently about the way that they write applications. Instead of thinking of their application in terms of the apis that any one platform offer (and I treat XForms as just another platform, even though it sort of in-part aggregates iOS/Windows and Android) or in terms of raw navigation constructs (eg Activities/Intents, ViewControllers or Pages), but more in terms of states within their application.

Applications are littered with states, whether they be the states of a button (Normal, Focussed, Pressed, ToggledOn etc) or visual states of a Page (a Windows concept but you can think of visual states as being the different layouts of any given page/view of your application). In fact if you think of your application as a whole you realise that you can map the entire navigation within your application as just a series of state transitions, with your pages/views being the states that the application can be in. This is indeed the foundation of BuildIt.Lifecycle.

One other point I’ll make before jumping into building the Flickr Viewer application, which should demonstrate the core of the BuildIt.Lifecycle framework, is that most application frameworks, at least for mobile platforms, have been built around the notion of a single window or frame that houses the content that the user is currently looking at. As such most of the frameworks have some metaphor that maps to the application as a whole (often called the App or Application class), and is assumed to be a singleton. In exploring the use of states to model applications, it became evident that there are some platforms, such as Windows and Mac, where this assumption breaks down, and that in fact the framework needs an additional metaphor that maps to each window within an application. I’ve settled on Region for the timebeing, and there’s no surprises that there is a mapping between an instance of a Region in the framework to a Window presented at runtime. This should make more sense once we explore creating the application; alternatively there’s a post on working with Additional Windows using Regions with BuildIt.Lifecycle.

Let me start by giving an example of visual states, just to warm up and get us all thinking about using states to represent an application, or parts of the application. I’m going to start by creating a new Universal Windows Platform project, based on the Blank App (Universal Windows) project template, called FlickrViewer.

image

Next I’ll create another project, this time called FlickrViewer.Core, based on the Class Library (Portable) template, keeping the defaults in the Add Portable Class Library targets dialog.

image

To complete the setup of the projects, I just need to add a reference from the FlickrViewer UWP project to the FlickrViewer.Core PCL project.

image

The last reference I’m going to add initially is to the Microsoft.Net.Http and Newtonsoft.Json NuGet packages (Note that under the Install and Update Options the Dependency behaviour is set to Highest, and both packages are install into both projects).

image 

I’m going to add the FlickrService class (and corresponding IFlickrService interface) that’s listed at the end of this article, which I’ll use to download content from the public flickr feed, to the PCL. This application is going to consist of two pages, the first will display a list of images retrieved from the Flickr public feed; the second page will display the details of the image selected from the first page. I’ll add another page at this point called DetailsPage.xaml, based on the Blank Page item template.
Next, I’m going to switch across to Blend and create a couple of visual states: a Loading state, for when data is being loaded; and a Loaded state, for when data is available to be displayed on the screen. The following XAML for the MainPage includes a ListView, which will be visible once data has loaded (ie Loaded state), and a ProgressRing, which will be visible and active while data is being loaded (ie Loading state).

<Page x:Class="FlickrViewer.MainPage"
      xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:FlickrViewer"
      xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="MainStates">
                <VisualState x:Name="Loading">
                    <VisualState.Setters>
                        <Setter Target="progressRing.(UIElement.Visibility)" Value="Visible" />
                        <Setter Target="progressRing.(ProgressRing.IsActive)" Value="True" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Loaded">
                    <VisualState.Setters>
                        <Setter Target="listView.(UIElement.Visibility)" Value="Visible" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <ListView x:Name="FlickrListView" Visibility="Collapsed">
          
<ListView.ItemTemplate>
                <DataTemplate>
                    <Grid Height="100" Margin="0,10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Image Source="{Binding media.m}"
                               Stretch="UniformToFill"
                               HorizontalAlignment="Center"
                               VerticalAlignment="Center" />
                        <TextBlock Text="{Binding title}"
                                   Grid.Column="1"
                                   TextWrapping="WrapWholeWords"
                                   FontSize="20"
                                   Margin="10,0"/>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <ProgressRing x:Name="LoadingProgress"
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      Visibility="Collapsed"
                      Width="100"
                      Background="Red"
                      Height="100" />

    </Grid>
</Page>

When the MainPage loads, I’ll create an instance of the FlickrService and invoke the LoadPhotos method in order to retrieve data to be displayed on the MainPage.

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    VisualStateManager.GoToState(this, "Loading", true);
    var flickr = new FlickrService();
    var photos = await flickr.LoadPhotos();
    FlickrListView.ItemsSource = photos;
    VisualStateManager.GoToState(this, "Loaded", true);
}

Running the application at this point will display the progress ring while the data is being downloaded (Loading state), followed by the list of photos once the data has been retrieved (Loaded state).

image

So now that you’ve got the idea of visual states, let’s move on to talking about the application in terms of states. I’m going to use the BuildIt.Lifecycle framework, so I’ll start by adding references to the NuGet packages. Into the FlickrViewer project, I’m going to add the BuildIt.Lifecycle.UWP package; into the FlickrViewer.Core project, I’m going to add the BuildIt.Lifecycle package (there will be some refactoring coming which will consolidate these into a single package soon but for now there are platform specific packages to be added).

As with most frameworks I need to add a class to the PCL that will correlate to the application as a whole. There are a couple of different options available as base classes; in this case I’m going to pick the one that is already setup to be able to handle multiple regions, despite not actually requiring it for this application. Currently this base class does require the DefineApplicaitonRegions method to be defined, but in this case since we are only going to define a single region, PrimaryRegion, there is no need to do anything within this method. The type, PrimaryRegion, is provided as a generic argument when inheriting from the RegionAwareBaseApplication class, which registers and identifies the PrimaryRegion class as the first region of the application.

public class RootApplication : RegionAwareBaseApplication<PrimaryRegion>
{
    protected override void DefineApplicationRegions()
    {
    }
}

Next, I need to define the PrimaryRegion class, and for this I’m going have it inherit from the StateAwareApplicationRegion class. It’s possible to define regions of the application that aren’t state aware, meaning that the developer has to provide some other mechanism for navigating between pages/views. In this case, we want to think of our application in terms of states. Rather than thinking about the navigation through our application as going from MainPage to the DetailsPage, I’ll define two states, Main and Details, between the application will transition. These states are easily defined as enum values, and used within the PrimaryRegion to define what makes up each state:

public class PrimaryRegion : StateAwareApplicationRegion
{
    public enum RegionStates
    {
        Base,
        Main,
        Details
    }

    public PrimaryRegion()
    {
        StateManager.Group<RegionStates>()
            .DefineState(RegionStates.Main)
            .DefineState(RegionStates.Details);
    }
}

On of the interesting things about the pages of the PrimaryRegion is that they correspond to pages within the application, which in turn need a view model to data bind to. This is how most developers think of this relationship. Actually it’s almost the other way around. Each of the states within the PrimaryRegion will have a view model (think of this as the data that is related to that state). The platform specific implementation of moving to a particular state will display a page that correlates to the state, and will data bind the corresponding view model to the page. Hopefully this helps set the stage for the next step – we’re going to go back to the StateManager within the PrimaryRegion, and this time change to defining view models for each state:

public PrimaryRegion()
{
    StateManager.GroupWithViewModels<RegionStates>()
        .StateWithViewModel<RegionStates, MainViewModel>(RegionStates.Main)
        .EndState()
        .StateWithViewModel<RegionStates, DetailsViewModel>(RegionStates.Details)
        .EndState();
}

You’ll notice that this makes use of two view models, MainViewModel and DetailsViewModel, which I’ll need to create in the PCL. The code associates each of the view model types with the corresponding state enum value. For the timebeing the view models will be relatively basic, only inheriting from NotifyBase which is a simple implementation of the INotifyPropertyChanged interface.

public class MainViewModel:NotifyBase
{ }
public class DetailsViewModel:NotifyBase
{ }

So far I’ve added two states, with view models, to the PrimaryRegion but it doesn’t know which state to start at. By overriding the CompleteStartup method, it’s possible to direct the StateManager to go directly to the Main state.

protected override async Task CompleteStartup()
{
    await base.CompleteStartup();

    await StateManager.GoToState(RegionStates.Main);
}

The navigation to actual pages in the UWP application is handled at the UI layer, and relies on there being a known association between the state that the application is in, and the corresponding page that should be displayed. Some frameworks rely on convention to do this; we take the approach that we prefer this to be explicit, which has the secondary benefit of avoiding reflection. When the application launches, I need to register the MainPage and DetailsPage to the corresponding state:

public App()
{
    this.InitializeComponent();
    this.Suspending += OnSuspending;

    LifecycleHelper.RegisterView<MainPage>().ForState(PrimaryRegion.RegionStates.Main);
    LifecycleHelper.RegisterView<DetailsPage>().ForState(PrimaryRegion.RegionStates.Details);
}

I’m also going to replace all the startup logic that would normally reside in the OnLaunched method in the App.xaml.cs file, with the following code:

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
    var core = new RootApplication();
    var wm = new WindowManager(core);
    await core.Startup();
}

From this code you can I’m creating an instance of the RootApplication, which knows about the PrimaryRegion, which in turn manages the state transitions between Main (ie MainPage) and Details (DetailsPage). The WindowManager introduces all the glue necessary to navigate to the appropriate pages with the application, and to ensure the window completely loads by calling the Activate method.

For the timebeing I’m going to replace the contents of the MainPage with the following TextBlock and Button, contained within a vertical StackPanel.

<StackPanel>
    <TextBlock Text="{Binding Data}"
                FontSize="40" />
    <Button Click="GoToSecond">Go</Button>
</StackPanel>

Into the MainViewModel I’ll add a simple property that returns a static value:

public string Data { get; } = "Hello World!";

Running the application displays Hello World! on the screen, indicating that the MainViewModel has been setup as the DataContext for the MainPage that’s being displayed.

image

Next, is to wire up the Go button to transition the application to the Details state, in order to display the DetailsPage. The change in state needs to be controlled by the StateManager that belongs to the PrimaryRegion. This means that in order to change state, the Main state needs to raise an event, or send a message, to signal to the PrimaryRegion. In actual fact, it’s the MainViewModel that will raise the event but rather than simply define a custom event I will instead inherit from the BaseViewModelWithCompletion class, using the DefaultCompletion enum which defines a single usable value of Complete (if the state had multiple states it could transition to, I could define a new enum that has a value for each state to transition to). The MainViewModel class now looks like:

public class MainViewModel : BaseViewModelWithCompletion<DefaultCompletion>
{
    public string Data { get; } = "Hello World!";

    public void Complete()
    {
        OnComplete(DefaultCompletion.Complete);

    }
}

The Complete method simply gets invoked by the event handler for the Button

private void GoToSecond(object sender, RoutedEventArgs e)
{
    (DataContext as MainViewModel).Complete();
}

The last thing is to handle the completion of the MainViewModel/Main state in the PrimaryRegion. This can be done by extending the definition of the Main state.

StateManager.GroupWithViewModels<RegionStates>()
    .StateWithViewModel<RegionStates, MainViewModel>(RegionStates.Main)
        .OnComplete(DefaultCompletion.Complete)
        .ChangeState(RegionStates.Details)
    .EndState()
    .StateWithViewModel<RegionStates, DetailsViewModel>(RegionStates.Details)
    .EndState();

Clicking the Button will transition the application from the Main to Details state, and in doing so navigate from the MainPage to the DetailsPage. You’ll also see that a back button appears in the top navigation bar, allowing the transition back to the Main state – this works without having to manually define the back button behaviour.

image

I’ll return now to the MainPage and look at how to populate it with data. There are three main steps to this:

- Register the FlickrService
- Associated the FlickrService with the MainViewModel
- Use the FlickrService to populate data within the MainViewModel

The registration of services happen at an application level but can be done either within the RootApplication, if the implementation of the services is accessible within the PCL (like our FlickrService), of it can be done on App startup as part of the call to Startup using a delegate callback to register platform specific services. For example either the following:

RootApplication

protected override void RegisterDependencies(ContainerBuilder builder)
{
    base.RegisterDependencies(builder);

    builder.RegisterType<FlickrService>().As<IFlickrService>();
}

Or, App.Xaml.cs

await core.Startup(builder => {
       builder.RegisterType<FlickrService>().As<IFlickrService>();
});

In this case, since the FlickrService doesn’t rely on any platform specific implementation I’ll register it in the RootApplication.

Next, we want the instance of the FlickrService to be passed into the MainViewModel so that we can use it to retrieve feed data. To do this we rely on constructor injection by specifying an IFlickrService parameter to the MainViewModel constructor.

public IFlickrService Flickr { get; }

 

public MainViewModel(IFlickrService flickr)
{
    Flickr = flickr;
}

And then I need to expose a collection of photos and provide a method that will invoke the LoadPhotos method on the IFlickrService.

public ObservableCollection<Photo> Photos { get; } = new ObservableCollection<Photo>();
public async Task Load()
{
    var photos = await Flickr.LoadPhotos();
    photos.DoForEach(Photos.Add);
}

A lot of other MVVM style frameworks rely on the view models inheriting from a base view model which has init/dispose style methods defined for when the view model comes into focus or leaves focus (ie the user arrives or leaves at a page). In this case, because we’re defining our application behaviour in terms of states, we want to invoke code when we arrive at the Main state. Again we do this within the PrimaryRegion by adding a call to WhenChangedTo to the Main state definitiion.

.StateWithViewModel<RegionStates, MainViewModel>(RegionStates.Main)
    .WhenChangedTo(vm => vm.Load())
               
    .OnComplete(DefaultCompletion.Complete)
    .ChangeState(RegionStates.Details)
.EndState()

Before running the application at this point we need to return to our MainPage.xaml and return the layout to what we initially created with the ListView, the ProgressRing and the visual states. I needed to make two minor adjustments:

- Make sure the ListView is visible initially – since we don’t have anything to drive the Loaded/Loading visual state changes, yet
- Add the ItemsSource attribute to be data bound to the Photos property ie ItemsSource=”{Binding Photos}”

Running the application now should display a list of photos, similar to what I had at the beginning of this post. To add back the visual states, all I need to do is adjust the MainViewModel to include its own state manager:

public class MainViewModel : BaseStateManagerViewModelWithCompletion<DefaultCompletion>
{
    public enum MainStates
    {
        Base,
        Loading,
        Loaded
    }

    public MainViewModel(IFlickrService flickr)
    {
        StateManager.Group<MainStates>().DefineAllStates();
        Flickr = flickr;
    }

Into the MainViewModel we’ve added a new enumeration, whose values match the names of the visual states. I’ve also adjusted the base class for the MainViewModel to be one that already has a state manager defined. And finally I’ve called DefineAllStates which will create all the states that correlate to the values in the MainStates enumeration. That’s it! Run the application again, and the visual state changes are already wired up.

The final stretch of this application is handling when the user selects an item from the list of photos. The selected photo needs to be passed through to the details page so that the details of the photo can be displayed on the page. A lot of developers prefer to use command binding to pass through the click event to the method in the view model. To keep it simple I’m just going to wire up an event handler for the ItemClick event on the ListView

<ListView x:Name="FlickrListView"
            ItemsSource="{Binding Photos}"
            IsItemClickEnabled="True"
            ItemClick="FlickrListView_OnItemClick"
>
private void FlickrListView_OnItemClick(object sender, ItemClickEventArgs e)
{
    (DataContext as MainViewModel).DisplayPhoto(e.ClickedItem as Photo);
}

Inside the DisplayPhoto method on the MainViewModel I’m going to track the SelectedPhoto before calling the OnComplete method.

public Photo SelectedPhoto { get; private set; }
public void DisplayPhoto(Photo photo)
{
    SelectedPhoto = photo;
    OnComplete(DefaultCompletion.Complete);
}

In order to pass the selected photo into the DetailsViewModel, the state manager in the PrimaryRegion needs to be updated to hand the selected photo into the DetailsViewModel when the Details state is transitioned to.

StateManager.GroupWithViewModels<RegionStates>()
    .StateWithViewModel<RegionStates, MainViewModel>(RegionStates.Main)
        .WhenChangedTo(vm => vm.Load())
       
.OnCompleteWithData(DefaultCompletion.Complete, vm => vm.SelectedPhoto)
        .ChangeState(RegionStates.Details)
    .EndState()

    .StateWithViewModel<RegionStates, DetailsViewModel>(RegionStates.Details)
        .WhenChangedToWithData<RegionStates, DetailsViewModel, Photo>((vm, data) =>
        {
            vm.Photo = data;
        })
    .EndState();

There are two updates to the StateManager:
- The OnComplete in the Main state is replaced with OnCompleteWithData which is used to extract the SelectedPhoto so that it can be passed through to the new state (in this case the Details state)
- A call to WhenChangedToWithData is used to pass through the selected photo into the Photo property on the DetailsViewModel.

Of course, the DetailsViewModel needs to be updated to include the Photo property. As the Photo property may not be set by the time the DetailsViewModel is set as the DataContext on the page, it’s important that the Photo property raises the PropertyChanged event in the setter.

public class DetailsViewModel : NotifyBase
{
    private Photo photo;

    public Photo Photo
    {
        get { return photo; }
        set
        {
            photo = value;
            OnPropertyChanged();
        }
    }
}

The last thing to do is to update the design of the DetailsPage.xaml to render the photo:

<Page x:Class="FlickrViewer.DetailsPage"
      xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:FlickrViewer"
      xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Image Source="{Binding Photo.media.m}"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"
               Stretch="UniformToFill" />
        <Grid VerticalAlignment="Bottom">
            <Border Background="Black"
                    Opacity="0.9"/>
            <TextBlock Text="{Binding Photo.title}"
                       TextWrapping="WrapWholeWords"
                       FontSize="30"
                       Foreground="White"
                       MaxLines="3"
                       Margin="20,10" />
        </Grid>

    </Grid>
</Page>

Running the application now gives the following interface for viewing the public Flickr feed.

image


********************************************************************************************************************************************************
                 Flickr Download Service

********************************************************************************************************************************************************

public interface IFlickrService
{
    Task<IEnumerable<Photo>> LoadPhotos();
}

public class FlickrService : IFlickrService
{
    public async Task<IEnumerable<Photo>> LoadPhotos()
    {
        var client = new HttpClient();
        var json = await client.GetStringAsync(
                    "
http://api.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1");
        var photos = JsonConvert.DeserializeObject<FlickrData>(json);
        return photos.items;
    }
}

public class Media
{
    public string m { get; set; }
}

public class Photo
{
    public string title { get; set; }
    public string link { get; set; }
    public Media media { get; set; }
    public string date_taken { get; set; }
    public string description { get; set; }
    public string published { get; set; }
    public string author { get; set; }
    public string author_id { get; set; }
    public string tags { get; set; }
}

public class FlickrData
{
    public string title { get; set; }
    public string link { get; set; }
    public string description { get; set; }
    public string modified { get; set; }
    public string generator { get; set; }
    public List<Photo> items { get; set; }
}

Pingbacks and trackbacks (1)+

Comments are closed