jedidja.ca

There's always something new to learn today

Version control for your non-profit's website

Although there was some automated backup at the free shared hosting account, that is of little comfort when you've made several changes to a website in one day and realize (too late) that you've lost some important work.

Developers are quite used to some sort of version control system always having their back and this situation is no different.

Visual Studio Online

I signed the Festival up for a free visualstudio.com account which gives you 5 free users and unlimited private repositories. You also get planning/tracking tools and team collaboration capabilities. I'm the only developer so there probably won't be much of that last item happening...

Creating a new project and uploading the existing PHP website was extremely straightforward and within two days of making changes, I was extremely glad to have history available :)

(Note: Github also provides free Bronze plans to non-profit and says they can "work something out if you're not US-based." There is still somewhat of a learning curve associated with Git even using the very nice native apps they've created. Sticking to my goal of "creating the right foundation" and "making it easy for people to help" was enough for me to pick VS.)


Join a community of like-minded developers who want to help Canadian non-profits.

Master of your (non-profit's) domain

When I first started volunteering with the Orléans Festival, we were lucky enough to have free web hosting by a local company. There was "unlimited" disk space and bandwidth on a shared Linux box.

Why move?

As I discovered while registering us for Office 365, there are only so many DNS records you can manipulate through cPanel. Since some records require going through the DNS hosting website and our provider held our registration along with his other customers, there was a bit of a bottleneck and unnecessary layer of red tape.

Personally I think every entity should be in control of as much of as its assets as possible. One of my original goals was to get everything tech-related into a "proper" state so that anyone could take over in future years. Controlling your own registration and DNS is definitely part of that.

After all, you never know what could go wrong. Perhaps there's a disagreement and all of a sudden your nameserver registration mysteriously vanishes. Perhaps the company is sold and the new owners forget to renew your domain. Perhaps you're switching over to Office 365 and realize that you cannot set the SRV records for Lync yourself. anyway...

The Process

Moving domains with .ca is fairly straightfoward. You need whoever currently registers the domain to unlock it and then provide you with the transfer code. After that, you follow the instructions provided by your new DNS hosting service.
easyDNS

I chose to move us to easyDNS, for the simple reason that I and my friends have used them for years without a single issue. And they're Canadian! There may well be places cheaper than $15/year but I wonder if they have as good a track record. (My second choice would be dnsimple.com)

Key Records

It's important you make note of all the existing records at your current host/registrar. Here are the ones you definitely want to check:

  • MX (for Mail)
  • A (for your website)
  • CNAME (usually to redirect www.mydomain.ca to mydomain.ca)

Join a community of like-minded developers who want to help Canadian non-profits.

Displaying items

Without worrying about storing or retrieving from the database, let's integrate the model classes we created a while ago.

MainPageViewModel.cs

public class MainPageViewModel : PropertyChangedBase
{
  private ObservableCollection<Item> items;

  public MainPageViewModel()
  {
      items = new ObservableCollection<Item>();
      LoadData();
  }

  public ObservableCollection<Item> Items
  {
    get { return items; }
    set
    {
        items = value;
        NotifyOfPropertyChange(() => Items);
    }
  }

  public void LoadData()
  {
    Items.Add(new Item(0, "almond butter"));
    Items.Add(new Item(0, "coconut flour"));
    Items.Add(new Item(0, "pistachios"));
  }     
}

You'll notice that we've renamed Name to ItemName; that's because if we want to display sample data in the designer, we'll end up conflicting with the one defined in ReflectionTypeNode. You'll want to update SampleData/MainViewModelSampleData.xaml accordingly.

MainPage.xaml

<phone:PhoneApplicationPage
    // ... all the normal stuff
    xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
    xmlns:vm ="clr-namespace:IsItWorthIt.ViewModels"
    d:DataContext="{d:DesignInstance Type=vm:MainPageViewModel, IsDesignTimeCreatable=True}"    
    cal:Bind.AtDesignTime="True">

<Grid Background="Transparent">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>

  <StackPanel Grid.Row="0" Margin="12,17,0,28">
    <TextBlock Text="IS IT WORTH IT?" 
    Style="{StaticResource PhoneTextNormalStyle}"/>
   <TextBlock Text="items" Margin="9,-7,0,0" 
    Style="{StaticResource PhoneTextTitle1Style}"/>
  </StackPanel>

  <Grid Grid.Row="1" Margin="12,0,12,0">

    <phone:LongListSelector x:Name="Items" 
        ItemsSource="{Binding Items}">

      <phone:LongListSelector.ItemTemplate>
        <DataTemplate>
          <StackPanel Margin="0,0,0,17">
            <TextBlock x:Name="ItemName"
              Text="{Binding ItemName}" 
              TextWrapping="Wrap"/>
          </StackPanel>
        </DataTemplate>
      </phone:LongListSelector.ItemTemplate>

    </phone:LongListSelector>

  </Grid>

</Grid>
</phone:PhoneApplicationPage>

Note: by default, the framework will automatically bind a control based on its name. For example, our LongListSelector having x:Name="Items" means that it will automatically have its ItemsSource set to Items from our ViewModel. However, this doesn't work at design time, so I've left the explicit binding in.

The same is true of the TextBlock inside our DataTemplate. You don't have to explicitly bind it to ItemName if you're not worried about the design-time experience.

MainPage.xaml.cs

public partial class MainPage : PhoneApplicationPage
{
  public MainPage()
  {
    InitializeComponent();            
  }   
}

displaying-items

Interested in learning more about developing for Windows Phone 8? Enter your information to receive e-mail notifications on relevant articles.

Further Reading

Focusing

Welcome back :)

After wasting spending a couple weeks writing the "sprint two" post on data entry (and MVVM, DI, etc), I've realised that I need to focus on audience. Sean d'Souza and The Brain Audit have finally sunk in.

When I started this series of posts, I didn't really consider who exactly I was writing for. Trying to balance between beginner and intermediate is tough, especially when I'm fairly new to WP8 development. Someone is bound to get bored or lost.

So I've made a decision.

These posts are targeted at experienced Windows developers, preferably with some knowledge of WPF and MVVM, who want to write a Windows Phone 8 app.

Of course this means the series has officially been renamed to "Journey to the WP8 App Store in 3 months" :) Eep!

