Fear and Loathing
Gonzo blogging from the Annie Leibovitz of the software development world.
-
Opening Files in the Browser with SharePoint
A change in SharePoint 2010 is how it handles browser file permissions which results in how files are opened when they’re selected. A common problem is clicking on a PDF file and seeing the browser Save dialog instead of launching the Adobe reader.
There is a setting called Browser File Handling in each Web Application (under General Settings). By default it is set to strict which causes additional HTTP headers to be injected which blocks the opening of PDF and other files. Switching the option to permissive will allow things to function like they did in SharePoint 2007.
This is web application wide so you’ll need to do it for each web app in your farm. Here’s the dialog and the setting.
Hope that helps!
-
Demystifying the Windows 8 Grid Application
If you’re looking to “re-imagine” your apps on the Windows 8 platform in Metro style you can start with the Grid Application template that’s provided in the Visual Studio 2011 preview.
From Visual Studio choose New Project and select Grid Application under Windows Metro Style in the template tree:
When you start you’ll get a fully blown Windows 8 Metro application, ready to begin filling in with your own content.
How did all that stuff get there and where does it go?
First open up the Sample Data folder in the solution. In there you’ll find a file called SampleDataSource.cs. This contains some sample data to work with and is bound to the Xaml pages in the solution at runtime. You’ll of course replace this with runtime data but the sample helps you visualize your app along with understanding where stuff goes on the screen.
Or does it? The default app is all in Lorem Ipsum speak and while this is great for visualizing a fully populated application, I thought it was a little confusing to know how it fit together. To make it clearer where everything goes I’ve modified the first collection and item in the SampleDataSource.cs calls with more descriptive labels. Here’s a better picture of the GroupedCollectionPage.xaml, the “Home” page in the app:
Note the collection title appears above the group but only the description and category for the item appears for each item. So if you’re relying on users finding things by the item title (maybe it’s the title of a recipe or a newsfeed) then you might want to modify the layout for this to bind the title somewhere. You can get creative for example and overlay the title on top of the image.
And here’s the DetailPage.xaml, what you see when the users clicks on a single item in the collection:
The Collection Title is the prominent title here but it does show the Item Subtitle. Again this might not be the desired location for titles. For example the Item Title is a small item below the image and category. Personally I would swap out the Item Title and Item Subtitle here or even use the Item Title in place of the Collection Title and leave the Item Subtitle alone (removing the Item Title below the image).
There may be some Metro guidelines coming out to help understand the reasoning behind this positioning and maybe my brain isn’t thinking “Metro” just yet, but it doesn’t make a lot of sense to me the way the default locations are.
And here’s the CollectionSummaryPage.xaml, you would navigate to this if you clicked on the Back Arrow in the screen above (next to Collection Title).
Interesting to note with this is that the Collection Content isn’t used here, only the Collection Description. I would probably modify this to include the content or drop that property entirely and just use the description.
You don’t necessarily need to follow all of the structures the default Grid Application layout offers but do keep true to the Metro style and keep things consistent. I urge you strongly to watch Jensen Harris’ keynote on the 8 traits of a Metro app and realize that by using the default setup they give you, you’ll achieve a consistent look to your app that will fit harmoniously within the Windows 8 operating system.
Make more sense now?
Enjoy.
-
Realistic Blood For Your Films (or Halloween)
With Halloween approaching I thought I would share a recipe for blood (and start a new tag in my blog). I used to do a lot of special effects and animation for amateur and semi-pro films back in the 80s and 90s so thought I would see how that knowledge transfers to the 21st Century. Obviously we’re not shooting in Super-8 anymore and dealing with back winding film, optical printers, and A+B rolling but some things are timeless.
One makeup effect often taken for granted is blood. It’s easy enough to power some store bought “stage” blood but some of these stage bloods are not as realistic as the real thing and tend to look too bright-red. You want to make sure that the blood you see on the screen looks like blood. In addition to stage blood being somewhat expensive, it’s even cost prohibitive these days to get large quantities of it. 8oz of it is about $13 so you’re looking at over $200 for a gallon.
Dick Smith is the undisputed master of makeup and was responsible for making Linda Blair a demon in The Exorcist, John Hurt a caveman in Altered States, and getting pretty bloody with Robert DeNiro in Taxi Driver. Fortunately, Smith was also very open about how he did things and shared a lot of his professional experience over the years.
Here are Dick Smith’s tried-and-proven formulas for creating movie blood guaranteed to set your audiences squirming…
Karo Syrup Zinc Oxide Red Food Color Yellow Food Color Kodak Photo-Flo *see note below Water 1 Pint 1/2 Tsps. 1 ounce 2 1/2 Tsps. 1 ounce 1 ounce 1 Quart 1 Tsps. 2 ounces 5 Tsps. 2 ounces 2 ounces 2 Quarts 2 Tsps. 4 ounces 1 ounce + 4 Tsps. 4 ounces 4 ounces 3 Quarts 3 Tsps. 6 ounces 2 1/2 ounces 6 ounces 6 ounces 1 Gallon 4 Tsps. 8 ounces 3 ounces + 2 Tsps. 8 ounces 8 ounces * If the blood will not be used in the mouth, the Photo-Flo can be added. Photo-Flo is a wetting agent which makes the blood flow over surfaces without beading and helps it to soak into fabrics naturally, like real blood. If you use the Photo-Flo, shake your mixture more gentle than otherwise to avoid excess foaming (caused by the Photo-Flo). Remember, do not use blood with Photo-Flo in the mouth!
Mixing Procedure
Put the zinc oxide in a bowl, add an equal amount of water and stir into a paste. Add the measured amounts of red and yellow food colours and stir. Add a little of the Karo syrup and stir until it is well-mixed.
Pour this mixture into a container which is larger than the final amount of blood; that is, if you are making three quarts of blood, your container should be a gallon jug. This is necessary because once the formula is mixed, it must be shaken prior to each use in order to re-mix the contents, which tend to separate while sitting.
Add the remaining amount of Karo to your container and shake well. Finally, add the amount of water specified, mix thoroughly and you will have blood with normal consistency (thickness).
Ingredients and Tips
Food Colors: Most red food colors are similar, but the yellow ones vary. Some stain white paper lemon-yellow while others are more orange. Do some tests on your targets to see the effect and reduce the amount of yellow you add if it stains too much.
Karo Syrup: This is just a brand name for corn syrup. It’s preferable to get the clear stuff but if that’s not available reduce the amount of yellow food colouring by half for regular corn syrup.
Zinc Oxide: The type referred to in the formula is the pure powder form and can be obtained from a drug store or chemical supply house. In the small amounts used it’s harmless and adds opacity to the blood. It’s the same zinc oxide that’s added to breakfast cereals.
Darker Blood: The formula above matches true blood but might look too bright when used on white materials. Use about 35 drops of green food colouring per quart of Karo blood to darken it.
Thinner Blood: If you want a thinner blood (to pump through tubes) use the same formula (with the Photo-Flo) but dilute the Karo Syrup with an equal amount of water. For instance if the formula calls for one pint of Karo, add one pint of water to it before mixing in the rest of the ingredients.
Keep your blood mixture refrigerated when not in use. If you don’t it will form mould and have to be thrown out. A covered, plastic container is good enough. If refrigerated it’ll keep for long periods of time (months, not years). The regular formula washes off skin and clothes without any trouble but I wouldn’t be dressing in my Sunday best. Get your actors or victims to wear old clothes.
Enjoy!
-
jQuery, SharePoint Picture Libraries, and automatic thumbnails
Picture Libraries are an interesting beast in SharePoint. When you upload a picture to a picture library you get not one but three images! Whenever you upload an image to a Picture Library, SharePoint automatically creates two additional images for you. A small thumbnail it uses in views and a preview image it uses when you’re viewing the picture properties. Leveraging this “feature” we can build a pretty cool client side gallery using the auto-generated thumbnails and some JavaScript to produce this:
No C# or custom web parts needed. Just a few simple things to get this effect:
- jQuery installed on your site (there are many ways to do this like Jan Tielens solution here)
- A document library to store the JavaScript we’re going to write
- The jQuery Thumbnail Hover Popup for Greasemonkey plugin
- A SharePoint Picture Library
Creating the Picture Library
Create a picture library. I named mine “Pictures” but your can be whatever you want it to be. You must create a Picture Library as we’re going to be using the automatic thumbnails and only the Picture Library template will do this for us. In the demo I just grabbed half a dozen images from the Windows 7 wallpapers on my hard drive (C:\Windows\Web\Wallpaper) and uploaded them.
The Script
Here’s the complete source code to the JavaScript we’ll use. Copy this and upload it to a document library. I use the Site Assets library that’s available on any SharePoint site where the Wiki Home Page feature is activated (like the Team Site template) or you can just use or create any document library for it.
Let’s break down some of the parts to this.
First we have a div tag with an id of “pictureMicroGallery”. This is where we’ll load our pictures into. The text “Loading…” is just a placeholder. You can put an animated image here or whatever you want while the images are fetched.
Next we start our own script.
The first line is this:
Here we’re using a built-in function in SharePoint to tell it to call our own method, “loadSharePointPictures” after the sp.js file is loaded. This is similar to the typical jQuery document load technique you might see:
However instead of waiting for the document to load, we’re waiting for a script to load. If you try to execute client side functions before sp.js is loaded, you’ll run into problems. So we let the system launch our function when it’s ready. Next is the loadSharePointPictures function. This is our call to the client side object model to load content dynamically.
All the lines are commented so pretty self explanatory. In a nutshell we’re loading a list called “Pictures” (line 7), creating a query to fetch all the items (line 9), selecting two columns (line 13), and then executing the call asynchronously (line 15-17).
When this call is successful, we call this function:
Here we get the enumerator of items from our “pictures” variable (this was set in the previous function). Then we iterate through each item, fetching it and the data from each item in the list in lines 6-10.
The FileLeafRef column contains the name of the image file we uploaded (test.jpg, etc.). The FileDirRef column contains the folder where the list lives.
Line 11 is where the magic happens. Remember when you upload a file to a picture library I said there are three images. Your original + two additional images that are automatically generated for you.
Here’s the picture library in SharePoint Manager:
See how we have img13.jpg (which is the original image file uploaded) but then in a “_w” folder there’s “img13_jpg.jpg” and the same in a folder called “_t”.
The _t folder contains the small thumbnail image (set to 160 pixels wide). It’s the image used when you view the “All Pictures” view of your picture library. The other _w folder is the large thumbnail (set to 640 pixels wide). This is the image used when you view the picture properties.
The great thing about these images (apart from being created for you) is that they’re set to a known size (width wise) and the do some magic in setting the height, getting it as close to the original aspect ratio as possible. That means you can use these images yourself to produce a gallery (like we’re doing here) and get pretty consistent results even if your images are totally off.
In any case, in this example I deliberately uploaded very large images (1920 x 1200) to show you that you can use these thumbnails as your gallery rather than trying to resize the originals.
Back to the code. In lines 7-11 we take the filename and directory and build our path to the small thumbnail version of the image (in our _t folder). So img13.jpg becomes:
The thumbnail itself is a .jpg file (regardless of what the original source file is) so the original extension is embedded in the filename if you need it. Just decompose the thumbnail file, drop the extension, and replace the “_” with a “.” to find your original file. Lines 8 and 9 turn our original filename into our thumbnail version.
Lines 15-19 we take the array of pictures we previously built (which is a url to the thumbnail image) and build up a HTML string of IMG tags with the SRC pointing at where the thumbnail is.
Finally in line 21 we use jQuery to replace the contents of our DIV tag with our new HTML containing the IMG tags.
Note: You can separate these out and just have an include file with the .js content and manually add to the <div> tag to your page. Your choice. I just find that SharePoint often mangles the HTML I write and litters it with it’s own styles and tags so I just include the whole thing from a library. That way none of my markup is messed with. Take that Microsoft!
Adding It To The Page
With our script ready, add a Content Editor web part to the page and point the content at our JavaScript include file we uploaded to the Site Assets library (or wherever you have the script living).
Save the page and it should look something like this (I just replaced the Documents web part on the Team Site home page with my Content Editor):
Thumbnail Hover Popup Plugin
I mentioned this up front but why? As it happens, I stumbled over this jQuery plugin awhile ago and filed it away for something to look at some day. Turns out it’s a cool plugin which mirrors how the automatic thumbnails in SharePoint are generated. The author created it for Greasemonkey so he could preview larger images on sites without having to click through to a page. It works where the thumbnails and larger images have similar filenames (except for a change or two).
This works great for SharePoint because the _t or _w directories hold different size images and the filenames are built from the filename of the original image. Once you know one, you can get the image for the other.
We’ll do this on our gallery (which uses the thumbnails) so when we hover over the image, we present the preview image.
First upload the plugin (a single file, jquery.thumbhover.js) to your Site Assets library (or wherever your other script is).
Next add the following line to your own script to include the thumbhover one:
We’ll add a call to the plugin for all our images. Remember the IMG tag we added a class named “pictureGallery” to it (line 16 above). Now we can use a jQuery selector to attach the plugin to every IMG file. We need to do this after we rewrite the HTML for the IMG tags so find this line:
And add this after it:
This attaches the thumbPopup to every IMG tag with the class “pictureGallery”. The imgSmallFlag is the folder for the small images and the imgLargeFlag is for the medium thumbnails (the _w folder).
When it comes time to render the popup, the plugin will grab the SRC of the image then change the imgSmallFlag to the imgLargeFlag (so changing the IMG from the 160 pixel wide thumbnail to the 640 pixel one).
Now when you hover your mouse over the image thumbnails on your page, the plugin will fetch the large thumbnail and use it in the popup. The final effect will look something like this:
Enhance it!
That’s it. Now you have a simple script you can drop onto a page and get a cool looking gallery display out of it, leveraging the automatic thumbnails SharePoint generates for you. Of course, that’s only the beginning.
Some ideas on enhancements you can do to the script:
- Add in the ability to fade the popups in when you hover over the thumbnail. This will create a nicer effect for the user
- Display additional text on the popup in an overlay (use the description field from SharePoint for the text!)
- Change how you display the thumbnail, perhaps link each thumbnail to the original image or SharePoint list item (you’ll need to add more fields to your JavaScript if you want to read them in the success function)
- Add paging to the list of images so you display them in batches of 20 or 30
- Do some better formatting on the gallery and jazz it up a bit
Have fun and don’t be afraid to experiment!
Last Note: We don’t really *need* jQuery for this to work (except for the hover effect). Most of the code is just plain old JavaScript. The line to replace the contents of the DIV tag can be done with regular JavaScript so if you don’t want the hover effect (which is a jQuery plugin so requires it) and don’t want to use jQuery… then don’t. You’ll still have a simple and free client side photo gallery from the automatic thumbnails SharePoint gives you.
Enjoy.
Live SharePoint Data Out Of The Box
I posed a question this morning about how many people knew about the live data feature in SharePoint views.
The what?
Yeah, you can configure a view to do live updates. It's a new feature in SharePoint 2010 but one that removes the need for additonal JavaScript code to do it for you. Here's how to do it.
- Pick a view you want to see live data for (or create a new view) and navigate to that page.
- Click on Site Actions in the menu and choose Edit Page
- Select the List View Web Part on the page and open up the Edit Web Part menu to bring up the properties for the web part
There's a new section you'll see in the Web Part Properties pane:
Click on Enable Asynchronous Refresh and set the interval to refresh the data (default is 60 seconds). Click OK and sit back and watch your list view update automagically!
Simple and sweet. Jan Tielens has a larger writeup on this little known feature here. It's sad that people don't know this feature is there and free, as in beer. Now, go forth and refresh your listviews without postbacks!
MediaWiki, WP7 and JSON Together Again For The First Time
In another project that I’m working on (like I don’t have enough of them) I stumbled across the MediaWiki API. MediaWiki is the workhorse wiki software that powers such sites as Wikipedia. What you may (or may not) know is that the MediaWiki API is pretty slick, offering access to any content on a MediaWiki powered site.
In this post we’ll walk through building a Windows Phone 7 app to browse the content on Wikipedia using JSON and the MediaWiki API.
First create a new Windows Phone 7 app. Any app template will do but I find the Windows Phone Databound Application template to be useful as it creates a few useful things out of the box for you like a ViewModel class and adds it to the app. It’s nothing special that you can’t do yourself, but does save a little time.
Next you’ll want to be able to read the data coming from Wikipedia. We’ll be using the MediaWiki API (no download required) which can serve data up in XML format but we’ll opt for using JSON. Rather than using the native JSON methods in .NET let’s use the Json.NET library, a wicked cool library by James Newton-King that makes serializing and deserializing JSON into .NET objects a breeze.
Unfortunately at the time of this writing, the Nuget package for Json.NET doesn’t install properly on Windows Phone 7 projects so you have to download the file, unzip it, and add the references manually. Hopefully someone updates the Nuget package so this 5 minute task can be avoided in the future.
The default app has a listbox with items to display that links to a detail page. For this sample, we’ll fill the list with categories and drill into the category to display the pages associated with it. The first task is to retrieve the categories from Wikipedia. The full documentation for the API is online here. To get the categories it’s a straight forward API call that looks like this:
http://en.wikipedia.org/w/api.php?action=query&list=allcategories&format=json
The first part is where the API page is located on Wikipedia (it may not be in this location on other WikiMedia sites so check with the site owner). Then we specify an action, in this case a query. We ask for a list of items specifying “allcategories” and we want it in JSON format.
Here’s the output:
{
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> query: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> allcategories: [</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "!"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "!!! EPs"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "!!! albumns"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "!!! albums"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "!!! songs"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "!!AFRICA!!"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "!910s science fiction novels"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "!928 births"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "!936 births"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "!946 poems"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> query-continue: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> allcategories: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> acfrom: "!949 births"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
MediaWiki queries come back in two groups. First is the query results and then a section titled “query-continue” that contains the next value that you can use to start from on a subsequent query. You may need to do several queries if you want to get everything. MediaWiki supports up to 500 items per call but very often (especially with the size of Wikipedia) that number can be in the thousands. It’s up to you how to do the queries (all at once or as you go) but think of it as picking up where you left off. The default size is 10 which is fine for now.
However the results are not very pretty and somewhat bizarre list of categories. First we’ll add some more data to the category with information about it. This is done by adding more parameters to the API call:
http://en.wikipedia.org/w/api.php?action=query&list=allcategories&format=json&acprop=size&acprefix=A
All we’ve done is add “&acprop=size&acprefix=A” to the call. This brings in number pages, files, sub categories into the mix (the size property is the sum of all those). We’ll also get categories that start with the letter “A” to avoid the categories named “!”. Here’s the results:
{
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> query: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> allcategories: [</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "A"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "A&E Network shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "A&E Shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "A&E Television Network shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "A&E Television Networks"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> size: 17</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pages: 14</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> subcats: 3</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "A&E Television network shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "A&E network shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "A&E shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> size: 66</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> pages: 66</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> *: "A&E television network shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "A&M Records"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> query-continue: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> allcategories: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> acfrom: "A&M Records EPs"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
That looks better. Unfortunately there’s no way to include additional filters like “don’t include items with size = 0” so you’re going to have to make multiple calls and the filtering in your app (LINQ is great for this) but this is good enough to get started.
Json.NET can deserialize these results into an object graph but you need to create the classes for it. One way to do this is to use the JSON C# Class Generator which is a pretty handy tool if you’re starting with JSON as your format and don’t have anything. It doesn’t know anything about the Json.NET library so it uses native C# calls for the structure and helpers and but will give you something to start from.
We’ll just build our classes manually as there are only a few we need. Here’s the first cut (based on the JSON data above):
public class MediaWiki
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> Query Query { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> QueryContinue QueryContinue { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'><span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Query</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> Allcategory[] Allcategories { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'><span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Allcategory</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Size { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Pages { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Files { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Subcats { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'><span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> QueryContinue</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> Allcategories Allcategories { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'><span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Allcategories</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Acfrom { get; set; } </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
The class and property names here are not the most intuitive but we’ll fix that. First let’s make the call to the API to get our JSON then deserialize it into this object graph. Replace the LoadData method in the MainViewModel with this code:
public void LoadData()
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var address = <span style="color: rgb(0, 96, 128);">@"http://en.wikipedia.org/w/api.php?action=query&list=allcategories&format=json&acprop=size&acprefix=A&aclimit=500"</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> var webclient = <span style="color: rgb(0, 0, 255);">new</span> WebClient();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> webclient.DownloadStringCompleted += OnDownloadStringCompleted;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> webclient.DownloadStringAsync(<span style="color: rgb(0, 0, 255);">new</span> Uri(address));</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
This kicks off the download and sets up the callback to invoke when the download is complete (I also added “&aclimit=500” to the end of the query to get more than the default 10 results). When the string is downloaded we call this:
foreach (var category in json.Query.Allcategories.Where(category => category.Size > 0))
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> Items.Add(</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">new</span> ItemViewModel</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> LineOne = <span style="color: rgb(0, 96, 128);">"Pages: "</span> + category.Pages,</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> LineTwo = <span style="color: rgb(0, 96, 128);">"Subcats: "</span> + category.Size,</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> });</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
This takes the result of the download and calls the JsonConvert method DeserializeObject. This is a generic method that we pass our MediaWiki class from above to. Then just loop over the Allcategories array to pluck out each category and create our ItemViewModel items manually. We use LINQ in the loop to filter out any categories with a size of 0.
Here's the result on our phone:
This isn’t too exciting because frankly we don’t know what the name of each category is. Remember the JSON?
{
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> *: "A&E Television Network shows"</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> size: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> pages: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> files: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> subcats: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Hmmm.. how are we going to get that name into a property in our class? The Json.NET library matches up names of attributes in the markup with the name of a property in your class. We can’t create a property called “*” as that’s not valid in C#.
The answer is to use the JsonPropertyAttribute on our class and introduce a new property called Title. Here’s our updated Allcategory class with the markup:
public class Allcategory
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> [JsonProperty(<span style="color: rgb(0, 96, 128);">"*"</span>)]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Title { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Size { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Pages { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Files { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Subcats { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
This tells Json.NET that when it comes across a value with the markup “*” to deserialize it into the Title property.
Now we can update our LoadData method to use the title instead:
private void OnDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var json = JsonConvert.DeserializeObject<MediaWiki>(e.Result);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">foreach</span> (var category <span style="color: rgb(0, 0, 255);">in</span> json.Query.Allcategories.Where(category => category.Size > 0))</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> Items.Add(</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">new</span> ItemViewModel</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> LineOne = category.Title,</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> LineTwo = <span style="color: rgb(0, 0, 255);">string</span>.Format(<span style="color: rgb(0, 96, 128);">"Pages: {0} Subcats: {1}"</span>, </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> category.Pages, category.Subcats),</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> });</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> IsDataLoaded = <span style="color: rgb(0, 0, 255);">true</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Which now looks like this on the phone:
That’s a little better.
With the [JsonProperty] attribute we can also specify the name of the property in the JSON markup so that we’re not tied to that name when specifying the name in our C# class. This allows us to make our C# class a little more readable. Here’s an couple of examples:
public class Query
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> [JsonProperty(<span style="color: rgb(0, 96, 128);">"allcategories"</span>)]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> Allcategory[] Categories { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'><span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Allcategory</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> [JsonProperty(<span style="color: rgb(0, 96, 128);">"*"</span>)]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Title { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Size { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Pages { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Files { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> [JsonProperty(<span style="color: rgb(0, 96, 128);">"subcats"</span>)]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> Categories { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
The JsonProperty will match whatever markup MediaWiki (or whomever is providing your JSON feed) and we can use a more friendlier name in our code (P.S. the class names can be whatever you want, it’s the properties that are important).
The default app already has the function to display the DetailsPage when you tap on an item in the list. It passes the index of the array of ItemViewModel items to the page which retrieves it from the Items property of the ViewModel stored in the App class and sets up the title of the DetailsPage to the LineOne property of the ViewModel.
This property is really the title of the category and the value we can use to get more information from the MediaWiki API. We’ll use the “categorymembers” action to get all pages in a given category. Here’s the url we’re going to use:
http://en.wikipedia.org/w/api.php?action=query&list=categorymembers&format=json&cmtitle=Category:Zombies&cmprop=type|ids|title
We’re going after all pages in the “Zombies” category and want to include the type of page, the id, and the title.
Here’s the JSON from this call:
{
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> query: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> categorymembers: [</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 8375</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Draugr"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 27279250</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Felicia Felix-Mentor"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 143895</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Jiang Shi"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 1776116</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Clairvius Narcisse"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 781891</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Philosophical zombie"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 7568400</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Zombie Squad"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 5048737</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Zombie walk"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 22328159</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Zombeatles"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 34509</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 0</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Zombie"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"page"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> pageid: 6569013</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ns: 14</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> title: <span style="color: rgb(0, 96, 128);">"Category:Hoodoo"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> type: <span style="color: rgb(0, 96, 128);">"subcat"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> ]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> query-<span style="color: rgb(0, 0, 255);">continue</span>: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> categorymembers: {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> cmcontinue: <span style="color: rgb(0, 96, 128);">"subcat|31450932|ZOMBIES AND REVENANTS IN POPULAR CULTURE"</span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Note that we have a new attribute called “categorymembers” instead of “allcatgories” (but the top level attribute is still “query”). We’ll definitely need a class to handle the categorymembers array returned by the call but should it go into the existing Query class?
Technically you could do it. Json will deserialize it for you and if it can match up the attribute with the C# property name (or find the property decorated with the JsonProperty attribute) it will and the other items will just be null.
It’s up to you if you want to build a special query class for each type of query. I suggest you do (you can even create a generic MediaWikiQuery<T> class that takes in things like a CategoryMember class or AllCategory class) to keep things clean. Otherwise you’re violating a few SOLID principles and laying the foundation for a God class.
For demo purposes I’ll just add the CategoryMember class to our Query class but like I said, it’s a demo only. Here’s the new CategoryMember class and the modified Query class:
public class CategoryMember
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> PageId { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Title { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Type { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'><span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">class</span> Query</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> [JsonProperty(<span style="color: rgb(0, 96, 128);">"allcategories"</span>)]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> Allcategory[] Categories { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> [JsonProperty(<span style="color: rgb(0, 96, 128);">"categorymembers"</span>)]</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> CategoryMember[] Pages { get; set; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Now when the DetailsPage loads we’ll figure out the index of the item based on the parameter passed in and call a new method on our MainViewModel to load the pages.
protected override void OnNavigatedTo(NavigationEventArgs e)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">string</span> selectedIndex = <span style="color: rgb(0, 96, 128);">""</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">if</span> (NavigationContext.QueryString.TryGetValue(<span style="color: rgb(0, 96, 128);">"selectedItem"</span>, <span style="color: rgb(0, 0, 255);">out</span> selectedIndex))</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">int</span> index = <span style="color: rgb(0, 0, 255);">int</span>.Parse(selectedIndex);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> App.ViewModel.LoadPages(index);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> DataContext = App.ViewModel.SelectedCategory;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
Note that the LoadPages method is another call to the service so we’ll probably want to create a handler in our view to handle displaying a “Loading” indicator and respond to something like a property changed event on our ViewModel to remove it. Here we’ll just the binding in the ViewModel and the page will update (eventually) with the list of pages. It’s not the UX you want to build but this post is already getting long and I’m sure you’re pretty tired reading it.
We also set the DataContext of our DetailsPage to a new property we added in the ViewModel call SelectedCategory. This way MainViewModel hangs onto whatever category the user selected so we can reference it (and it’s properties) later.
To load the pages first we’ll call out to Wikipedia to fetch them based on our category. We could pass in the title we want but here we’ll pass the index to the title and fetch it in the method:
public void LoadPages(int index)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> SelectedCategory = Items[index];</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> var address =</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">string</span>.Format(</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 96, 128);">"http://en.wikipedia.org/w/api.php?action=query&list=categorymembers&format=json&cmtitle=Category:{0}&cmprop=type|ids|title"</span>,</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> SelectedCategory.LineOne);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> var webclient = <span style="color: rgb(0, 0, 255);">new</span> WebClient();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> webclient.DownloadStringCompleted += OnDownloadPagesCompleted;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> webclient.DownloadStringAsync(<span style="color: rgb(0, 0, 255);">new</span> Uri(address));</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
This sets up SelectedCategory based on the index we passed in and crafts the url to the MediaWiki API to fetch all pages for whatever the category is (based on the title).
Now we need to process the JSON when the download of the page list completes. For this we’re going to need a new ViewModel. Here’s a quick and dirty one that just uses the title and pageid property:
public class PageViewModel : INotifyPropertyChanged
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">int</span> _pageid;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">string</span> _title;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> PageId</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> get { <span style="color: rgb(0, 0, 255);">return</span> _pageid; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> set</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">if</span> (<span style="color: rgb(0, 0, 255);">value</span> == _pageid) <span style="color: rgb(0, 0, 255);">return</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> _pageid = <span style="color: rgb(0, 0, 255);">value</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> NotifyPropertyChanged(<span style="color: rgb(0, 96, 128);">"PageId"</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Title</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> get { <span style="color: rgb(0, 0, 255);">return</span> _title; }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> set</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">if</span> (<span style="color: rgb(0, 0, 255);">value</span> == _title) <span style="color: rgb(0, 0, 255);">return</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> _title = <span style="color: rgb(0, 0, 255);">value</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> NotifyPropertyChanged(<span style="color: rgb(0, 96, 128);">"Title"</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">event</span> PropertyChangedEventHandler PropertyChanged;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> NotifyPropertyChanged(String propertyName)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var handler = PropertyChanged;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">if</span> (<span style="color: rgb(0, 0, 255);">null</span> != handler)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> handler(<span style="color: rgb(0, 0, 255);">this</span>, <span style="color: rgb(0, 0, 255);">new</span> PropertyChangedEventArgs(propertyName));</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
We’ll display the Title to the user but the pageid needs to be stored because later we’ll want to retrieve all the details about a single page.
In the ItemViewModel (our category) we have an ObservableCollection of PageViewModel objects called Pages. This mimics the Items property in the MainViewModel. Here’s the declaration:
public ObservableCollection<PageViewModel> Pages { get; private set; }
And here’s the constructor creating them:
public ItemViewModel()
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> Pages = <span style="color: rgb(0, 0, 255);">new</span> ObservableCollection<PageViewModel>();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
Back in the MainViewModel we deserialize the JSON and add the PageViewModel objects to our selected category:
private void OnDownloadPagesCompleted(object sender, DownloadStringCompletedEventArgs e)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var json = JsonConvert.DeserializeObject<MediaWiki>(e.Result);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">foreach</span> (var page <span style="color: rgb(0, 0, 255);">in</span> json.Query.Pages.Where(page => page.Type.Equals(<span style="color: rgb(0, 96, 128);">"page"</span>)))</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> SelectedCategory.Pages.Add(</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">new</span> PageViewModel</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> PageId = page.PageId, </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> Title = page.Title</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> });</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Just like when we loaded the categories here we only select items where the page.Type is a “page”. In our Zombie example, one of the items is a “subcat”. In a real app, we would have something to handle constructing that and creating some kind of link to another category (since everything is a category, we could reuse a lot of this code for that).
The last part is changing the DetailsPage.xaml to display a list of pages. Here’s the LayoutRoot grid updated:
<Grid x:Name="LayoutRoot" Background="Transparent" d:DataContext="{Binding SelectedCategory}">
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">Grid.RowDefinitions</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">RowDefinition</span> <span style="color: rgb(255, 0, 0);">Height</span><span style="color: rgb(0, 0, 255);">="Auto"</span><span style="color: rgb(0, 0, 255);">/></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">RowDefinition</span> <span style="color: rgb(255, 0, 0);">Height</span><span style="color: rgb(0, 0, 255);">="*"</span><span style="color: rgb(0, 0, 255);">/></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">Grid.RowDefinitions</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 128, 0);"><!--TitlePanel contains the name of the application and page title--></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">StackPanel</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">="TitlePanel"</span> <span style="color: rgb(255, 0, 0);">Grid</span>.<span style="color: rgb(255, 0, 0);">Row</span><span style="color: rgb(0, 0, 255);">="0"</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">="12,17,0,28"</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">TextBlock</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">="PageTitle"</span> <span style="color: rgb(255, 0, 0);">Text</span><span style="color: rgb(0, 0, 255);">="MY APPLICATION"</span> <span style="color: rgb(255, 0, 0);">Style</span><span style="color: rgb(0, 0, 255);">="{StaticResource PhoneTextNormalStyle}"</span><span style="color: rgb(0, 0, 255);">/></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">TextBlock</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">="ListTitle"</span> <span style="color: rgb(255, 0, 0);">Text</span><span style="color: rgb(0, 0, 255);">="{Binding LineOne}"</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">="9,-7,0,0"</span> <span style="color: rgb(255, 0, 0);">Style</span><span style="color: rgb(0, 0, 255);">="{StaticResource PhoneTextTitle1Style}"</span><span style="color: rgb(0, 0, 255);">/></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">StackPanel</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 128, 0);"><!--ContentPanel contains details text. Place additional content here--></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">Grid</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">="ContentPanel"</span> <span style="color: rgb(255, 0, 0);">Grid</span>.<span style="color: rgb(255, 0, 0);">Row</span><span style="color: rgb(0, 0, 255);">="1"</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">="12,0,12,0"</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">TextBlock</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">="ContentText"</span> <span style="color: rgb(255, 0, 0);">Text</span><span style="color: rgb(0, 0, 255);">="{Binding LineThree}"</span> <span style="color: rgb(255, 0, 0);">TextWrapping</span><span style="color: rgb(0, 0, 255);">="Wrap"</span> <span style="color: rgb(255, 0, 0);">Style</span><span style="color: rgb(0, 0, 255);">="{StaticResource PhoneTextNormalStyle}"</span><span style="color: rgb(0, 0, 255);">/></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">ListBox</span> <span style="color: rgb(255, 0, 0);">x:Name</span><span style="color: rgb(0, 0, 255);">="MainListBox"</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">="0,0,-12,0"</span> <span style="color: rgb(255, 0, 0);">ItemsSource</span><span style="color: rgb(0, 0, 255);">="{Binding Pages}"</span> <span style="color: rgb(255, 0, 0);">SelectionChanged</span><span style="color: rgb(0, 0, 255);">="MainListBox_SelectionChanged"</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">ListBox.ItemTemplate</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">DataTemplate</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">StackPanel</span> <span style="color: rgb(255, 0, 0);">Margin</span><span style="color: rgb(0, 0, 255);">="0,0,0,17"</span> <span style="color: rgb(255, 0, 0);">Width</span><span style="color: rgb(0, 0, 255);">="432"</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"><</span><span style="color: rgb(128, 0, 0);">TextBlock</span> <span style="color: rgb(255, 0, 0);">Text</span><span style="color: rgb(0, 0, 255);">="{Binding Title}"</span> <span style="color: rgb(255, 0, 0);">TextWrapping</span><span style="color: rgb(0, 0, 255);">="Wrap"</span> <span style="color: rgb(255, 0, 0);">Style</span><span style="color: rgb(0, 0, 255);">="{StaticResource PhoneTextExtraLargeStyle}"</span><span style="color: rgb(0, 0, 255);">/></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">StackPanel</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">DataTemplate</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">ListBox.ItemTemplate</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">ListBox</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">Grid</span><span style="color: rgb(0, 0, 255);">></span></pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'><span style="color: rgb(0, 0, 255);"></</span><span style="color: rgb(128, 0, 0);">Grid</span><span style="color: rgb(0, 0, 255);">></span></pre>
The result is a details page that shows our category as the title and a listbox full of pages.
At this point you can add an additional handler to the listbox to drill down into the page itself. From there you can pluck out a list of images, links to other pages, and even the wiki content and sections.
Check the MediaWiki API for more info on getting down into all of this stuff. It’s very cool being able to poke into MediaWiki and beats the hell out of screen scraping!
A Call to Action!
This just gives you an intro to accessing a resource like Wikipedia using the public API and deserializing results via JSON into a set of classes you can use to bind to a Windows Phone 7 app. I’m going to leave the rest up to you. A few ideas to think about if you were to build on this example:
- Calling directly from the ViewModel isn’t a production practice, it was a demo only. You’ll probably want to create a MediaWikiService and inject it into your ViewModel
- The call can take a few seconds so you’lll need to handle this in your service and update the UI accordingly
- Drill down into a single page, pluck out the images and create a visual MediaWilki browser experience or something.
- You can even post content *to* a MediaWiki wiki (after you login with a username/pass that has rights to) so not only can it be a browser experience but it can be an editing experience too.
- This is just one example to use the MediaWiki API by fetching categories and page content but there are a lot of other type of queries you can make like getting a list of recent changes, comment history, or even random pages.
Use your imagination and above all, have fun. If you get stuck feel free to leave questions in the comments section and I’ll do my best to answer them.
Custom SharePoint Ribbons and Current List Item in JavaScript
I recently had to build a custom ribbon item displayed when a user viewed a list item. The ribbon button would whisk the user away to a view on another list, passing the current list item ID to the view to use as a query string filter. The secondary list has a field that references the first list (sort of a parent/child relationship).
The challenge (besides creating a Ribbon item which is a Hell unto itself) was to get the current list item ID and pass it to the view via a query string so the view could be filtered. The SP.ClientConext class in the ECMAScript Class Library for SharePoint lets you get all kinds of things like retrieving lists and even list items from SharePoint through JavaScript. I didn’t find any way to get the current list item (it’s easy to get all list items for the current list but there didn’t seem to be a property like SP.ListItem.get_current()). Maybe I overlooked it.
After posting a few pleas for help on Twitter (including one dude who told me to go trolling Stack Overflow because “several MVPs frequent” there, to which I replied “Yes I know. I’m one of them.”) I caved in and posted the question on Stack Overflow (well, the recently promoted SharePoint site on StackExchange).
It got a few answers but nothing that really worked for my situation. Remember, I want to a) create a custom ribbon item that links to another view on another list and b) pass along the id of the current list item to that view for filtering (via a query string). Finally I stumbled over the answer buried deep on MSDN.
So here’s the effect we’re going after. A ribbon button on the display form of a list item that links to an associated view:
The Custom Group contains a series of Buttons, each of which links to a view in another list. Each view was created with a custom filter to only show items where a field in that list matched the value passed in.
Normally you would open the list view in SharePoint designer, select the DataView and add a parameter to filter on a query string value. I’ll follow up with another blog post on how to do this programmatically without opening SharePoint Designer.
When the user clicks on one of the custom buttons, we open another dialog and display that view.
Like I said there were some good answers on the question from the SharePoint community. Brage Tukkensæter posted an answer that involved a custom action that iterated through each item selected and passed it along to a dialog. The problem with this was that you needed to have the user select at least one item in the list in tabular form (I’ve turned this function off in the view) and launching from the Display Form, the SP.ListOperation.Selection property he proposes to use is blank.
Wouter van Vugt, another SharePoint MVP (Stack Overflow is just full of them), had another option. Grab the ID value from the query string of the list form and use it. That sounds like a fairly simple idea but it requires a few lines of JavaScript to grab the query string value. There are many examples on the web that you find to do this but they all require some kind of parsing, maybe some regular expressions, and it all feels awkward.
I thought about using SPServices since we had that available but it meant that I would be relying on the JavaScript to call out to SPServices to do this and all of my buttons would have to be written to pass in something unique to a JavaScript method to do this. Again, more work than I think was necessary. I have the item up on the screen so I *know* SharePoint knows what the ID is. Why is it so freakin’ hard to get it.
Well, it isn’t but it required some lucky digging and MSDN documentation from the previous version of SharePoint to surface it.
Buried on this page in MSDN is a HOWTO on adding actions to the user interface. On that page there’s a list of URL Tokens.
- ~site - Web site (SPWeb) relative link.
- ~sitecollection - site collection (SPSite) relative link.
- In addition, you can use the following tokens within a URL:
- {ItemId} - Integer ID that represents the item within a list.
- {ItemUrl} - URL of the item being acted upon. Only work for documents in libraries. [Not functional in Beta 2]
- {ListId} - GUID that represents the list.
- {SiteUrl} - URL of the Web site (SPWeb).
- {RecurrenceId} - Recurrence index. This token is not supported for use in the context menus of list items.
These are normally associated with a UrlAction element but there’s nothing to prevent you from using them in your CustomAction (in my case the CommandAction attribute of a CommandUIHandler since I was building a custom ribbon button).
So I crafted my CommandAction like this:
<CommandUIHandler CommandAction="javascript:viewDialog({ItemId});"/>
I simply call my custom JavaScript method and pass it the {ItemId} token. Inside my JavaScript (which I added to my solution as another CustomAction in a mapped folder to Layouts) I have this snippet:
function viewDialog(id) {
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">var</span> options = { </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> url: <span style="color: rgb(0, 96, 128);">"/sitename/Lists/ListName/ViewName.aspx?ID="</span> + id, </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> width: 800, </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> height: 600, </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }; </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> SP.UI.ModalDialog.showModalDialog(options);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
This takes in an id as a parameter (the {ItemId} token) and builds up the options to pass to the showModalDialog method on the SP.UI object.
The result is a custom button that links to another view passing it the id for the current item.
What’s sad is that it was a bugger trying to find that piece of knowledge. A few people have written short blogs on it but it’s not very discoverable. The 2010 version of the MSDN documentation doesn’t include the URL Token section and frankly its misplaced IMHO. It seems you can use these tokens on any custom action whether it’s a UrlAction or CommandAction. I’m sure there are other uses for it and it’ll be handy in the future.
Hopefully I didn’t kill any kitten or unicorns (or worse, zombies) in answering my own question on Stack Overflow.
If the universe explodes later you’ll know why and you can freely blame me. In the meantime, enjoy this snippet of knowledge.
SharePoint Server 2010 Windows Phone 7 Mobile Edition
It’s wonderful to end the week with two of my favorite passions, SharePoint and Windows Phone 7. The last few months I’ve been working on a special project that you can install on your Windows Phone 7 starting today.
SharePoint Server 2010 running on Windows Phone 7.
That’s right. Now you no longer need an IT shop to use SharePoint. You can just deploy it on your own phone and decide how to run it.
Why would anyone deploy a server to your phone? Several reasons but mostly it’s because you can totally control your entire SharePoint environment even if you’re out of the office or offline.
Scott Haack, Senior Principle Program Manager in Bellevue, Oregon said this about why this project came about:
“It’s about choices. SharePoint Server 2010 on the mobile platform allows users to have choices about who runs their IT systems and decides how the system is configured without having to go through complicated business processes.”
It’s all about competition too. Here’s what Phil Haanselman, Principle Platform Program Manager in Portland, Washington says about the platform:
“With SharePoint Server 2010 on the Windows Phone 7 this pushes the platform to the edge and beyond. There is nothing like this on the iOS or WebOS and nobody is thinking in this space. It’s going to be game changing.”
Bringing the Pieces Together
I knew it was going to be a big job but I was up for it. There were so many pieces to get co-ordinated and I knew I would have a few challenges along the way. Here’s what it took to bring it all together.
IIS Express to the Rescue
Back in June when Scott Guthrie announced IIS Express I got thinking about SharePoint and the Mobile user. Wouldn’t it be great if they could run SharePoint on their phones! I knew Windows Phone 7 was coming up as I was currently in the beta and building Silverlight apps already. I knew SharePoint would run on Window 7 so why not combine the best of both worlds.
IIS Express is a lightweight component that weighs in at under 10mb. It also does not require administrative access to run applications and has a full feature set including SSL, URL Rewrite, and other IIS 7.x modules. All packaged together that can be run from a single location and does not require any registration/configuration steps.
It seemed like IIS Express was perfect to run on WP7.
NoDo Delays
Getting IIS Express onto the phone was going to be a bit of a challenge however with some work and co-ordination with high ranking figures at Microsoft I was able to get it to compile down into a single DLL file.
To get it onto the phone I worked with top people on the Windows Phone team and packaged IIS Express into the next update, the infamous NoDo release. So as of right now, anyone with NoDo installed actually has web server available to them. There were several other updates we packaged into NoDo that were put there for SharePoint Server to run (mostly around Kerberos ticket support and background processing).
So as a result this was the primary reason why NoDo was delayed. I do apologize to the community for the NoDo delays, but now you know the reason. It was so mobile users could run SharePoint.
Getting past the NoDo release the Windows Phone 7 was now ready and enabled for serving up .aspx pages and SharePoint.
Redesigning Central Administration
The biggest challenge was having to rebuild the Central Administration site to work with the Metro look and feel. It was a lot of work but we’ve built all the screens as new using the Windows Phone 7 controls. This was done so you have good touch targets to hit. In early betas, we were just using the out of the box Central Admin web pages but it was hard to click on things and I was always zooming in and out. So I built the Metro screens to make it easier for you to work with Central Admin.
Everything is all there and hopefully organized in a good way so you won’t be totally thrown back when using the WP7 version of SharePoint. Here’s Central Admin running on WP7.
The Metro Way
With the Metro overhaul of Central Admin we wired everything back up. Using MVVM was key in being able to have the UI respond to what you did so it was easy to show real-time information about the server.
Here’s how you can create a new Web Application with Central Admin on the Windows Phone 7
First select Web Applications from Application Management pivot. You’ll see this menu
Now click on Manage web applications. We couldn’t implement the Ribbon in Windows Phone 7 so from the list select Create New Web Application. You’ll be presented with a data entry screen to enter all the information for your new web app:
All of the options are there just like the current release of SharePoint. Unfortunately due to some last minute problems and trying to get this done by the end of the week we were not able to include Claims Based Authentication so only Classic Mode is available. Anyone would be a Fool to try to deliver something in April without testing it so this was why this decision was made.
Complete Feature Set, Almost
Not exactly. As I said we don’t support Claims Based Authentication and there are few other features in SharePoint 2010 we don’t support. Here’s a list of the current restrictions. These have been left out either due to time constraints or technical limitations on the phone platform. A few of these features will be available in the Mango release of Windows Phone 7 due out sometime in 2012.
- Kerberos. Kerberos support is not fully implemented so it’s suggested to keep web sites running under NTLM (most of the system works under Kerberos but without a true integrated authentication on the Windows Phone 7 not everything works, yet)
- Content Deployment. This isn’t implemented at all in this release due to time constraints. It’s planned for a future release. Hopefully this doesn’t hinder people too much.
- InfoPath Forms Services. This was tricky but with some clever coding, we’re able to convert, on the fly InfoPath forms into Windows Phone 7 controls. However in some last minute testing we found that InfoPath forms with complex rules breaks the model. Simple forms work, complex forms will be delivered in Service Pack 1 due out later in the year.
- Define blocked file types. Unfortunately as we don’t have complete control over access to the Windows Phone 7 sub-systems (like the registry) so we’re unable to control blocked file types. No word on when this feature might be available.
Leveraging the Phone
This release doesn’t just allow you to run SharePoint Server on your phone but we’ve also leveraged specific features of the WP7 platform as well. For example you can now hook up SharePoint Picture Libraries to the Windows Phone 7 Media Library and any picture taken on the your phone can instantly be available to anyone browsing the site.
The other big integration point is Geolocation. Windows Phone 7 devices all have a built-in GPS. SharePoint Server 2010 leverages this by geotagging any content. A new feature in Document Libraries allows users to geotag documents with location information which can then be used to filter documents and lists based on locality and even plot document creation locations on a Bing Map using the built-in Bing Map Controls.
It’s really up to the SharePoint web part developer to figure out how best to leverage these new capabilities. Like it was said before, this is game changing.
Availability
Most importantly when can you get this?
Right now!
Download the .xap file here directly and you can instantly sideload it onto your Windows Phone 7 (developer unlock required). If you’re not a developer then you’ll have to wait for the app to make it through Marketplace Certification which should be any day now.
You do require the NoDo update to be able to run SharePoint Server 2010 on your phone so please make sure you have that installed first. The software will detect if IIS Express is installed or not and unfortunately fail if you don’t have the update yet.
The software will be released on CodePlex in the next while under the Apache License so anyone can contribute to it. Please contact me offline if you’re interested.
After MIX11 we’ll be making the server available as a NuGet package that can be deployed anywhere without the need to access the Windows Phone Marketplace. Stay tuned for news on this.
Many Thanks
Many thanks to all the wonderful people that made this project possible. Without their co-operation we wouldn’t be where we are today:
- Geoffrey Chaucer
- Eduard de Dene
- John Aubrey
- Sizdah Bedar
- Joseph Boskin
Using the Builder Pattern To Create SharePoint Lists
In building out a custom solution I get tired of writing procedural code to create a list, add the columns, setup the views, and add some initial data. Many moons ago I decided to follow a creational design pattern called the Builder pattern. This pattern separates the construction of a complex object from it’s representation so that the same process can create different representations. Basically, let’s build a bunch of custom lists using OO instead of repetitive procedural-like code. Here’s how.
First let’s take a look at the Builder pattern. Here it in UML form for visual dudes:
Pretty straight forward. The principle here is that all concrete objects will adhere to a contract (the Builder) and implement the specific way they need to build things while the Director handles the correct sequence of object creation.
This suites Lists pretty nicely in SharePoint. A list needs to be created via the SPWeb it lives in, it needs fields added to it, views setup, and items added to the finished list.
Okay, time for some code.
First here’s our abstract ListBuilder. All implementations of creating a new list will inherit from this. It provides abstract methods that make up all the parts of a list (fields, views, items, and the list itself).
public abstract class ListBuilder
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">protected</span> SPList TheList {get; set;}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">abstract</span> <span style="color: rgb(0, 0, 255);">void</span> CreateList(SPWeb web);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">abstract</span> <span style="color: rgb(0, 0, 255);">void</span> CreateFields();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">abstract</span> <span style="color: rgb(0, 0, 255);">void</span> CreateViews();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">abstract</span> <span style="color: rgb(0, 0, 255);">void</span> AddInitialData();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">abstract</span> <span style="color: rgb(0, 0, 255);">void</span> DeleteList(SPWeb web);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Pretty basic stuff. We have methods to create and delete the list (which requires the SPWeb object it belongs to) and there’s methods for creating fields and views and adding some initial data (for example if you have a lookup list you’re creating and you want to seed it with some initial values).
There’s also a protected property called TheList which holds an instance of the list created. This is for adding fields and views later in the construction process. P.S. Personally I hate the property name “TheList” but “List” gets all confused with System.Generic.List types and SPList is already a SharePoint type so to avoid conflicts I picked this. I’m also lazy and didn’t want to call it SharePointList or something. Feel free to make it whatever you want.
Now that we have our abstract ListBuilder let’s create the ListDirector. This class will handle building the list by orchestrating the assembly of the pieces. It also provides outside access to create and delete the lists (which we’ll call from our feature activating/deactivating).
public class ListDirector
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">readonly</span> SPWeb _web;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> ListDirector(SPWeb web)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> _web = web;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">void</span> CreateUsing(ListBuilder builder)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> builder.CreateList(_web);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> builder.CreateFields();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> builder.CreateViews();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> builder.AddInitialData();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">void</span> DeleteUsing(ListBuilder builder)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> builder.Delete(_web);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
The ListDirector is created using the SPWeb object (so it can tell the list builder where to create/delete lists from) and knows the order to put the pieces of the list together. This way ListBuilder (and any classes inheriting from it) don’t need to know how to orchestrate the pieces and can just work independently. You can also create special ListDirectors that do things like update lists (avoid the creation) or append data.
Armed with our abstract class and our director we head down the path of actually creating a specific list. For this sample we’ll do something really simple. A lookup list for countries and contains a name we’ll use for picking items and some internal codes for each item.
First we’ll start with the bare bones builder:
public class CountryListBuilder : ListBuilder
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> CreateList(SPWeb web)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> CreateFields()</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> CreateViews()</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> AddInitialData()</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">override</span> <span style="color: rgb(0, 0, 255);">void</span> DeleteList(SPWeb web)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> </pre>
Now let’s look at each method in detail. There’s not a lot here to see because our list builder should be lightweight. Of course this completely depends on your needs so you might have something with 30 fields but the ListBuilder class should simplify things and remove redundancies for you.
First we’ll create the list. To do this we need the SPWeb object which we’ll pass in.
public override void CreateList(SPWeb web)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var id = web.Lists.Add(<span style="color: rgb(0, 96, 128);">"Countries"</span>, <span style="color: rgb(0, 96, 128);">"Country lookup list"</span>, SPListTemplateType.GenericList);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> TheList = web.Lists[id];</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> TheList.OnQuickLaunch = <span style="color: rgb(0, 0, 255);">true</span>;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> TheList.Update();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Here we create a generic list, retrieve it, and update the metadata (in this case if it’s on the Quick Launch or not, but you could do things like setting the Title or other values. The call to Update() is only needed to update the metadata. If all you’re doing is adding the list then the first line is all you need.
Next we’ll add our additional fields:
public override void CreateFields()
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> TheList.Fields.Add(<span style="color: rgb(0, 96, 128);">"Code"</span>, SPFieldType.Text, <span style="color: rgb(0, 0, 255);">true</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> TheList.Fields.Add(<span style="color: rgb(0, 96, 128);">"Currency"</span>, SPFieldType.Text, <span style="color: rgb(0, 0, 255);">true</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Since we’re creating this list from a generic (custom) list we already have a title field. Here we add two new fields.
Next up is the views.
public override void CreateViews()
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var defaultView = TheList.Views[0];</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> view.ViewFields.Add(<span style="color: rgb(0, 96, 128);">"Code"</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> view.ViewFields.Add(<span style="color: rgb(0, 96, 128);">"Currency"</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> view.Update();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
When a list is created a default view is created that contains the Title field. Here we grab that view then add our two custom fields to it. You could also do all kinds of things with the view like setting the sort order, adding filters or creating entirely new views.
Finally we want to seed our list with some initial data so every time our feature is activated we have some data for it.
public override void AddInitialData()
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> addItem(<span style="color: rgb(0, 96, 128);">"Canada"</span>, <span style="color: rgb(0, 96, 128);">"CAN"</span>, <span style="color: rgb(0, 96, 128);">"CDN"</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> addItem(<span style="color: rgb(0, 96, 128);">"United States"</span>, <span style="color: rgb(0, 96, 128);">"US"</span>, <span style="color: rgb(0, 96, 128);">"USD"</span>);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> </pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'><span style="color: rgb(0, 0, 255);">private</span> <span style="color: rgb(0, 0, 255);">void</span> addItem(<span style="color: rgb(0, 0, 255);">string</span> title, <span style="color: rgb(0, 0, 255);">string</span> code, <span style="color: rgb(0, 0, 255);">string</span> currency)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var item = TheList.Items.Add();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> item[<span style="color: rgb(0, 96, 128);">"Title"</span>] = title;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> item[<span style="color: rgb(0, 96, 128);">"Code"</span>] = code;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> item[<span style="color: rgb(0, 96, 128);">"Currency"</span>] = currency;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> item.Update();</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
Here we have a helper to add an item to the list. Adding an item requires a few steps (creating the SPItem via Add, setting the data, calling update) so it’s wrapped in a method in the class.
You might be balking at the use of hard coded values and field names and whatnot here. That’s fine. You could go ahead and assign those to constants inside the class. The point is that the field names are all contained within this one class so if you ever wanted to change them you only have to change the values here. Create constants for the field name might be a good idea too. You could also do something like read from a text or xml file in your AddInitialData method if you had a lot of data to seed (or even pull it from a resource file in the assembly).
Now we have all the parts to build up the list. Let’s call it in a feature. As we move up the ladder and away from our list builder things get dead simple. Here’s the code we have to activate the feature:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">using</span>(var web = properties.Feature.Parent <span style="color: rgb(0, 0, 255);">as</span> SPWeb)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var director = <span style="color: rgb(0, 0, 255);">new</span> ListDirector(web);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> director.CreateUsing(<span style="color: rgb(0, 0, 255);">new</span> CountryListBuilder());</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
Like I said. Dead simple. We pass the SPWeb object to the director (who will internally pass it along to the ListBuilder as needed) and we call the CreateUsing method passing it a new CountryListBuilder.
If we want to add new lists all we have to do is this:
- Create a new ListBuilder class inheriting from ListBuilder
- Fill in the abstract methods
- Add a new CreateUsing call in our FeatureActivated method
- Deploy
Cool huh?
Okay, so we have it build but what about getting rid of it? Just as easy.
First we need to finish our CountryListBuilder. There’s one more method we need to implement:
public override void DeleteList(SPWeb web)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var id = web.Lists[<span style="color: rgb(0, 96, 128);">"Countries"</span>].ID;</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> web.Lists.Delete(id);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'>}</pre>
Now to finish off we just implement the FeatureDeactivating method:
public ovveride void FeatureDeactivating(SPFeatureReceiverProperties properties)
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>{</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> <span style="color: rgb(0, 0, 255);">using</span>(var web = properties.Feature.Parent <span style="color: rgb(0, 0, 255);">as</span> SPWeb)</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> {</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> var director = <span style="color: rgb(0, 0, 255);">new</span> ListDirector(web);</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'> director.DeleteUsing(<span style="color: rgb(0, 0, 255);">new</span> CountryListBuilder());</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: white;'> }</pre>
<pre style='margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: "Courier New", courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);'>}</pre>
Just reverse of activating we call the DeleteUsing method on the director passing it a CountryListBuilder object. We really don’t have to keep the variable around and there’s little cost to creating a new object to pass it. There’s nothing unique that we have to keep around from creation. The CountryListBuilder class is just there to provide us the interace to it’s methods.
You might be thinking why do all this? Why don’t I just create the ListBuilder class and call the methods directly. Like I said, the Director abstracts away all the co-ordination you need to build the list while the list knows how to build its parts, it doesn’t necessarily know how to construct itself. This is done for reason. Maybe you want to build the list up differently depending on the environment. Or maybe you have a Director to handle creating items from scratch and another director for updating existing data (say in the Feature Upgrading event with a production site). The same pattern and classes apply, there are just different implementations to create. Also with the abstraction the ListDirector and ListBuilder provides, the code you have to maintain (and debug) in the Feature events is next to nothing and any errors can happen and be handled down where they need to be, not have to bubble up to the feature (remember you have no UI in the feature events).
So that’s it. Now I just have to create a new class for each List I want to build, inherit from ListBuilder and just add one line of code to my Feature activating/deactiving. Pretty clean and pretty quick to get up and running.
This is just *one* way of doing this. You could use other patterns that might be more appropriate (the Abstract Factory or perhaps Prototype comes to mind) but this is the one I went with and it works pretty good for me so far. Fields are more complex beasts so my CreateFields() method is sort of doing double duty by adding fields and also updating existing ones (or new ones) by setting the field lengths. This could be split out to say two methods (CreateFields and UpdateFields) or even a FieldBuilder class could be created and handled by the ListBuilder (who would act as the Director) but maybe that’s over complicating things.
I’m sure there are a lot of things that could be better in this code. Interface vs. abstract class; passing in the SPWeb as a constructor to the ListBuilder; etc. And yes, there’s no exception handling, logging, or backout in case things fail here. What if someone renamed the list? What if the list doesn’t exist when the feature is deactivated? Hey guys, it’s a blog post, not production code. Feel free however to comment and suggest improvements and alternate ideas.
Hope this helps! Happy coding.
Metro Inspiration–Designing Better Looking Apps for Windows Phone 7
Recently I had the pleasure of checking out @jeffwilcox’s newest app for the Windows Phone 7 platform called 4th & Mayor. It allows you to interact with the location-based platform called foursquare.
His app allows you to check in, take pictures, find friends, etc. Everything other mobile apps for foursquare do. In the design of the app, Jeff has really done a nice job on embracing and extending the Metro look and feel that’s prevalent on the Windows Phone 7 platform.
Inspiration
For me, I’m not even a foursquare junkie. I (still) don’t see the value in telling the world where I am at any given moment but I gave it a shot with Jeff’s app. Using his app I created an account, checked into a few places, and even became mayor of a few local hops (like the corner variety store, go me). I still think foursquare is silly, but 4th & Major made it fun for me.
Some key design features I saw in 4th & Mayor:
- Large touchable icons. Not tiny little things but big, bold 200 pixel wide images that expanded out to full size when tapped.
- Use of Accent Colour. Jeff uses it sparingly and doesn’t overdo it by setting subtitles and key information on the screens in whatever accent colour you use.
- Light and Dark. Jeff makes sure that the app still looks good in both dark and light themes and uses bold Metro like icons for types of locations (instead of say photos)
- Whitespace and Alignment. Nothing is too crowded in the design, even when there’s a lot of information to present and all the text aligns nicely so it’s not all over the place.
Enter Metro
What’s Metro? Metro is the name of the new design language created for the Windows Phone 7 interface. It’s heavily influenced by Swiss print and packaging and focusing on simplicity. You can read up more about Metro here and review the Windows Phone 7 design guide that talks about specific controls and implementation here.
Since the release Jeff has also posted what he’s calling his Metro Design Guide. It’s a checklist (of sorts) that goes through the various aspects of the Metro design fundamentals and some things he’s sharing with everyone. It’s a great read and gets you into “thinking Metro” so I encourage everyone to check it out.
So where were we? Oh yeah. After playing around with 4th & Major and checking out a few other apps (like the Rock & Snow App out of the U.K. Marketplace) I thought it would be appropriate to give my simplistic Hamurabi game a facelift, Metro style. Little tweaks here and there just to clean things up a little. Here’s how I transformed the app to the next version.
The app is pretty basic. A pivot table with various sections for playing the app, getting help, etc. and one gameplay screen where all the action takes place. Here’s version 1.0
Bland and boring. The pivot control is the out of the box one. The gameplay screen is just a portrait page with some controls on it. I did do some styling in the first version around the slider control to make it a little better. I really didn’t care for the out of the box slider as my first test user claimed they didn’t know how to use it. So I just styled it with a round button with a little gradient background to make it more prominent. Yes, it looks a little like an iPhone control but there’s no real Metro equivalent and the default implementation sucks.
Metrofication
First up I went through the entire app and cleaned up the various offsets. In Jeff’s guide, the magic number is 12 pixels. I think I have engrained into my head now. 12, 12, 12, 12. And multiples of 12 are fine too, but by default regular text is indented 12 pixels from the edge of the screen. It’s a nice number so don’t mess with it.
As you can see from the screenshots above, I’m not completely consistent in my alignment and spacing. Text overhangs various places and isn’t consistent. Also the text on the home page doesn’t seem to stand out. It’s hard to tell the difference between the titles and the descriptions (they’re larger in size but I think we can do better).
The Prime Directive
If there were a Prime Directive when building an app for WP7 this might be it. Simplicity through consistency. Stick with known styles and numbers. The default spacing is 12 pixels and while you might need some more, try to keep with that number in mind. Keep your spacing consistent throughout your entire app as well as colours and fonts. I don’t mean use the same font everywhere but I do mean to not arbitrarily space controls on the design surface.
This idea here is that when a user is looking at one app, then flipping to another it shouldn’t be a jarring user experience. Your own app pages should be consistent in their font use and spacing as well. If everyone follows some simple consistent rules (both in and out of your app) then the user benefits. The experience is consistent and while you’re totally allowed to be creative, don’t overdo it.
That is, don’t do this:
Here’s a Windows app that someone wrote which just goes to show that being unique isn’t the best experience in the world for the end user. Cluttered, multiple colours, multiple fonts. In a word, confusion just from an aesthetic sense (let alone the poor sucker who has to use this app).
Metro is like a design pattern. In software development we have all kinds of patterns. Patterns for separation of responsibility, patterns for accessing external data. If you can program it, there’s probably a pattern for it. The thing is with patterns think of them like sewing patterns. They provide a shape to cut out and some instructions on how to use them. You can follow them by the book and get a good result. It’s the people that take patterns and apply their own creativity to them that makes them stand out. This is the same with Metro. There’s no rule written in stone that says you have to use a specific font in a specific size for a panorama title. What the Windows Phone toolkit provides out of the box is a default implementation.
Pivot to Panorama
For example let’s take a look at the Panorama control. As I was cleaning up the Hamurabi app I thought it might fare better as a panorama control instead of a pivot. It’s a choice as to what control to use as the two are very similar but I follow a simple rule. If the data presented is different slices of a bigger list (for example filtered views of a large list) then a pivot might be more appropriate. If the screens are somewhat disjointed and don’t really go well together then a panorama might work. Again, no hard and fast rule here but I thought I would try a panorama control.
Here’s the default panorama control you get if you use the Microsoft template:
Simple and it works. Nothing special you need to do except replace the background image with you own.
Or is there?
For Hamurabi I didn’t want the harshness of the title that comes with the out of the box panorama control so I created a new style for the title and added it to the page:
Then instead of using the default Header property I overrode that with a custom TitleTemplate:
The result is a slightly less glaring title. It’s still a Panorama control but I like it better than the default one. The text doesn’t stand out as much and I find it more inviting. Also with the reduced font size from the default, I have a little more room for my application.
Next up were the Panorama Items. Again, here’s the default:
And again we do a little styling. Instead of a single text title I wanted to put in a subtitle and change up the font weights and sizes a bit. Here’s the new XAML styles that we’ll use in our custom item header:
And here’s the updated XAML for the Panorama Item:
And here’s the updated Panorama control from the app:
These are simple changes we’re making here. Nothing Earth shattering but they’re making a difference. I’m not saying everyone should go out and make their apps look like this but be creative. Look at your subject and think about what experience you want your user to have. Is it better to identify things with icons than words? If you’re building something common that everyone knows by sight, maybe you should use graphics for titles (or to supplement them). Again, don’t go overboard here. Look back at good examples like 4th & Mayor. It’s not littered with graphics in every corner. Use it sparingly as an accent, not as the cornerstone of your app.
After doing the initial changes I found a suitable background image for the Panorama control. As Jeff points out in his article, don’t use a Panorama control if you only have two pages (it doesn’t flow very well) and keep the number down (he recommends no more than three). I went a little overboard with mine as I had help, tips, and strategy pages. If I combine them all I could bring that number and maybe I will.
The other slight change I did was to style the default button. Again I didn’t go overboard here as I didn’t want buttons to look like WinForms apps or something. I just felt the default white border button was boring so I just used a simple gradient background and a colour that was more appropriate for the application. Hopefully it blends in a little more. Here’s some screenshots from the transformation.
A little different from the original but not by much. Here’s a summary of the UI changes I did:
- Changed from Pivot control to Panorama (new background image)
- Styled Panorama Title (smaller font; lighter opacity)
- Styled Panorama Item Header to be two lines instead of one (plus slight font and size changes)
- Styled default buttons
- More use of Accent colours sprinkled on various pages to highlight important parts
- Larger icons for game mode selection (130 pixels instead of 100)
- More consistent alignment and spacing throughout the app
All in all it took me about two hours to do all the changes (including hunting down some stock images and putzing around with the button gradient). No real investment and I think it’s an improvement over the original version. It’s still not perfect and I can see a few little tweaks here and there with margins that I need to correct. There’s also version 1.2!
So if you’re going to take anything away from this its to be creative. Don’t always just accept the norm as norm. Apply your artistic talents to your apps and with a few slight modifications you can go from bland and boring to interesting and fun. And remember to not to go crazy. Get an outside opinion. Pass your app onto others, post screenshots, etc. Above all, go out and create.