UWP : Spike The List
NOTE: This was written about a year before posting.
Churning
I've been putting off working on the UWP app for a while. Part of it is routine change due to summer. The larger part is that I don't really know UI Widgets in UWP well (or any platform) and have bene very uncertain of where to go to TDD this properly. Where to start, what test to write. I could get one, but is it the right one? am I doing the wrong path? Given the uncertainties, it creates a lot of questions in my head, which tends to cause me to do nothing (for side projects like this).
I'm pretty sure I've mentioned it before; and am trying to do it better. If I don't know how to effectively TDD a piece; don't TDD. Do a spike, learn. Then TDD into the direction discovered.
I've done it before; but it's hard to catch yourself in this rabbit hole solo. Again, a mob/pair is going to be much quicker at identifying when this kinda churn is happening, but also more likely that someone can identify /a/ test to get started with.
Spike
This is what I'll do. I'll do a spike, and run with it. Because the purpose of this particular exercise is more about learning the UWP Widget control, I /may/ not go back and TDD it. I'll regret it later if I don't, I know this.
Deliberate Practice
I'm treating this as deliberate practice[1] around creating a custom control using the Hotel pattern. Not around TDD or XP. Most of the HackerNews App projects have been a deliberate practice around XP practices, including extreme encapsulation.
Doing it through TDD isn't helping me, I'm not producing ANY code because I'm blocked. So we change it up and focus on a different aspect to practice.
Doing it
Creating a new XAML file is easy.
Using a grid for layout because I don't know any better, and dropping a ListView
into it. This ListView
will contain ItemRoom
s. This is where my mental block starts; not sure how this is done in UWP.
After Adding a very simple LsitView
; the MainPageView.xaml
is breaking in the Design view. It looks like it's trying to make a call through Refit
.
I really hate the suggested if (this.DesignMode) return;
fix for the exception. It's going to clutter the code in a very ugly fashion. Also make expression bodies all but impossible. ... which doesn't work. OK, never mind. Seems that's EITHER a Windows Forms or a WPF feature... not UWP.
There is, unfortunately, this if (DesignMode.DesignModeEnabled) return;
. Same effect... I think.
OK, that seems to have fixed the design exception.
#######ListView
Working on the ListView, I'm just hacking in some stuff. Which is pretty simple since the ItemView
has a hard coded Id.
I'm trying an async loading of the content for these. Using a quick hack to see if I can load them in order; MyItemList.Items?.Insert(iCopy, new ItemRoom(iCopy));
.
Which, at least superficially; seems to be working.
The ItemRoom
had a hard coded value for the ItemId. This worked well for it then, and during the development of the ListView
. As things progressed, I slowly worked it out so that it now loads different Id's in the list.
The code for the ItemListRoom
pretty damn simple; and this is with out any clean up.
using HackerNewsUwp.Network;
using HackerNewsUwp.UserControls.ItemHotel;
using System;
using Windows.ApplicationModel;
using Windows.UI.Core;
namespace HackerNewsUwp.UserControls.ItemListHotel
{
public sealed partial class ItemListRoom
{
public ItemListRoom()
{
InitializeComponent();
}
private async void Grid_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (DesignMode.DesignModeEnabled) return;
HackerNewsAccess hackerNewsAccess = new HackerNewsAccess();
Items items = (await hackerNewsAccess.TopStories()).Body();
for (int i = 0; i < items.Count(); i++)
{
int index = i;
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ItemRoom itemRoom = new ItemRoom();
items.ProvideId(index, itemRoom);
MyItemList.Items?.Insert(index, itemRoom);
});
}
}
}
}
The primary things I'm thinking about here:
- Needs the Hotel Pattern in place to remove the logic from the View.
- The
ItemRoom
control got a slight update during this to support having anItemId
set, not in the constructor. Instead of having theItemRoom
implementItems.IItemIdContainer
I'd rather haveItemElevator.IRoom
implement it. - Following the idea of NOTHING in the Room. The
ItemId
variable needs to be pushed down into theItemConcierge
. This should resolve the null checks when using aNullObject
. This "check/decision" should be made in the Concierge. The metaphor for this is that the user is providing the Concierge with what they want; theItemId
. It holds onto it and gets the details for it. Kinda like "Get me information on X play". - Changes the
ItemElevator
up a smidge as well. Won't need to pass in the_itemId
; the Concierge has it for when it's needed.
Summary
I have a pretty good idea now of how this can be TDD'd. It'll require a bit of change to other tests; but that's how it goes with emergent design.
I'll get the code cleaned up. I wanted to finish this with the spike, no reset and TDD; Code
For more info on the Deliberate Practice concept, check out Kata Nation. ↩︎