Contents tagged with C#
-
SPListCollection ContainsList Extension Method
This is such a simple thing but something every SharePoint developer should have in their toolkit (well, actually, this is something Microsoft should put into the product). The SPFieldCollection has a nice method called ContainsField so you can check for the existence of a field (without throwing an exception if you get the spelling wrong). This is my version of the same method for SharePoint Lists.
Enjoy.public static class SPListExtensions
{
public static bool ContainsList(this SPListCollection lists, string displayName)
{
try
{
return lists.Cast<SPList>().Any(list => string.Equals(list.Title, displayName, StringComparison.OrdinalIgnoreCase);
}
catch (Exception)
{
return false;
}
}
}
-
Convention over Configuration with MVC and Autofac
One of the key things to wrap your head around when doing good software development using frameworks like ASP.NET MVC is the idea of convention over configuration (or coding by convention).
The idea is that rather than messing around with configuration files about where to find things, how to register IoC containers, etc. that we used to do, you follow a convention, a way of doing things. The example normally expressed is a class in your model called “Sale” and a database named “Sales”. If you don’t do anything that’s how the system will work but if you decide to change the name of the database to “Products_Sold” then you need some kind of configuration to tell the system how to find the backend database. Otherwise it can naturally find it based on the naming strategy of your domain.
MVC does this by default. When you create a controller named “Home” the class is HomeController and the views of the HomeController are found in /Views/Home for your project. When dealing with IoC containers there’s the task of registering your types to resolve correct. So let’s take a step back and take a look at a project with several repositories in it (a repository here being some kind of abstraction over your data store).
Here we have under our Models: Customer, Invoice, and Product with their respective classes, repositories, and interfaces:
This might be how your project is typically setup. I want to inject the dependencies on my repositories into my controller (via my IoC system) like this:
1: public class HomeController : Controller
2: {
3: private readonly ICustomerRepository _customerRepository;
4: private readonly IInvoiceRepository _invoiceRepository;
5: private readonly IProductRepository _productRepository;
6:
7: public HomeController(
8: ICustomerRepository customerRepository,
9: IInvoiceRepository invoiceRepository,
10: IProductRepository productRepository)
11: {
12: _customerRepository = customerRepository;
13: _invoiceRepository = invoiceRepository;
14: _productRepository = productRepository;
15: }
16:
17: //
18: // GET: /Home/
19: public ActionResult Index()
20: {
21: return View();
22: }
23: }
Then somewhere in my controller I’ll use the various repositories to fetch information and present it to the user (or write back values gathered from the user). How do my IoC know how to resolve an ICustomerRepository object?
Here’s how I have my IoC engine setup for this sample using Autofac. You can use any IoC engine you want but I find Autofac works well with MVC. First in Global.asax.cs I just follow the same pattern that the default projects setup and add a new static class called ContainerConfig.RegisterContainer()
1: public class MvcApplication : System.Web.HttpApplication
2: {
3: protected void Application_Start()
4: {
5: AreaRegistration.RegisterAllAreas();
6:
7: WebApiConfig.Register(GlobalConfiguration.Configuration);
8: FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
9: RouteConfig.RegisterRoutes(RouteTable.Routes);
10: BundleConfig.RegisterBundles(BundleTable.Bundles);
11: ContainerConfig.RegisterContainer();
12: }
13: }
Next is setting up Autofac. First add the Autofac MVC4 Integration package either through the NuGet UI or the Package Manager Console:
PM> Install-Package Autofac.Mvc4
Next here’s my new ContainerConfig class I created which will register all the types I need:
1: public class ContainerConfig
2: {
3: public static void RegisterContainer()
4: {
5: var builder = new ContainerBuilder();
6: builder.RegisterControllers(Assembly.GetExecutingAssembly());
7: builder.RegisterType<CustomerRepository>().As<ICustomerRepository>();
8: builder.RegisterType<InvoiceRepository>().As<IInvoiceRepository>();
9: builder.RegisterType<ProductRepository>().As<IProductRepository>();
10: var container = builder.Build();
11: DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
12: }
13:
14: }
What’s going on here:
Line 5 Create a new ContainerBuilder Line 6 Register all the controllers using the assembly object Line 7-9 Register each repository Line 10 Build the container Line 11 Set the default resolver to use Autofac Pretty straight forward but here are the issues with this approach:
- I have to keep going back to my ContainerConfig class adding new repositories as the system evolves. This means not only do I have to add the classes/interfaces to my system, I also have to remember to do this configuration step. New developers on the project might not remember this and the system will blow up when it can’t figure out how to resolve INewRepository
- I have to pull in a new namespace (assuming I follow the practice of namespace = folder structure) into the ContainerConfig class and whatever controller I add the new repository to.
- Repositories are scattered all over my solution (and in a big solution this can get a little ugly)
A little messy. We can do better with convention over configuration.
First step is to move all of your repositories into a new folder called Repositories. With ReSharper you can just use F6 to do this and it’ll automatically move and fix the namespaces for you, otherwise use whatever add-in your want or move it manually. This includes both the classes and interfaces. Here’s what our solution looks like after the move:
Pretty simple here but how does this affect our code? Really minimal. In the controller(s) that you’re injecting the repository into, you just have to remove all the old namespaces and replace it with one (whatever namespace your repositories live in).
The other change is how you register your ContainerConfig. Here’s the updated version:
1: public class ContainerConfig
2: {
3: public static void RegisterContainer()
4: {
5: var builder = new ContainerBuilder();
6: builder.RegisterControllers(Assembly.GetExecutingAssembly());
7: builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
8: .Where(x => x.Namespace.EndsWith(".Repositories"))
9: .AsImplementedInterfaces();
10: var container = builder.Build();
11: DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
12: }
13: }
Note that a) we only have one line to register all the repositories now and b) the namespace dependency we had in our file is now gone.
The call to RegisterAssemblyTypes above using the convention of looking for any class/interface in a namespace that ends with “.Repositories” and then simply registers them all.
So for the new developer on the project the instructions to them are to just create new repository classes and interfaces in the Repositories folder. That’s it. No configuration, no mess.
Hope that helps!
-
Where tips in LINQ
These might be old but as I was going through doing some code reviews and optimizations I thought I would share with the rest of the class.
Count() > 0 vs. Any
This is a bit of heated debate but as you dive in the LINQ world you'll start seeing simpler ways to write things. LINQ itself gets you away from writing loops for example (sometimes). One thing I notice in code are things like this:
if(entity.Where(some condition).Count() > 0)
When you could just write this instead:
if(entity.Any(some condition))
For me, writing *less* code is *more* important. It's a cornerstone in refactoring. Making your code more readable, more succinct. If you can scan code quicker while troubleshooting a problem or trying to figure out where to add an enhancement, all the better.
Donn Felker wrote about the Count() vs. Any() discussion here back in 2009 and it was discussed on StackOverflow here in case you're wondering if Any() is more performant than Count() (it generally is). So my advice is use Any() to make your code potentially easier to read unless there's a performance problem.
Another simple tip for code reduction is this one.
var x = entity.Where(some condition).FirstOrDefault();
Becomes:
var x = entity.FirstOrDefault(some condition);
I know it's simple but again it's about readability. The better you can scan someone elses new code you've never seen before is less time stumbling over the syntax and more about what the intention is.
Yes, these tips are old but you would be surprised seeing new developers write new code with them and wonder why?
-
Code to Interfaces. Right. What’s an Interface?
The premise of coding to interfaces has been around for awhile now. The concept is simple. Given a definition of something you create things based on that definition. That might be a horrible description of an interface but I didn’t want to go all Computer Science on you.
Interface? What’s an Interface?
Here’s a simple interface:
1: interface ICustomerService
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> IEnumerable<Customer> GetAllCustomers();</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> }</pre>
Pretty basic. We have a Customer class somewhere and this interface describes a method called GetAllCustomers that will return you a list of Customer objects.
With an interface you don’t have an implementation. There’s no code here to say where we get the customers from, just that we expect this to return us a list of them.
Now in our code we can write something like this:
1: public void DisplayAllCustomers(ICustomerService service)
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">foreach</span> (var customer <span style="color: #0000ff">in</span> service.GetAllCustomers())</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> <span style="color: #008000">// Output whatever customer info here</span></pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> }</pre>
The method here expects an object that implements the ICustomerService interface. That’s how we can build and compile this but we have yet to build an implementation of this method. Of course the code won’t run because your application doesn’t know how to create an object that implements ICustomerService.
Like I said, the implementation is up to you but you’ll probably be driving it from requirements or what the user needs to see or whatever. Here’s a sample implementation:
1: internal class CustomerRepository : ICustomerService
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">public</span> IEnumerable<Customer> GetAllCustomers()</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> <span style="color: #0000ff">return</span> <span style="color: #0000ff">new</span> List<Customer></pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> <span style="color: #0000ff">new</span> Customer {Name = <span style="color: #006080">"Harold"</span>}, </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum8"> 8:</span> <span style="color: #0000ff">new</span> Customer {Name = <span style="color: #006080">"Kumar"</span>}</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum9"> 9:</span> };</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum10"> 10:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum11"> 11:</span> }</pre>
So if we created an object of this CustomerRepository class and passed it to the DisplayAllCustomers method above, we would output Harold and Kumar’s names (or whatever your display code was).
The $10,000 Question
People will stare at the code and say, why? Why create that ICustomerService and then have to go to the trouble of creating it and passing it along to the DisplayAllCustomers. More code to maintain they say. More work.
Let’s try to dispel some myths here.
Coding to Interfaces is Hard
Really? Do you understand the code above? That’s coding to an interface. Could you do that yourself? Sure you can. Let’s move on.
Coding to Interfaces Constrains Me
It’s true. If you added the method “void AddCustomer(Customer customer)” to your inteface, you wouldn’t be able to compile your code. The CustomerRepostory class (and any other class that implemented the ICustomerService interface) would require it. Stop thinking about this as a constraint, it’s a design choice. It’s like the Architect giving you a window or door on the side of your house. You don’t go cutting open another hole because you want another window. You have to take into account load bearing walls, structural integrity, etc. which is what the Architect does (I know, I used to be one). Just because it looks good or you need it, doesn’t mean it should be done (at least in the way you might want it).
Coding to Interfaces makes you do extra work
Yes, you have to create those interfaces so yeah, that’s extra work. Some might argue that if your implementation is simple then you’re writing double the code. Again, all true. There are benefits that will outweigh this which we’ll look at in a moment.
Where are the Benefits?
Let’s talk some benefits here. First coding to an interface is giving you a layer of abstraction. Remember that ICustomerService above? The implementation is sort of silly but shows that we can write code that does what the system intends. We could also build an implementation that reads from a database. Or Active Directory. Or SAP. Or a Web Service. Each time we write a new implementation, we don’t have to change our DIsplayAllCustomers method.
That’s abstraction. You don’t have to worry in your DisplayAllCustomers method where the data came from or what infrastructure may or may not exist. All you care about is that you expect a list of customers to come back.
Now multiply that by 10 or 100 and you get the benefits of abstraction against a real codebase.
Some people will talk about future proofing and interfaces and while that may be a benefit down the road, and it can happen, consider it icing on the cake. Imagine if you had coded to an IEnumerable interface instead of ArrayList? Now you *might* not have to rewrite a lot of code (or any if you’re really lucky).
I do believe, and have rarely seen, entire implementations changed. For example one classic is the “build a database interface so we can swap between SQL and Oracle”. You build an abstraction over a database to make it simpler to code to but not necessarily swap out technologies.
Just don’t use the future proofing claim as a crutch to not code to interfaces claiming YAGNI or something. There are different reasons for this.
The other big thing is testing. Going back to our CustomerRepository. It’s an in-memory representation to a list of customers. Imagine you had additional methods on your interface like this:
1: internal interface ICustomerService
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> IEnumerable<Customer> GetAllCustomers();</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> <span style="color: #0000ff">void</span> AddCustomer(Customer newCustomer);</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> <span style="color: #0000ff">void</span> DeleteCustomer(Customer customerToDelete);</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> }</pre>
And now with your in-memory representation you can write tests that ensure items are added and deleted in your repository and the counts all match and the list comes back with the right names. Now you’re starting to test against your interface, which is a good thing.
Testing
Testing frameworks will let you do things like create stubs or fake implementations of the interface, without actually writing code to return actual values. Without interfaces if you tried to test the AddCustomer method in say a SQL based implementation, you would need a database, login information, test data, etc. That’s great for infrastructure tests but for unit tests it’s a lot of overhead you shouldn’t be getting into.
Another benefit is getting ahead of infrastructure. Imagine if your ICustomerService is going to talk to a web service, as web service that won’t be written for another month. You could go ahead and wait for the infrastructure to show up, code concrete classes against it, and then start your testing but now you’re in the crunch to get the system done and you’re just starting your unit testing.
Instead, based on requirements and perhaps UI discussions with users using paper, whiteboard, or digital wireframes, you come up with the interface. “We’re going to need to display the customer fields and oh yeah, we want to search by first and last name”. Great. From that description you can come up with an interface something like this:
1: interface ICustomerService
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> IEnumerable<Customer> FindBy(<span style="color: #0000ff">string</span> firstName);</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> IEnumerable<Customer> FindBy(<span style="color: #0000ff">string</span> firstName, <span style="color: #0000ff">string</span> lastName);</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> }</pre>
Again we can write up some implementation (maybe going against a preset list of names you import from a spreadsheet) and actually build out a working UI. The user can put their hands on it, search by names, and see the results returned. All without that pesky infrastructure. Then come the say the database gets built, you create your implementation to read it and do searches and BAM, your system is online and working end-to-end.
On the testing front again, how would you test something that’s dependent on DateTime? For example you have a piece of code that ages some items in a system based on some business rules (or expires them).
It’s all well and fine to start tossing around DateTime objects like this:
1: public void ExpireTest(ICustomerService service, DateTime date)
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">foreach</span> (var customer <span style="color: #0000ff">in</span> service.GetAllCustomers())</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> <span style="color: #0000ff">if</span>(customer.ContractDate > date)</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> ExpireContractFor(customer);</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum8"> 8:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum9"> 9:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum10"> 10:</span> }</pre>
However things get real ugly real fast. First I have to write this test and I’m sort of breaking both encapsulation and responsibility of the customer class. Maybe I should have a method on customer that takes in a DateTime object. Yuck. Now I’m passing that value into my business object which might be okay (it depends) but now consider the idea of something like this business rule:
1: foreach (var customer in service.GetAllCustomers())
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">if</span>(customer.ContractDate.Day == date.Day && date.Hour > 12)</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> ExpireContractFor(customer);</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> }</pre>
Now I’ll only expire the contract if the date passed in is the same day as my contract and it’s after noon. Silly logic yes, but would require another test method, another date object to be passed in, etc. A lot of setup to test something and then along comes this somewhere in my Customer class:
1: class Customer
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Name { get; set; }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span>  </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> <span style="color: #0000ff">public</span> DateTime ContractDate { get; set; }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span>  </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> AgeOfContract()</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum8"> 8:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum9"> 9:</span> <span style="color: #0000ff">return</span> (<span style="color: #0000ff">int</span>) (DateTime.Now - ContractDate).TotalDays;</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum10"> 10:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum11"> 11:</span> }</pre>
Now I’m screwed, both in testing in code and testing on the site. I’m going to have to create test data with very specific dates, maybe mess around with the values (because I certainly can’t change the clock on the server) and frankly I’m going to cry.
Interfaces can save you here. What if we had an interface called:
1: interface IDateTime
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> DateTime Now { get; set; }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> }</pre>
And instead of the concrete implementation in our customer class we use the IDateTime interface. Here’s the Customer class refactored to use an interface:
1: class Customer
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">readonly</span> IDateTime _dateTime;</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span>  </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> Customer(IDateTime dateTime)</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> _dateTime = dateTime;</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum8"> 8:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum9"> 9:</span>  </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum10"> 10:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Name { get; set; }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum11"> 11:</span>  </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum12"> 12:</span> <span style="color: #0000ff">public</span> IDateTime ContractDate { get; set; }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum13"> 13:</span>  </pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum14"> 14:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> AgeOfContract()</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum15"> 15:</span> {</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum16"> 16:</span> <span style="color: #0000ff">return</span> (<span style="color: #0000ff">int</span>) (_dateTime.Now - ContractDate).TotalDays;</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum17"> 17:</span> }</pre>
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum18"> 18:</span> }</pre>
Yes, there’s more that needs to be here like how an IDateTime can subtract values from each other, return a TImeSpan object, etc. but this is just for concepts.
With the interface added, I’m now abstracted away from the concrete implementation of DateTime hard coded into my Customer class. I’ll pass in something that might implement DateTime to return some real time but for testing I can set it to anything I want.
Testing is easier now and I don’t have to change my domain logic to deal with responsibilities outside of my concerns.
Interfaces vs. Classes is the kind of thing to start holy flame wars. Some argue it adds extra code/work to the developer, others claim it unnecessarily future-proofs your app (aka YAGNI) and others think it makes for easier testing and abstraction away from things that have yet to come.
I like to live in the latter world where I build my systems loosely coupled but tightly integrated. Interfaces provide me that ability. I hope this article sheds some light on the subject for you, whatever you choose.
Enjoy.
-
Farseer tutorial for the absolute beginners
This post is inspired (and somewhat a direct copy) of a couple of posts Emanuele Feronato wrote back in 2009 about Box2D (his tutorial was ActionScript 3 based for Box2D, this is C# XNA for the Farseer Physics Engine).
Here’s what we’re building:
What is Farseer
The Farseer Physics Engine is a collision detection system with realistic physics responses to help you easily create simple hobby games or complex simulation systems. Farseer was built as a .NET version of Box2D (based on the Box2D.XNA port of Box2D). While the constructs and syntax has changed over the years, the principles remain the same.
This tutorial will walk you through exactly what Emanuele create for Flash but we’ll be doing it using C#, XNA and the Windows Phone platform.
The first step is to download the library from its home on CodePlex. If you have NuGet installed, you can install the library itself using the NuGet package that but we’ll also be using some code from the Samples source that can only be obtained by downloading the library.
Once you download and unpacked the zip file into a folder and open the solution, this is what you will get:
The Samples XNA WP7 project (and content) have all the demos for Farseer. There’s a wealth of info here and great examples to look at to learn. The Farseer Physics XNA WP7 project contains the core libraries that do all the work. DebugView XNA contains an XNA-ready class to let you view debug data and information in the game draw loop (which you can copy into your project or build the source and reference the assembly).
The downloaded version has to be compiled as it’s only available in source format so you can do that now if you want (open the solution file and rebuild everything). If you’re using the NuGet package you can just install that. We only need the core library and we’ll be copying in some code from the samples later.
Your first Farseer experiment
Start Visual Studio and create a new project using the Windows Phone template can call it whatever you want.
It’s time to edit Game1.cs
Lines 4: Declare the debug view we’ll use for rendering (more on that later).
Lines 8: Declare _world variable of type class World. World is the main object to interact with the Farseer engine. It stores all the joints and bodies, and is responsible for stepping through the simulation.
Lines 12-17: Create the graphics device we’ll be rendering on. This is an XNA component and we’re just setting it to be the same size as the phone and toggling it to be full screen (no system tray).
Lines 34: We create a SpriteFont here by adding it to the project. It’s called “font” because that’s what the DebugView uses but you can name it whatever you want (and if you’re not using DebugView for your production app you might have several fonts).
Lines 37-44: We create the physics environment that Farseer uses to contain all the objects by specifying it here. We’re using Vector2.UnitY*10 to represent the gravity to be used in the environment. In other words, 10 units going in a downward motion.
Lines 46-56: We create the DebugViewXNA here. This is copied from the […] from the code you downloaded and provides the ability to render all entities onto the screen. In a production release you’ll be doing the rendering yourself of each object but we cheat a bit for the demo and let the DebugView do it for us. The other thing it can provide is to render out a panel of debugging information while the simulation is going on. This is useful in tracking down objects, figuring out how something works, or just keeping track of what’s in the engine.
Lines 49-67: Here we create a rigid body (Farseer only supports rigid bodies) to represent the floor that we’ll drop objects onto. We create it by using one of the Farseer factories and specifying the width and height. The ConvertUnits class is copied from the samples code as-is and lets us toggle between display units (pixels) and simulation units (usually metres). We’re creating a floor that’s 480 pixels wide and 50 pixels high (converting them to SimUnits for the engine to understand). We also position it near the bottom of the screen. Values are in metres and when specifying values they refer to the centre of the body object.
Lines 77-78: The game Update method fires 30 times a second, too fast to be creating objects this quickly. So we use a variable to track the elapsed seconds since the last update, accumulate that value, then create a new box to drop when 1 second has passed.
Lines 89-94: We create a box the same way we created our floor (coming up with a random width and height for the box).
Lines 96-101: We set the box to be Dynamic (rather than Static like the floor object) and position it somewhere along the top of the screen.
And now you created the world. Gravity does the rest and the boxes fall to the ground. Here’s the result:
Lines 105: We must update the world at every frame. We do this with the Step method which takes in the time interval. [more]
Lines 108-114: Body objects are added to the world but never automatically removed (because Farseer doesn’t know about the display world, it has no idea if an item is on the screen or not). Here we just loop through all the entities and anything that’s dropped off the screen (below the bottom) gets removed from the World. This keeps our entity count down (the simulation never has more than 30 or 40 objects in the world no matter how long you run it for). Too many entities and the app will grind to a halt.
Lines 125-130: Farseer knows nothing about the UI so that’s entirely up to you as to how to draw things. Farseer is just tracking the objects and moving them around using the physics engine and it’s rules. You’ll still use XNA to draw items (using the SpriteBatch.Draw method) so you can load up your usual textures and draw items and pirates and dancing zombies all over the screen. Instead in this demo we’re going to cheat a little. In the sample code for Farseer you can download there’s a project called DebugView XNA. This project contains the DebugViewXNA class which just handles iterating through all the bodies in the world and drawing the shapes. So we call the RenderDebugData method here of that class to draw everything correctly. In the case of this demo, we just want to draw Shapes so take a look at the source code for the DebugViewXNA class as to how it extracts all the vertices for the shapes created (in this case simple boxes) and draws them. You’ll learn a *lot* about how Farseer works just by looking at this class.
That’s it, that’s all. Simple huh?
Hope you enjoy the code and library. Physics is hard and requires some math skills to really grok. The Farseer Physics Engine makes it pretty easy to get up and running and start building games. In future posts we’ll get more in-depth with things you can do with the engine so this is just the beginning.
I've posted the entire source code for this article on Bitbucket so you can grab the whole package here.
Enjoy!
Some JSON Resources For You
As a developer I really dig JSON over XML for information exchange. It's clean, simple, and just works. I don't have to deal with complicated schema validation, reams of unreadable encoded text, and hierarchies that make no sense. I prefer to consume in apps and more and more services offer it up natively these days. Here are a few key tools I use that you might fun value-add in your dealings with JSON.
Hmm... is it JSON or Json? Anyway, here's a short list.
IMHO this is the key library for working with JSON. Yes, you can do some native de-serialization with .NET but the Json.NET library makes reading and writing JSON easy. You can de-serialize a JSON response with one line of code and suck it into a set of C# classes (or use the JObject class itself to pluck values out directly). If you're really hung up on the angle brackets, Json.NET can translate your JSON to and from XML (but that would be silly). Highly recommended and it even comes as a Nuget package so nothing to download and unzip!
Getting JSON and trying to look at it can be painful. Sure, it's a human readable format but sometimes it comes as a big gobbly-gook glob of text if the API returns your data with the whitespace compressed (to reduce the size of the package). This online tool lets you paste in a JSON string and reformat it into something more readable. Bonus feature is you can just plug in a URL that returns the data in JSON format as well. Handy.
Of course if you consume JSON with the Json.NET library one very slick feature is a single line of code to de-serialize it into a C# class. Here's an example:
var records = JsonConvert.DeserializeObject<RecordModel>(someDownloadedString);
The variable "records" will now be a complete object graph of your JSON (including child objects de-serialized into arrays, lists, or whatever your collection type is). For this to work you need some classes of course. They're simple to write and are just POCO but do have to match up with the structure of the JSON so it can be tricky creating them and a tiresome task.
Enter the JSON C# Class Generator project. This is a GUI tool that you plunk your JSON into, set some properties and generate a series of C# classes you can link into your project and perform the online-line-de-serialization above. Pretty nice and supports a lot of features plus it's open source to boot.
Going one step better (or worse depending on your point of view) is the online version of a similar tool. With the json2csharp site, you can either enter the JSON data directly or enter the URL and it'll fetch it for you. The class generation is simple (as it should be) with no options but it's nice as you don't have to download/install anything. I did find a few JSON APIs that didn't work with this one but did work with the formatter so in that case I had to enter the URL in the formatter to get the JSON, then plunk the data into this tool to get my classes. In any case, it's way better to use tools like these rather than writing the classes yourself (again, my opinion, your mileage may vary).
If you're using JSON and looking for a quick hit to see your data, look no further than JSONView, a Firefox add-on that will format the JSON data output for you. Once installed, just visit the URL to your JSON data and the output will render in the browser. That's as simple as it can get. No mess, no fuss.
There are a lot of tutorials out there on using some of these tools, including one shameless plug of my own. Here's a blog entry I wrote on consuming MediaWiki's content in JSON format. MediaWiki is the software powering Wikipedia (along with a ton of other wiki sites). It walks through using the Json.NET library and navigating around Wikipedia using the data in JSON created classes.
Got more resources? Post them in the comments if you have some other JSON related resources and I'll update the post with them. Until then, enjoy!
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.
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.
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.