metadevblog

Windows Phone 7 App Development

Archive for the ‘WP7’ Category

Long List Selector (Jump List) Example

with 8 comments

I am developing an App that will require the Long List Selector (LLS) functionality that is demonstrated in the  WP7 People selector.  The LLS also provides a good UI for breaking up even modest sized lists in to easy on the eye groups.

The Windows Phone Toolkit on Codeplex (http://silverlight.codeplex.com/releases/view/75888) provides a LLS but there is no documentation to explain how to use it.

The LLS is not a simple control – there is a fair amount of setup and code required before it will work, however I’ve learnt a lot and am quite pleased with the result.

I have replaced the standard List control in my Master Key application with a LLS but have created a cut down version of the App just to focus on using LLS.  I’ve kept the Transitions code in place as well from the previous post.

Before going into the details of LLS I should explain some of the other code that is in the example I’m going to use.

I make extensive use of XML in my applications when working with data, I find the ease with which I can create a robust and arbitrarily complex data model using XML documents far outweighs any disadvantages. Linq for XML is brilliant to work with and over the years I’ve created a set of simple helper extensions which simplify the extraction of values from XML Elements and Attributes.

I always add a copy of XElementHelpers.cs to my projects when I’m using Linq as it removes some of the pain involved when the XML is missing elements. It is a helper class which adds some useful methods for retrieving the values of XML Elements and Attributes. The most commonly used being .ToStringNull() which will return either the Element value or an empty string, however it also returns an empty string even if the Element does not exist (is null).

I’ve also learnt that using a Data Access Layer (DAL) even for small projects brings long term benefits in terms of maintainability and extensibility so my code examples will be slightly more complex as a result.

I’ve put the source for the project here: www.metadev.co.uk/Downloads/PhoneAppLLS.zip please feel free to download and use it.

I have included enough code in the example, which is a cut down version of my Master Key App, to demonstrate the full lifecycle for list management allowing entries to be displayed, selected and edited as well as being able to add and delete entries.

The idea behind the Master Key App is pretty simple – it allows the user to keep a list of keys, passwords and other info in an encrypted form on the phone.  A single ‘master key’ decrypts the information giving access to the entries.  I’ve called the list a Vault which is made up of individual Vault Entries so you will see plenty of references to Vaults in the code!

For this sample I have removed the encryption and much of the other persistence code as it distracts from the LLS.

LLSScreenShot

The LLS groups items under a title.  The title can be arbitrary but for this example I have stuck to grouping the entries based on the first character of the caption.  The classification is however completely open and any parent can be defined.

The Vault is maintained in the DAL as a sorted Dictionary of <Caption, VaultEntry> pairs.  In the previous version these entries where then simply dropped into a standard ListBox.  Sorting is not something that comes for free with either the ListBox or the LLS (or even the Dictionary object for that matter) so maintaining the sorted list is part of the DAL.

        public void SortEntries()
        {
            try
            {
                // sort the keys - Silverlight Dictionary does not have a sort option
                string[] array = new string[VaultEntries.Keys.Count];
                VaultEntries.Keys.CopyTo(array, 0);
                List<string> list = new List<string>(array);
                list.Sort();

                // now recreate the dictioanry                
                Dictionary<string, VaultEntry> sortedEntries = new Dictionary<string, VaultEntry>();
                VaultEntry entry = new VaultEntry();

                foreach (string key in list)
                {
                    VaultEntries.TryGetValue(key, out entry);
                    sortedEntries.Add(key, entry);
                }

                VaultEntries = sortedEntries;
            }
            catch
            {
                throw;
            }
        }
 
A generic Group class is used to hold the entries for the LLS.  The Group class looks like this:

The Group class has two principle properties, a Title which provides the group heading and a List of entries which provide the rest of the group content.

    /// <summary>
    /// Generic Group class which has a string Title and a generic list of items
    /// that are related to the title - for example the Title could be 'k' and then
    /// all the items could have entry such as a persons surname that being with 'k'
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Group<T> : IEnumerable<T>
    {
        /// <summary>
        /// Group title - this provides the jump list titles
        /// </summary>
        public string Title
        {
            get;
            set;
        }

        /// <summary>
        /// List of items in the group
        /// </summary>
        public IList<T> Items
        {
            get;
            set;
        }

        /// <summary>
        /// True when there are items - it's used primarily to create the tiles in the jump list
        /// to indicate visibly where items exist
        /// </summary>
        public bool HasItems
        {
            get
            {
                return Items.Count > 0;
            }
        }

        /// <summary>
        /// This is used to colour the tiles - greying out those that have no entries
        /// </summary>
        public Brush GroupBackgroundBrush
        {
            get
            {
                return (SolidColorBrush)Application.Current.Resources[(HasItems) ? 
                            "PhoneAccentBrush" : "PhoneChromeBrush"];
            }
        }

        /// <summary>
        /// Group constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="items"></param>
        public Group(string name, IEnumerable<T> items)
        {
            this.Title = name;
            this.Items = new List<T>(items);
        }

        /// <summary>
        /// Test for Title equality (and that object exists)
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            Group<T> that = obj as Group<T>;
            return (that != null) && (this.Title.Equals(that.Title));
        }

        /// <summary>
        /// When overriding Equals the GetHashCode has to also be overridden
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Title.GetHashCode();
        }

        /// <summary>
        /// Item collection enumerator
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            return this.Items.GetEnumerator();
        }

        /// <summary>
        /// Item collection enumerator
        /// </summary>
        /// <returns></returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.Items.GetEnumerator();
        }
    }

 