I'm going to be using Matteo Pagani's series on Caliburn Micro with Windows Phone 8 as a reference (along with SO, of course). In case Matteo's site disappears, I'm copying the code examples here and modifying as required to work with the latest version of Caliburn.Micro. Total credit goes to Matteo.

Moving to Caliburn.Micro

I come from a heavy Prism background, so seeing the PhoneBootstrapper class made my eyes light up. (On a side note, I don't see p&p paying any attention to Prism for WP8 which is why I had to choose between MVVM Light and Caliburn.Micro)

To be honest, this looks an awful lot like configuring Prism. Unity has been replaced with PhoneContainer and there a few phone-specific methods, but the code should be quite recognizable.

Create a Bootstrapper

public class Bootstrapper : PhoneBootstrapper
{
PhoneContainer container;

protected override void Configure()
{
  // Fixes a superfluous compiler error while viewing App.xaml
  // Value cannot be null. Parameter name: rootFrame
  if (Execute.InDesignMode)
  {
    return;
  }

  container = new PhoneContainer();
  container.RegisterPhoneServices(RootFrame);
  container.PerRequest<MainViewModel>();

  AddCustomConventions();
}

static void AddCustomConventions()
{
}

protected override object GetInstance(Type service, string key)
{
    return container.GetInstance(service, key);
}

protected override IEnumerable<object> GetAllInstances(Type service)
{
    return container.GetAllInstances(service);
}

protected override void BuildUp(object instance)
{
    container.BuildUp(instance);
}
}

Update (clean up) App

We declare the bootstrapper inside Application.Resources, and since we're letting the framework configure everything for us, we can strip these files down to a minimum.

<Application
x:Class="IsItWorthIt.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:IsItWorthIt">

<Application.Resources>
    <local:Bootstrapper x:Key="Bootstrapper"/>
</Application.Resources>

</Application>

and the code-behind

public partial class App : Application
{
    public static PhoneApplicationFrame RootFrame { get; private set; }

public App()
{
  // Standard XAML initialization
  InitializeComponent();

  // Show graphics profiling information while debugging.
  if (Debugger.IsAttached)
  {
      // Display the current frame rate counters.
      Application.Current.Host.Settings.EnableFrameRateCounter = true;

      // Prevent the screen from turning off while under the debugger by disabling
      // the application's idle detection.
      // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
      // and consume battery power when the user is not using the phone.
      PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
  }
}
}

Displaying our view

Since our application comes with a built-in sample view and viewmodel, let's connect them first. Caliburn.Micro seems to rely on convention over configuration which means a couple things for us:

  • Views and ViewModels are stored in separate folders, named Views and ViewModels. Note that the sample app already creates ViewModels for you.
  • The namespaces for Views and ViewModels should also end in Views and ViewModels.
  • A View and ViewModel are linked by name (e.g. Items <-> ItemsViewModel)

To update the demo app,

  1. Create a Views subdirectory at the same level as ViewModels
  2. Move MainPage.xaml to Views
  3. Update its namespace (remember to update the full class name in .xaml as well. You'll also need to update the path to the SampleData folder by prepending ../)
  4. Delete the code in the constructor that's setting the DataContext (Caliburn.Micro is doing this automagically for us now)
  5. Delete the entire OnNavigatedTo method
  6. Delete the line in DetailsPage.OnNavigatedTo that is setting the DataContext. Details will just have to be broken for now.
  7. Rename MainViewModel to MainPageViewModel
  8. Add a call to LoadData from the MainPageViewModel constructor.
  9. Double-click on Properties/WMAppManifest.xml and update the Navigation Page to Views/MainPage.xaml

After performing the appropriate steps to get the sample MainPage view and viewmodel configured appropriately, you should be able to run the application and see....exactly what you would have seen before we introduced Caliburn.Micro :)

caliburn-integrated

Interested in learning more about developing for Windows Phone 8? Enter your information to receive e-mail notifications on relevant articles.

The developer and the bathroom remodelling

bathroomWe recently had our ensuite bathroom renovated. Remodelling a small ensuite is a simple job that should have taken 7 to 10 days. Almost eight weeks later, we're still not able to sign off on the work. We have experienced terrible communication, a complete lack of quality/workmanship, outright mistakes, delays and a large bill that is no longer defendable. And, we went with a reputable company: Home Depot.

(Feel free to add your horror stories on remodelling below.)

Yesterday morning we had another 'final' inspection, this time with a Home Depot manager present. I was discouraged to learn that the official company attitude is "we'll keep fixing things until it's right". Sure, there was an 'apology', and a tacit acknowledgement that we might have been inconvenienced, but no actual compensation offered/promised.

I felt frustrated. Helpless. Angry.

After all, we had hired experts to do this renovation. For the amount we paid, we expected:

  • someone to oversee the project
  • to be told options we needed to chose and when
  • the appropriate supplies ordered on time
  • competent tradesmen
  • daily inspections to make sure the work was done properly (or at least quickly corrected.)

And yet we got none of that. We did the inspections and pointed out mistakes. We answered the tradesmens' questions. We had to prompt/remind them to order materials. We basically acquired all the stress of managing a project.

I mentioned my thoughts to Lisa, and wondered out loud if they (the contractors) ever go home at night and feel bad about screwing over their customers. Do they even care? Or do they think that they are really doing the best they can, things happen, and we should still be paying full price regardless of all the problems they caused?

Developers are just as guilty

And then it dawned on me - as developers, we do this to our clients.

All The Time.

At the very least, we did it in the past and have vowed to improve.

Think about it. Have you ever signed up for a fixed-price contract for a set of known features, but when you delivered the project, the customer said:

  • but there are still all these bugs
  • this doesn't look/work as I expected
  • and I'm not paying you the rest until ...

What happened when you went home from work that evening?

Did you empathize with the client?

More than likely, I'm guessing that you were somewhat annoyed/angry at the client for not understanding:

  • that software is complex and there's bound to be bugs
  • that you put in extra time to try to satisfy the client and your effective hourly rate has gone down the toilet
  • and that you deserve to be paid in full because, in the end, you completed all project deliverables.

This bathroom remodelling experience really made me think. As little as I understand about installing a vanity properly and re-connecting pipes, my clients probably understand even less about what I do to deliver their software. And if something doesn't work, they probably feel frustrated and helpless.

Is there hope?

Of course. We can keep returning to the principles of agile development to provide value on an ongoing basis: smaller iterations, more involved customers, more automated testing for our code/deployments.

I smile thinking about how awesome it would have been had these principles been followed during our remodelling job :) By day three, when we realised how many mistakes were being made, we would have had them stop. They would have been paid for their time, and we could have refocused on the best way to finish the job.

What next?

Thanks for reading about my aha! moment. As someone who's been keen on "agile" and TDD for years, I've never identified as closely with how we are trying to help our clients until now.

The next time you are out of your element and dependant on another skilled professional, think carefully about how you feel when something goes wrong. Then consider the possibility that your clients feel the same way when projects don't work out as expected. Could be an interesting exercise :)

But most importantly, make sure you are delivering value on an hourly/daily basis to your clients and make sure they agree what you are delivering is of value to them.

Update: Many thanks to Lisa who spent an hour on Sunday morning help me re-structure this rant from late Saturday evening.