The properties HasItems and GroupBackgroundBrush are primarily used when displaying the jump list:

LLSJumpList

A requirement for the LLS list is for the list elements to be IEnumerable and this is provided with the methods at the bottom of the class.

As the Group class is generic it needs to be solidified to provide the specific functionality needed for this application and this is achieved with this class:

    /// <summary>
    /// This class is responsible for building the specific a-z jump list. If grouping is required around
    /// a different classification then another implementation has to be written.
    /// This implementation is efficient in that there are only two loops through the data, one to create the
    /// Group list with the specific titles and then a second pass through the data itself.
    /// </summary>
    public class AGroupedVaultEntry : List<Group<VaultEntry>>
    {
        private static readonly string Groups = "#abcdefghijklmnopqrstuvwxyz";

        /// <summary>
        /// Build and return a Grouped list based on the current VaultEntry Dictionary
        /// </summary>
        public AGroupedVaultEntry()
        {
            DataAccessLayer DAL = (Application.Current as App).DAL;

            // create the groups by simply iterating over the group string
            foreach (char c in Groups)
            {
                Group<VaultEntry> g = new Group<VaultEntry>(c.ToString(), new List<VaultEntry>());
                this.Add(g);
            }

            List<Group<VaultEntry>> list = this;

            // now iterate over the VaultEntries and add them to the appropriate group
            foreach (VaultEntry vaultEntry in DAL.VaultEntries.Values)
            {
                // get the first char (lower case) of the entry
                string c = VaultEntry.GetCaptionGroup(vaultEntry);
                
                // calculate the index into the list
                int index = (c == "#") ? 0 : c[0] - 'a' + 1;

                // and add the entry
                list[index].Items.Add(vaultEntry);
            }
        }
    }

 

This class is designed to create and return a List of Group VaultEntries with each Group header being a letter of the alphabet (plus # for all others). It will then populate the Group Items with the actual VaultEntries based on their group Title (a-z) – this association is made using the GetCaptionGroup which has been added specifically to the VaultEntry class for this purpose:

/// <summary>
/// Get the first letter of the caption or #
/// </summary>
public static string GetCaptionGroup(VaultEntry Current)
{
    char key = char.ToLower(Current.Caption[0]);
    if (key < 'a' || key > 'z')
    {
        key = '#';
    }
    return key.ToString();
}
 
Because the Vault Dictionary is already sorted by the Caption then the code for adding the appropriate 
entries is simplified and each VaultEntry can be added to the list in a single pass.
 
So far all the code discussed has been written to provide suitable data for the LLS, the final part is to set up the control itself so that it can make use of it.
 

The LLS can be used as a standard list quite simply, this XAML will display the Vault Entries without any header decoration:

<toolkit:LongListSelector VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
                          Margin="6,6" 
                          Name="longListSelectorVaultEntries" 
                          IsFlatList="False"  IsBouncy="True"
                          Loaded="OnLoaded" SelectionChanged="OnSelectionChanged">

    <toolkit:LongListSelector.ItemTemplate>
        <DataTemplate>
            <Grid Background="Transparent">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Image Width="55" Source="/Toolkit.Content/LOCKED.png" 
                       VerticalAlignment="Top"/>
                <StackPanel Grid.Column="1"  VerticalAlignment="Top">
                    <TextBlock Text="{Binding Caption}" 
                               Style="{StaticResource PhoneTextLargeStyle}" 
                               Margin="0,8,0,0"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </toolkit:LongListSelector.ItemTemplate>

</toolkit:LongListSelector>

 

The TextBlock.Text is bound to the Caption in the Vault Entry it is currently iterating over.  I’ll show later that multiple items can be displayed here.

There are two events defined – OnLoaded which is used to fetch and display the current Vault Entry list and OnSelectionChanged which is used to capture the user selecting an entry for display/editing.

The OnLoaded event looks like this:

private void OnLoaded(object sender, RoutedEventArgs e)
{
    DataAccessLayer DAL = (Application.Current as App).DAL;

    // when the page is being resumed this event will fire but it's not
    // appropriate to display the info as we actually want to hide it.
    if (DAL.ResumeFromDeactivation)
    {
        return;
    }

    // if the key has changed then reload the value entries
    if (DAL.ListReloadRequired || longListSelectorVaultEntries.ItemsSource == null)
    {
        ShowVault();

        DAL.ListReloadRequired = false;
    }
}
 
The AGroupedVaultEntry method is called and the IEnumerable Group<> is applied to the LLS ItemsSource.
 
The SelectionChanged event simply returns the selected item as the VaultEntry object from the Group List
 
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // edit the selected item
    VaultEntry entry = (VaultEntry)longListSelectorVaultEntries.SelectedItem;
    if (entry == null) return;

    longListSelectorVaultEntries.SelectedItem = null;

    // pass the get to NewItem so that it can be looked up
    this.NavigationService.Navigate(new Uri(string.Format("/EditEntry.xaml?type=edit&key={0}", entry.Caption), 
              UriKind.Relative));
}
 

The SelectedItem is set to null again so that it can be reselected.
 
LLSStandardList
 
In order to provide the grouping and the jump list additional DataTemplates have to be added to the XAML none of which are particularly complex:
 
<toolkit:LongListSelector VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="6,6" 
                            Name="longListSelectorVaultEntries" 
                            IsFlatList="False"  IsBouncy="True"
                            Loaded="OnLoaded" SelectionChanged="OnSelectionChanged">

    <toolkit:LongListSelector.GroupItemsPanel>
        <ItemsPanelTemplate>
            <toolkit:WrapPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </toolkit:LongListSelector.GroupItemsPanel>

    <toolkit:LongListSelector.GroupItemTemplate>
        <DataTemplate>
            <Border Background="{Binding GroupBackgroundBrush}" Width="99" Height="99" Margin="6" 
                                IsHitTestVisible="{Binding HasItems}">
                <TextBlock Text="{Binding Title}" 
                            Style="{StaticResource PhoneTextExtraLargeStyle}"
                            Margin="{StaticResource PhoneTouchTargetOverhang}"
                            Foreground="{StaticResource PhoneForegroundBrush}"                                        
                            VerticalAlignment="Bottom"/>
            </Border>
        </DataTemplate>
    </toolkit:LongListSelector.GroupItemTemplate>

    <toolkit:LongListSelector.GroupHeaderTemplate>
        <DataTemplate>
            <Border Background="Transparent">
                <Border Background="{StaticResource PhoneAccentBrush}" 
                        Width="55" Height="55" HorizontalAlignment="Left">
                    <TextBlock Text="{Binding Title}" 
                                Foreground="{StaticResource PhoneForegroundBrush}" 
                                Style="{StaticResource PhoneTextLargeStyle}"
                                VerticalAlignment="Bottom"/>
                </Border>
            </Border>
        </DataTemplate>
    </toolkit:LongListSelector.GroupHeaderTemplate>

    <toolkit:LongListSelector.ItemTemplate>
        <DataTemplate>
            <Grid Background="Transparent">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Image Width="55" Source="/Toolkit.Content/LOCKED.png" VerticalAlignment="Top"/>
                <StackPanel Grid.Column="1"  VerticalAlignment="Top">
                    <TextBlock Text="{Binding Caption}" 
                               Style="{StaticResource PhoneTextLargeStyle}" Margin="0,8,0,0"/>
                    <TextBlock Text="{Binding Identity}" 
                               Style="{StaticResource PhoneTextNormalStyle}" Foreground="DarkGray"/>
                    <TextBlock Text="{Binding OtherInfo}" 
                               Style="{StaticResource PhoneTextNormalStyle}" Foreground="DarkGray"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </toolkit:LongListSelector.ItemTemplate>

    <!--<toolkit:LongListSelector.ListFooterTemplate>
                    <DataTemplate>
                        <Border Background="Transparent" Height="159">
                            <TextBlock Text="End..." />
                        </Border>
                    </DataTemplate>
                </toolkit:LongListSelector.ListFooterTemplate>-->

</toolkit:LongListSelector>
 
There is a lot of it but it’s pretty straight forward – I copied it pretty much verbatim from the Toolkit example code.
 
The GroupItemsPanel is used to display the Group headers as shown above.  The Orientation=”Horizontal” forces the panel items to be displayed as a horizontal list, when Vertical is used then the items are centred as a long scrolling list – useful for when there are wide group headings.  Horizontal is perfect for the a-z listing.
 
The GroupItemTemplate is used to create the individual items that will be shown in the Panel and is bound to the HasItems method in the Group class.  The Background is bound  GroupBackgroundBrush in the same class (it makes use of HasItems internally to choose the appropriate colour) as is the Title (Title in turn has been set previously from the Vault Entry Caption).
 
The GroupHeaderTemplate is used to create the heading for each Group that has items associated with it, empty groups are omitted.
 
That’s it! I hope this explanation has been useful and that it will help you get up to speed more quickly than I was able to with LLS.
 
One final thing though – I believe there is a bug in the LLS that prevents it from showing the last group in the list correctly.  If I click on a group header to display the jump list and then click ‘w’ the display looks like this:
 
LLSLastW
 
There are another two entries below this that are not displayed – i.e when the LLS displays this last list entry it should start a couple of rows higher.  I got round this by adding a footer to the LLS:
 
<toolkit:LongListSelector.ListFooterTemplate>
    <DataTemplate>
        <Border Background="Transparent" Height="159">
            <TextBlock Text="End..." />
        </Border>
    </DataTemplate>
</toolkit:LongListSelector.ListFooterTemplate>
 

This still isn’t perfect but at least it does seem to hide the problem somewhat.

All in all the Long List Selector is a great control that adds a lot of functionality to the mundane list control.

Oh – I forgot to show that the LLS can show more than lists – you will have seen this already in the LLS in the Toolkit example.  All you need to do is add the extra info to the StackPanel:

 
<StackPanel Grid.Column="1"  VerticalAlignment="Top">
    <TextBlock Text="{Binding Caption}" 
                Style="{StaticResource PhoneTextLargeStyle}" Margin="0,8,0,0"/>
    <TextBlock Text="{Binding Identity}" 
                Style="{StaticResource PhoneTextNormalStyle}" Foreground="DarkGray"/>
    <TextBlock Text="{Binding OtherInfo}" 
                Style="{StaticResource PhoneTextNormalStyle}" Foreground="DarkGray"/>
</StackPanel>
Advertisements

Written by metadevblog

December 27, 2011 at 11:58 am

Posted in c#, WP7

Silverlight Metro Theme

with 3 comments

The WP7 currently has a tiny market share so writing apps to target both the WP7 and the desktop using the same code base makes a great deal of sense to me.  Especially for tools such as Master Key which has as much validity (if not more) on the desktop as it does on my phone.

I picked up a copy of the Metro Theme from a project on Codeplex and copied one of the Master Key XMLA pages over.  It worked pretty well but in the end I’m left in the position of having to do a great deal of work to make the page fit into a browser window rather than the phone.

http://metrotheme.codeplex.com/

There are a number of issues.  Firstly the fonts and lines used to render on the WP7 screen look huge when viewed in normal Windows resolution; secondly the theme is incomplete and has missing features.  While all this can be fixed by modifying the theme it’s a lot of work.

Secondly, and obviously, the browser environment is significantly different to that of the WP7. The hardware back and close buttons have to be provided in Silverlight and handled in the browser. From a browser UX point of view these buttons would typically be at the top of the screen.  Handling ‘back’ is less of a problem as the browser supports this. However the browser also provides ‘forwards’ as well and the user can also pick an item from the navigation history and jump directly there, neither of which are possible in WP7.

Having experimented with directly porting the WP7 app and finding it a less than compelling outcome I searched around for the Silverlight theme to use.  After some experimentation I picked up a Window 7 theme that the Silverlight dev team released (see Tim Heuer’s blog http://timheuer.com/blog) and have used this as the basis for the Windows version.

The project template also provides a navigation framework which is really useful and this integrates into the browser navigation history providing back/forward support.

It was pretty simple to take a WP7 page and convert it to Win7, for the most part the XAML simply pasted directly and only needed to be lightly manipulated to fit.  Over the space of a couple of days I had pretty much replicated the App, however…

However, it was apparent pretty quickly that the integration into the browser navigation was not going to work, the history just kept getting bigger and worse I could jump forward when it was illogical to do so.  I spent a lot of time trying to make it work but in the end it was just too confusing from a UX point of view.  I switched it off and suddenly the navigation became a whole lot more logical as the framework is tabbed

image

The ApplicationBar in WP7 is a great little control and works really well on the phone.  It’s location at the bottom of the screen near to the where your fingers lay makes for a great UX. 

I considered briefly writing a version of this for Silverlight but decided against it as the more normal windows layout is better and the Ribbon interface is clean and well accepted.

In the end I decided that the simplest approach was to revert from having ApplicationBar based button back to a more traditional approach with the buttons embedded into the UI.  This is perfectly valid when there are so few buttons required to make the App work.

It was apparent as soon as I started to think about a Silverlight version of Master Key that combining the item selection and its content into a single page would be the best approach; so for WP7 the user is shown the Vault Entries as a list:

edit_vault

And when an item in the list is selected is is displayed on a separate screen:

edit_entry

This works really well – the UI is easy to use and quite clear in its intent.

Combining the two screens and modifying the UI slightly gives this screen:

image

Master Key on WP7 relies on the phone timeout kicking in to prevent unauthorised access, on my phone it’s set to a minute after which it will tombstone and the user has to re-enter the password to regain access to the information when the app is reactivated. 

In the browser environment this has to be done within the page by running a timer that will prevent access if there is no activity.  I’ve added a progress bar at the bottom of the screen – after 30 seconds of inactivity the page will close and revert to the login screen. Job done!

The core functionality of Master Key for WP7 has moved without change to the Silverlight version.  I’ve not had to touch a line of code in the data access layer to make it work and this includes the WCF service code (though to be fair I added the service reference to the new project and it auto-generated the appropriate code).

You don’t need to be too eagle eyed to see that the two vaults are the same – I simply loaded the demo vault into both versions of the App from the web service.

So the end result is that I now have a WP7 and Windows version of Master Key and the vault files are interchangeable between the two.  Also I now have a good idea as to how much additional effort has to be put in to convert between the two platforms.  I have a bit of work still outstanding to be able to launch the desktop version from the MasterKey service but I don’t anticipate this taking a lot of time.

One thing – I am pretty frustrated with myself for using the Windows 7 theme rather than Cosmopolitan as this is the closest the the WP7 Metro theme.  I was hoping I could simply swap themes but this failed on my first attempt to replace the Windows 7 theme. I’ll have to spend some time working this out when I have a chance.

Written by metadevblog

July 13, 2011 at 10:50 am

Posted in Master Key, Silverlight, WCF, WP7

Master Key version 2.0 published

leave a comment »

I’ve spent the last couple of months working on upgrading Master Key to version 2.0.  It’s been quite an interesting process as it makes use of the remote storage facility I have written about previously. 

I had this working in version 1.0 to allow me to save/load my data while the App was under development but it was really just prototype code.  I have added full support in this version of the App so that Load Vault and Save Vault are fully defined in the UI and perform robustly.

Master Key version 1.0 was primarily about creating a completed and fully functional App and understanding the Marketplace publishing process.  Version 2.0 rounds this out to create, what I consider, to be a really useful App that can be used day to day to remember multiple passwords; I’ve surprised myself by currently having 21 items stored.

Oh and I’ve not been disappointed with sales either, as they have lived up to my expectations of being pretty minimal.   Writing Apps for the WP7 at this time was never going to be about the money as the potential market is tiny. 

However I have my doubts about writing Apps for any platform; there have been some notable successes but for the vast, vast majority of Apps the cost of development far outweighs the money that can be generated from sales.

I chose to write Master Key because I had a well defined requirement and it’s a very widespread issue that we all pretty much share.  However it is lost amongst a mass of other competing Apps, some good but mostly crap and it’s really hard to get noticed.

When I started writing Master Key I was slightly concerned that it might be refused because it is not unique but this was quickly dispelled when I saw how many password saving Apps had been published – clearly uniqueness is not a criteria that is applied otherwise there would be thousands of ‘squatter’ Apps blocking the quality or more functional solutions.  In an ideal world the quality Apps would bubble to the top but this is not the case, first mover advantage especially with a few good reviews reduce the chances of similar Apps quite significantly.  And of course its relatively easy to ‘game’ the system by creating false accounts and/or getting mates to post positive reviews.

Without throwing money at good old fashioned marketing and advertising its hard to see how an App really stands much chance of being distinguished and as the money to be made is pretty low to start with that’s something of a fools errand.

Written by metadevblog

July 12, 2011 at 3:58 pm

Posted in Master Key, WP7

Master Key sent for approval

leave a comment »

I’ve finally sent Master Key off to the App Hub for approval so I now have to wait and see if it passes and gets published.

Writing my first Windows Phone app has been an interesting process.  Like anything new it’s taken quite a long time to go from a blank sheet of paper through to the final solution and now that I’ve done it once I’m pretty confident that next time it will be much quicker.

Writing for the WP7 platform is great, especially as I’m very experienced with Visual Studio, c# and to a lesser extent Silverlight.  Microsoft have excelled themselves in producing such a great development environment.

I’ve now completed version 1.0 of the application and am already thinking of version 2.0; I would have continued to add features but really wanted to complete the first deployment and get onto the Marketplace.  As I follow a RAD process it can be quite hard to be disciplined and stop development as feedback testing suggests new features and better approaches to solving a problem.

Setting up my account on App Hub has been very time consuming.  As a UK based developer I have to jump through a bunch of US IRS hoops in order to get paid the full royalty amount without losing 30% in withholding taxes.  While this is not much of an issue currently as I’m not expecting any significant royalties it will hopefully be worthwhile in the future.

Along with putting the app up for approval I’ve finally deployed the MDL website.  It does not have a great deal of content currently but I have published the help for Master Key.  The web site is at:

http://www.metadev.co.uk

I used the new WebMatrix tool to create the site and am reasonably happy with the outcome.  I use Discount.ASP as my hosting service; I’ve used them for a few years now and am pretty pleased with their level of support and the functionality.

It took me a few goes to get the deployment to work properly but this was partly down to me using .NET4.0, having to configure IIS7 and modify one of the CSHTML pages so that it wasn’t attempting to connect to a database.

I tried to use FTP but it didn’t work very well and read the recommended method was to use Web Deploy. I used the ‘Import publish settings’ to get the correct info from Discount.ASP and all I had to do was enter my password.  You need to use ‘Validate Connection’ and ensure it passes before deploying.

Take a look at this support article on Discount.ASP site:

http://support.discountasp.net/KB/a891/how-to-deploy-an-application-using-web-deploy-feature.aspx

Configure IIS7 by checking the box to give your Discount.ASP login account access.

image

I added the following to the web.config – I am not convinced that this is necessary as I do not have a database.

    <connectionStrings>
        <add name="LocalSqlServer" connectionString="x" providerName="System.Data.SqlClient" />
    </connectionStrings>
I still had errors but this time they were more explicit and commenting out this line: 
 

//WebSecurity.InitializeDatabaseConnection("StarterSite", "UserProfile", "UserId", "Email", true);

in _AppStart.cshtml fixed it and my web site was deployed.

Written by metadevblog

May 9, 2011 at 5:49 pm

Starting an WP7 app with alternate start page

with one comment

I have had to change the method for starting Master Key in order to fit in with the way that the WP7 page navigation works.  On the face of it the issue is pretty trivial and one that users would typically only hit once or twice during their use of the App.

WP7 uses a browser based metaphor for moving between pages in an application.  So you can move from page to page and the history is retained on a stack, pressing the back button returns to the previous page.  This can seem confusing, Page1 can invoke Page2 and this can in turn invoke Page1.  However it will NOT be the original Page1 but a new instance of it.  Pressing back on this last page will will return to Page2 and pressing back again will go to the first instance of Page1 (pressing back one more time will close the App).

Master Key requires the user to enter the master key password in order to access the secured information.  However when the user first starts Master Key the user has to enter the password that will be used on subsequent visits and I have a different UI for this.

I originally wrote the code so that the MainPage.xaml checked to see if the users vault (the list of passwords) already exists and if it does not then the page re-directs in the OnNavigatedTo event in MainPage.xaml to a password entry page called EnterPassword.xaml where the user enters the master key.

If the user enters a valid master key and presses ‘ok’ then the vault is set up and the app is ready to use.

However if the user hits the ‘back’ button before entering a valid password then the App is left in a state of limbo and will hit the same OnNavigatedTo event in the MainPage.xaml which will re-invoke the EnterPassword.xaml page again.

I could write code to trap this cycle but wanted to look at the alternatives; one of which is to have different start pages.  I.e. when the vault exists then the MainPage.xaml will be the start page, and if the vault does not exist then EnterPassword.xaml should be the start page.

This is best achieved using Uri remapping by adding the code to the App.xaml.cs Application_Launching event:

private void Application_Launching(object sender, LaunchingEventArgs e)
{
    DAL.LoadVault();

    UriMapper mapper = Resources["uriMapper"] as UriMapper;
    RootFrame.UriMapper = mapper;

    // Update the mapper as appropriate
    if (DAL.VaultExists)
        mapper.UriMappings[0].MappedUri =                   
          new Uri("/MainPage.xaml", UriKind.Relative);
    else
        mapper.UriMappings[0].MappedUri =                   
          new Uri("/EnterPassword.xaml?type=newvault", UriKind.Relative);
}

The App.xaml has to be modified to have a default UriMapper added to it (note the addition of the xmlns:navigation namespace).

<Application
    x:Class="WPVault.App"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"      
    xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:navigation="clr-namespace:System.Windows.Navigation;assembly=Microsoft.Phone"
    >

    <!–Application Resources–>
    <Application.Resources>
        <navigation:UriMapper x:Name="uriMapper">
            <navigation:UriMapping Uri="/MainPage.xaml" />
        </navigation:UriMapper>
    </Application.Resources>

It works and I can see uses for it in other Apps; however it comes at a cost that ruled it out for me as the back button behaviour is to return to the page that was navigated from.  This means that once the user has entered the master key password and started to make entries then hitting the back button will return to the EnterPassword.xaml page again which is not a great UX.

I ended up combining the Open and New Vault pages into MainPage.xaml as there is a lot of overlap in their functionality – I then simply drive changes into the UI in the OnNavigatedTo event as appropriate to what the user is doing.  Thus the ‘new vault’ info is shown when the vault does not exist and then when the user returns back to this page again and the vault does exist the ‘open vault’ page is shown.

image

 

Written by metadevblog

May 1, 2011 at 2:59 pm

Posted in Master Key, Silverlight, WP7

Build Version numbers

leave a comment »

A quick note on Visual Studio Build Version numbers as there is some quite confusing notes around and it’s taken me a few hours to get this process working end to end.

Version numbers are vital when building software that is to be released to the outside world and automating at least part of the process ensures that you can pretty much keep track of what is out there.

The default process provides a build number like this: 1.3.4138.18913.

This number represents the following information: Major.Minor.Build.Revision

So the build is version 1.3 and the build number is 4138 and the revision number is 18913.

The build information is maintained in AssemblyInfo.cs

// 1.0 – Password Vault
// 1.1 – first version released to MSFT
// 1.2 – renamed to Master Key
// 1.3 – added remote save/load and refactor UX
[assembly: AssemblyVersion("1.3.*")]

The auto generation of the build/revision only occurs when you remove either AssemblyVersion or AssemblyFileVersion; I have only worked with the former.

I have to set the major/minor build numbers and I include a comment to track the changes.  I only change these when there is a big change to the app and will also set a minor version prior to each release.

I use VS TS Source Control and as part of the development process I label the each change to the Minor build when I have checked the updates in.  This allows me to quickly pull a version from source control when I have an issue reported to me.

Each time I build the solution the Build and Revision numbers will change. The Build is actually the number of days since 31/12/1999 and so only changes when the date changes.  The Revision is the number of seconds since midnight/2.

Getting this info into an About Box where it’s visible to the user is the next task.  There are a bunch of ways to do this but the simplest is the following:

System.Reflection.Assembly execAssembly = System.Reflection.Assembly.GetExecutingAssembly();

string[] version = execAssembly.FullName.Split(',');
textBlockVersion.Text = version[1];


The output looks like this:

image

 

Written by metadevblog

May 1, 2011 at 11:06 am

Posted in c#, Silverlight, WP7

Tagged with , ,

WP7 accessing a Windows Service DataService

leave a comment »

This application took 2 minutes to write…

Now that I have demonstrated how Silverlight can access a DataService that is hosted by a WindowsService the final example is to show that it will work on a WP7.

I created a new Windows Phone application and added a Button and Textbox.

I added a Service Reference to the DataService to the Solution.

Finally I pretty much copied the code from the Silverlight application in the previous post.

using WPService.DataService;
using System.Xml.Linq;

namespace WPService
{
    public partial class MainPage : PhoneApplicationPage
    {
        DataServiceClient dataService = null;

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            dataService = new DataServiceClient();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                dataService.GetDateTimeCompleted += new EventHandler<GetDateTimeCompletedEventArgs>(dataService_GetDateTimeCompleted);
                dataService.GetDateTimeAsync();
            }
            catch (Exception ex)
            {
                textBlock1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }

        void dataService_GetDateTimeCompleted(object sender, DataService.GetDateTimeCompletedEventArgs e)
        {
            try
            {
                XElement xResult = (XElement)e.Result;

                string date = xResult.Element("Date").Value;
                string time = xResult.Element("Time").Value;

                textBlock1.Text = string.Format("The date is: {0}\nThe time is: {1}", date, time);

            }
            catch (Exception ex)
            {
                textBlock1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }
    }
}

 

I clicked the Button!

image

Says it all!

Written by metadevblog

March 29, 2011 at 10:00 pm

Posted in WCF, WP7

Tagged with , ,