Saturday, December 26, 2009

What I Learned from PDC - Domain Technologies (continued)

Controller Model: All the commanding and navigation instructions are implemented at this level. Think of this layer as reacting to menu clicks, hyperlinks or changes of URL address. The controller is one of the central models in ASP.NET MVC (Model View Controller) and in a good implementation will defer many of the control logic decisions to the domain model. In ASP.NET MVC, the controller is used to return a view based on an action, and will typically consult the model to do so. In other applications the controller is not as pronounced, however there are usually classes that expose functions to act as command targets.

Talking Points: Exposing ICommand for Silverlight has simplified the implementation of a control pattern. Best examples using the controller pattern are FT59: ASP.NET MVC 2: Ninjas Still on Fire Black Belt Tips by Scott Hanselman, FT22: Microsoft ASP.NET MVC 2: The New Stuff by Stephen Walther, CL22: Advanced Topics for Building Large-Scale Applications with Microsoft Silverlight by John Papa and CL21: Building Amazing Business Applications with Microsoft Silverlight and Microsoft .NET RIA Services by Brad Abrams. None of these talks show you how to build a controller, but they all use an MVC pattern to build their application.

Data Proxy: The Data Proxy layer is a service oriented architecture (SOA) where the communication is with a data source. There is no limit to the data proxies in this layer. In fact there is no limit to the number services your domain can subscribe or publish to. Proxies can be generated automatically by querying metadata about the WCF service. Proxies are class based representations that model the WCF services API. The abstraction is so good, you are hardly aware that you are using a service on a remote server as you program. Proxies can be generated for anything that has a communication contract. The contract is typically expressed in metadata and there are tools for translating the metadata into code that implements the contract explicitly.


This layer is all about communication. The pictures above were taken from Yavor Georgiev’s talk: CL06: Networking and Web Services in Silverlight. The first picture shows the technology layers that .NET WCF provides. The programming model is built over a TCP/IP based protocol. The second picture shows the spectrum of communication applications. Yavor discusses the interaction between communication and different types of applications, from transactional data systems to multicast video systems. Generally speaking, WCF RIA services is the Microsoft technology of choice for building Silverlight rich clients and there are several talks showing this.

Talking Points: See FT12: ADO.NET Data Services: What’s new with the RESTful data services framework Pablo Castro’s client demo (at 44 min in). For an intro to RIA services: CL21: Building Amazing Business Applications with Microsoft Silverlight and Microsoft .NET RIA Services with Brad Abrams. You can tell from this talk the Microsoft is shaping this technology to create Line of Business CRUD applications. You can dive below the surface with CL07: Mastering Microsoft .NET RIA Services.

Tuesday, December 22, 2009

What I Learned from PDC - Domain Technologies

The domain is the brain or personality of the application and is usually built with the help of a Subject Matter Expert (SME).

I like to think of the domain as having the real knowledge of the application and the Client and Server components as providing input/output services. Think of the case where a text box control is doing input validation. The code in the control can be kept simple, it just needs to verify that the text typed in by the user conforms to a validation rule, and if it does not, display some type of message.

An implementation that binds the control to a text string might also execute validation logic in the On-Click code behind method. An implementation that binds the control to a property lets the code in the control stay generic, and keeps all the validation logic in the domain model where it can be easily maintained. This separation of concerns is the ‘S’ of the ‘SOLID’ design principle and you can learn about the other components (OLID) here.

When you create a domain model that uses the vocabulary of your SME, and solves problems using the processes the SME would use, then the rest of the application becomes easier to build. The Client and Server layers become tools to visualize your domain model or persist your domain model.

Domain Model: The Domain Model executes the formal business logic and should be the primary location for all the application state transition rules. Very few talks have time to develop a Domain Model, however if you watch talks that focus on language features, LINQ, Rx or parallel computing they are full of examples showing how domain modeling is becoming more declarative and less imperative. That is, software design is evolving into describing what you need the computer to do instead of traditionally telling the computer how to do it. .NET is evolving to merge dynamic, functional and object oriented programming models into the platform. The declarative slant in coding allows the .NET platform to make decisions about how to execute the code at runtime, to take advantage of system resources like multiple CPUs or resources in the Cloud.

Talking Points: Microsoft talks do not focus on the domain model at all. The only talk I found that fits the definition is Cl36: Deep Dive on Bing Maps Silverlight Control because it focuses on the domain of mapping, however the domain is so well defined it is delivered as a GUI control and a service. Instead look to the language features to help you model your domain: FT11: Future Directions for C# and Visual Basic. Developers should be aware of a new (or maybe very old if you know LISP) way of programming FT20: F# for Parallel and Asynchronous Programming , languages for Data Modeling FT34: Microsoft Project Code Name “M”: The Data and Modeling Language, Parallel Computing Platform: P09: Manycore and the Microsoft .NET Framework 4: A Match Made in Microsoft Visual Studio 2010 and Erik Meijer VTL04: Rx: Reactive Extensions for .NET. Rx technology was the language innovation of the show. Learn about Rx in 15 min here.

I will cover the Controller Model and Data Proxy in the next post.

Friday, December 18, 2009

What I Learned from PDC - Client Technologies

Client Technologies

There are many different client side technologies in the Microsoft ecosystem, and each has evolved to meet niche markets and end user needs, including video game, business apps, reports, and web page browsing. The defining technology of the client is probably how the view is rendered and the device’s ability to interact with that rendering.

View Rendering: Microsoft provides two different solutions for rendering a GUI: the thin client solution is HTML/JavaScript rendered inside a web browser, the rich client solution is XAML, which can be rendered in a web browser via a Silverlight plug-in, or as part of a thick client application using WPF. So XAML is dual purpose with the caveat that the end user must install the Silverlight plug-in (5 MB, 30 second, one time operation, just like Flash). Simple HTML has more range, because it runs in any browser, but when you start adding CSS and JQuery libraries (and other fancy things) you begin to limit yourself to a subset of browsers, IE7,IE8,Firefox,… again if you can control your target, this is not a problem. The Silverlight plug-in is designed to remove browser incompatibilities and create a UI that is “run anywhere” including Mac and Linux.

Talking Points: FT29: Microsoft AJAX Library, jQuery, and Microsoft Visual Studio 2010 Stephen Walther, shows how to build thin HTML clients with a new open source AJAX library, including 40 client controls, Open Source.

View Model: The purpose of a View Model is to shape your domain model to a view rendering technology. You must manage the interaction controls, events, value conversion, input validation, and general state of the GUI controls rendering using this model. In WPF/Silverlight, the View Model provides a site to bind the UI to the domain model. Sometimes perceived as additional work by programmers, the decoupling of the view from the domain model actually simplifies the implementation of the client GUI by providing a site for view logic that is independent of the domain. Some good examples are multi-lingual UI’s, currency conversion, hiding sections based on roles, and changing screen presentations based on task, expertise or device size. This separation also makes the application much more testable using an automated test suite.

Talking Points: Newly added ICommand support was the final step in making the view rendering and the view model truly independent and testable. Some Silverlight talks that discuss View Models are Cl19: Building Line of Business Applications with Microsoft Silverlight 4 and John Papa’s talk Cl22: Advanced Topics for Building Large-Scale Applications with Microsoft Silverlight

Saturday, December 5, 2009

What I Learned from PDC - Service Oriented Architecture

Mapping Functionality into Abstract Software Components

Each of the presentations at PDC are designed to discuss some aspect of Microsoft’s packaged technology: ASP.NET, SQL Server, Silverlight, Entity Framework, LINQ & PLINQ, MVC, WPF, WCF Data service, Azure, … Windows 7. In every case, the presentations show code samples on how to use that technology, and in the process they casually introduce some abstract software architecture concept (View Models, Data Proxies, Domain Models…) to help illustrate the technology.

Application Depth

I have extracted the software concepts from all those talks and constructed an application depth stack that maps these functional requirements to software layers. These layers combine the “best practices” in software design from ASP.NET MVC, WPF & Silverlight applications and also include technologies that are common to all solutions like WCF Data Services, SQL Server and Entity Framework. The software component names are not official and the code boundaries between them in many cases are not precise, but they are useful in categorizing the Microsoft technologies that are used to construct and execute each layer.

Client, Domain and Server Models

I broke the sections up into Client, Domain, and Server. The Domain section can execute on the client or the server. When deployed as a thin client, the Domain logic is executed on the Server, but when deployed as a rich client the Domain executes on the Client and helps manage State. Server components are traditional data access services. Using data services is now Microsoft’s preferred way of connecting to data. Data services use simple web protocols to request and transport data even if you are consuming data on the same machine as the client. The service can be accessed just by passing a URL. Data access is provided using proxy service “wrappers” that provide an API by ingesting the META data about the service. These are the building blocks of a Service Oriented Architecture (SOA).

Best Practices? For My Purposes They Are!

I am not saying that each of these abstract software components need to physically exist in order for you to have a well designed or valid .NET application. Most CRUDS database solutions (Create, Read, Update, Delete, Search) are put together in a forms-over-data software architecture and do not implement all of these components. I am saying that during the design/development phase of a solution, I believe that the most developers should at least think about these functional areas, even if it is just to dismiss them as unnecessary.

Monday, November 30, 2009

What I Learned from PDC - Thick, Thin and Rich Clients

These three paradigms of application deployment try to optimize between application reach and application experience.


Thick Clients

Thick clients have easy access to client resources (file system, local databases, USB ports, and peripherals). They are also characterized by running their code on the local CPU. Thick clients do not need to be connected to the internet to run, but they can be connected to resources on the internet. Using local resources and memory allows them to be ‘stateful’, which means the application can easily remember the actions of the user as the user progresses. Behavior like dynamically enabling menus, progressing through a wizard dialog or cut/copy & paste are examples of statefullness. Microsoft provides 2 technologies to create thick client applications: Winforms (Win32 & GDI) and WPF.

Thin Clients

‘Thin client’ was the original name given to web browser based applications. This paradigm has all the code executing on the server, and the code is designed to deliver information to a web browser (HTML and JavaScript), and the browser will render the information as a web page. Microsoft markets ASP.NET and ASP.NET MVC as platforms that deliver thin client solutions. These applications have achieved wide popularity because of their reach -- anyone with a browser and an internet connection can access this information -- regardless of the hardware or operating system. Developing richness in these applications requires the use of Cascading Style Sheets (CSS), Ajax, JQuery and JavaScript.

Browser based applications are not really stateful on the client (with the exception of cookies and session tokens), so with each user interaction, the local HTML, stored in the browser’s in-memory Document Object Model (DOM), is modified or refreshed dynamically. Ajax and JQuery are technologies used to modify the contents of the DOM (letting it refresh the screen after the change) and can be used to create some amazing GUIs that rival thick client applications. Thin clients have two weaknesses:
1. For security reasons, they are forced to run in the browser’s “sandbox”, which limits access to local system resources.
2. Building a rich GUI is difficult, and making that GUI testable is extremely difficult.

Based on my conversations at PDC, I found that programmers who quickly adopt the Ajax / JQuery route to provide richness have spent years creating UIs by programming JavaScript or HTML in ASP.NET applications. These technologies naturally fit into their way of thinking and their existing knowledge and experience.

I have seen solutions that deploy thin client / server applications onto a single box to try and get the advantages of the thick client. This can be done by using a Web Server (like IIS) and works well in applications that do NOT need to access local machine resources. This approach has difficulty synchronizing with a data source on the web. Integrating to local resources from the browser “sandbox” eventually requires some sort of browser plug-in that the end user trusts and has given permission to access the resources.

Rich Clients

Rich client applications, and Silverlight in particular, try to blend what is best about thick and thin clients. Microsoft accomplishes this by making Silverlight a 5 MB subset of the .NET platform, optimized to run on any OS (Windows, Mac, Linux) instead of targeting any particular browser. Silverlight is delivered to the browser as a plug-in (similar to Flash or Adobe Reader) and can use the browser’s resources and DOM.

Applications written using Silverlight can also be installed on the desktop and access local machine resources. So, like thick client solutions, rich clients run code locally and offer GUI features like drag/drop, animation, threading, and statefulness. They can also run connected or disconnected and can access local resources. Like thin client solutions, rich clients are accessible via a web browser, deliverable without a separate install, can detect newer versions and update automatically. Silverlight has the advantage of using the same programming environment as the server. This is VERY important.

As a developer, what I really like about Silverlight is that you can use the same code regardless if it is running in a browser or on the desktop. All the development can be done in WPF (XAML) and C#, so I do not need to be expert in a lot of different authoring technologies. Having the common .NET platform on both the client and the server makes it easier for me to optimize the solution’s performance just by moving code between the client project and the server project. Silverlight is aware of where it is running, which makes it easier to add and remove application features at runtime by querying the environment dynamically and not having to trust that the user installed the correct version. Lastly, the entire solution, from GUI to database access, can have a built-in automated test suite to assure quality and performance.

Asynchronous vs. Synchronous Communication

Asynchronous and synchronous communications are shown in boxes on the diagram. Silverlight enforces an asynchronous communications model when communicating with the server. That is to say that GUI interaction is not blocked when you are loading data or web pages. So while the user is performing tasks like navigation or mouse over, in the background Silverlight can be running calculations, doing analysis, loading data or any other programming task. Thin client solutions, including ASP.NET MVC, tend to run in a synchronous manner, which is where the user performs some action, the request is passed to the server, then the server sends a page of information back to the client, and the browser re-renders the page. Ajax and JQuery allow the application to send requests to the server that re-render sections of the page, but you cannot interact with those sections while they are ‘waiting’ for information. Microsoft introduced GUI controls that allow the Silverlight UI to behave synchronously by showing a progress bar and blocking user interaction. Blocking has it uses, but to me this feels like a step backward.

The challenge is to design UIs that are still functional while waiting for data. Think of iTunes - while songs are downloading, iTunes does not prevent you from accessing all of its features. Building a good GUI that blends the menu / work-area paradigm of the desktop with the hyperlink / URL navigation of the web is very tricky. Users can get focused on the details so quickly (colors and styles) that the things that make a very useable GUI, (layout, workflow, transitions and statefulness) do not get detailed consideration until after deployment.

Wednesday, November 25, 2009

What I Learned from PDC - An Overview

I attended MIX07, PDC07, MIX08, PDC08, and MIX09 online, so I have been following the Microsoft technical narrative for the last 3 years. I attended PDC09 live. One of my goals was to organize the technical narrative, which is normally presented product / technology centric into the components of a single application centric model.

For the past decade, my customers were looking for applications that offered the best of all experiences. Because they were unconstrained by technology in their thinking, they asked for features that were an amalgam of Web, Phone and Desktop applications, their ‘ideal’ application. I have been looking for a set of technologies that can deliver all these features from a single code base. I could see Microsoft was approaching such a solution, and at PDC09 many of these pieces came together. I created a diagram to help navigate the changing face of Microsoft technology in the light of customers’ Web/Desktop visions.


If you find this diagram hard to read, don't worry -- it will be broken down into readable pieces in the blog posts that follow.

The ‘ideal’ application in my customers’ eyes can run over the web, or run disconnected on a laptop without changing the GUI or user experience. Being connected to the internet should only be necessary to read or share data, but not a requirement to complete work. It should also be able to run on nearly any device with any screen size, a server, desktop, laptop, or a phone. Software components must be agnostic of where they are running (client or server) and cannot duplicate logic. The software community describes this style of application as Line of Business (LOB) apps; I do not like the term but it looks like it is getting a lot of traction on the web. Two archetypes for this LOB style of application are a traveling salesman and a soldier deployed in Afghanistan.

The diagram above is used to map the Microsoft technologies to support the LOB application. Not everything discussed at PDC09 has a place in this mapping, but I think you can get more out of the talks if you keep this diagram in mind while you pick and choose among the talks. The diagram shows, on the left side, the breath and depth of an application, on the right side, the technology and tooling available to construct, maintain and deploy the application. The functional layer (vertically down the center) was extracted from and reinforced by many of the PDC talks.

A well designed end-to-end .NET application will account for all of these functional layers in each of the technology stacks. As developers (and users), we can see the value in a software architecture that includes GUI, Client/Server side validation, Logic and Rules, Command/Navigation control, Data services, Networking, Object Relation Mapping, and Persistence.

Vertically the diagram shows a progression from End User to Application Hosting, via a set of functional requirements (Functional Layer). Horizontally, there is a set of four technology stacks, each with a unique perspective on building software.
Horizontally I have juxtaposed 4 diagrams each focusing on a different aspect of the software development process -- breadth, depth, platform, and tooling. Here are some working definitions:
•Breadth: who is the audience and how do I reach them, how do they plan to access my solution?
•Depth: what tasks do my users do and under what conditions, how do I make the application easy to use?
•Platform: what are the technologies I can use to build a deployable, maintainable, extensible solution, successfully using the skills of my development staff?
•Tooling: what are the tools available to assure that I am producing the solution efficiently and correctly?

My goal is to explore the subset of the Microsoft technologies presented at PDC in the context of building a solution that can run thinly over the web, or disconnected as a thick client reusing as much code and common technologies / tooling as possible. The result should be a road map of the essential Microsoft technologies you should be aware of in order to build a modern .NET client/server application.

Monday, August 31, 2009

M-V-VM Product Configuration

Deb and I have created many solutions that support sales or engineering efforts. Each application is a little different, driven by the unique aspects of the product we are selling/designing. We have built systems to sell cars, to quote multi-million dollar communications infrastructure projects, to design chemical plants and gear boxes.



You do enough of these applications and you start to see a software design pattern. You also realize just how important the role of the subject matter expert is in helping you create a successful application.


Our first Silverlight product configuration solution is a Cookie Designer. With a lifetime of experience eating cookies and a trip to the store to read the packaging, I now consider myself a subject matter expert. You can test drive the program HERE.

The Cookie Designer is an entry in the Summer Silverlight Coding Competition -- please try it and vote for our solution. Community points are an important part of the judging in this contest.

Our cookie product configuration solution is composed of a set of ingredients that can be combined without any mutually exclusive constraints. Anyone for cinnamon oatmeal dark chocolate chunk?

The secret to creating a convincing picture is to have each ingredient on its own separate layer and to make visible the layers that are involved -- enter the M-V-VM pattern, and in this case used as an adapter / translator between the composition of the model and what is shown.


<Canvas x:Name="CookieRoot" Height="400" HorizontalAlignment="Center" VerticalAlignment="Center" Width="400" Clip="M0,0L400,0 400,400 0,400z" RenderTransformOrigin="0.5,0.5">



<Image Height="381" Width="404" Canvas.Left="-9" Canvas.Top="8" Source="Cookie_Images/Base Cookie.png"/>

<Image x:Name="Cinnamon" Height="382" Width="390" Opacity="0.651" Canvas.Left="6" Canvas.Top="13" Source="Cookie_Images/Cinnamon.png" Visibility="Collapsed">

</Image>

<Image x:Name="PeanutButter" Height="379" Width="385" Opacity="0.478" Canvas.Left="7" Canvas.Top="9" Source="Cookie_Images/Peanut Butter.png" Visibility="Collapsed"/>

<Image x:Name="ReesesPeanutButter" Height="379" Width="385" Opacity="0.478" Canvas.Left="7" Canvas.Top="9" Source="Cookie_Images/Peanut Butter.png" Visibility="Collapsed"/>

<Image x:Name="Cocoa" Height="387" Width="389" Opacity="0.678" Canvas.Left="5" Canvas.Top="5" Source="Cookie_Images/Cocoa.png" Visibility="Collapsed">

</Image>

<Image x:Name="Oatmeal" Height="380" Width="387" Opacity="0.412" Canvas.Left="6" Canvas.Top="7" Source="Cookie_Images/Oatmeal.png" Visibility="Collapsed"/>

<Image x:Name="Peanuts" Height="377" Width="381" Canvas.Left="9" Canvas.Top="10" Source="Cookie_Images/Peanuts.png" Visibility="Collapsed"/>

<Image x:Name="Raisins" Height="373" Width="370" Canvas.Left="15" Canvas.Top="10" Source="Cookie_Images/Raisins.png" Visibility="Collapsed"/>

<Image x:Name="Pecans" Height="313" Width="302" Canvas.Left="56" Canvas.Top="56" Source="Cookie_Images/Pecans.png" Visibility="Collapsed"/>

<Image x:Name="Almonds" Height="286" Width="333" Canvas.Left="14" Canvas.Top="59" Source="Cookie_Images/Almonds.png" Visibility="Collapsed"/>

<Image x:Name="WhiteChocolateChips" Height="351" Width="353" Canvas.Left="18" Canvas.Top="45" Source="Cookie_Images/White Chocolate Chips.png" Visibility="Collapsed"/>

<Image x:Name="SemisweetChocolateChips" Height="400" Width="400" Canvas.Left="0" Canvas.Top="0" Source="Cookie_Images/Dark Chocolate Chips.png" RenderTransformOrigin="0.5,0.5" Visibility="Collapsed">

</Image>

<Image x:Name="MilkChocolateChips" Height="351" Width="380" Canvas.Left="7" Canvas.Top="45" Source="Cookie_Images/Milk Chocolate Chips.png" RenderTransformOrigin="0.5,0.5" Visibility="Collapsed">

</Image>

<Image x:Name="MacadamiaNuts" Height="377" Width="354" Canvas.Left="38" Canvas.Top="12" Source="Cookie_Images/Macadamia Nuts.png" Visibility="Collapsed"/>

<Image x:Name="DarkChocolateChunks" Height="546" Width="456" Canvas.Left="-27" Canvas.Top="-88" Source="Cookie_Images/Dark Chocolate Chunks.png" RenderTransformOrigin="0.5,0.5" Visibility="Collapsed">

</Image>

<Image x:Name="MilkChocolateChunks" Height="546" Width="456" Canvas.Left="-27" Canvas.Top="-88" Source="Cookie_Images/Milk Chocolate Chunks.png" RenderTransformOrigin="0.5,0.5" Visibility="Collapsed">

</Image>

<Image x:Name="WhiteChocolateChunks" Height="546" Width="456" Canvas.Left="-27" Canvas.Top="-88" Source="Cookie_Images/White Chocolate Chunks.png" RenderTransformOrigin="0.5,0.5" Visibility="Collapsed">

</Image>

<Image x:Name="HersheyMiniKisses" Height="134" Width="134" Canvas.Left="134" Canvas.Top="126" Source="Cookie_Images/Hershey Kiss.png" Visibility="Collapsed"/>

</Canvas>





We were careful to name the XAML elements so they could be controlled through an automated binding statement that is generated when the picture is loaded.


void CookieCanvas_Loaded(object sender, RoutedEventArgs e)
{
foreach (Image oItem in CookieRoot.Children)
{
string sName = oItem.Name;
if (string.IsNullOrEmpty(sName))
continue;
var oBinding = new Binding(sName);
oBinding.Mode = BindingMode.OneWay;
oItem.SetBinding(Button.VisibilityProperty, oBinding);
}
}

With all this binding now in place the final step is the view model.

public class CookieVM : ViewModel

{

public CookieVM(Instance oSource)

: base(oSource)

{

}

public override void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

{

if (e.NewItems.IsNotNull())

foreach (Component oNote in e.NewItems.OfType<Component>())

NotifyPropertyChange(oNote.ClassName);



if (e.OldItems.IsNotNull())

foreach (Component oNote in e.OldItems.OfType<Component>())

NotifyPropertyChange(oNote.ClassName);

}

public Visibility Cinnamon

{

get { return DoesChildOfClassExist("Cinnamon") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility PeanutButter

{

get { return DoesChildOfClassExist("PeanutButter") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility ReesesPeanutButter

{

get { return DoesChildOfClassExist("ReesesPeanutButter") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility Cocoa

{

get { return DoesChildOfClassExist("Cocoa") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility Oatmeal

{

get { return DoesChildOfClassExist("Oatmeal") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility Peanuts

{

get { return DoesChildOfClassExist("Peanuts") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility Raisins

{

get { return DoesChildOfClassExist("Raisins") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility HersheyMiniKisses

{

get { return DoesChildOfClassExist("HersheyMiniKisses") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility Pecans

{

get { return DoesChildOfClassExist("Pecans") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility Almonds

{

get { return DoesChildOfClassExist("Almonds") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility DarkChocolateChunks

{

get { return DoesChildOfClassExist("DarkChocolateChunks") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility MilkChocolateChunks

{

get { return DoesChildOfClassExist("MilkChocolateChunks") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility WhiteChocolateChunks

{

get { return DoesChildOfClassExist("WhiteChocolateChunks") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility WhiteChocolateChips

{

get { return DoesChildOfClassExist("WhiteChocolateChips") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility SemisweetChocolateChips

{

get { return DoesChildOfClassExist("SemisweetChocolateChips") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility MilkChocolateChips

{

get { return DoesChildOfClassExist("MilkChocolateChips") ? Visibility.Visible : Visibility.Collapsed; }

}

public Visibility MacadamiaNuts

{

get { return DoesChildOfClassExist("MacadamiaNuts") ? Visibility.Visible : Visibility.Collapsed; }

}
}

Saturday, August 1, 2009

Converting SketchFlow to Production

Deb: I was really excited to start working with Blend 3 with SketchFlow! I had seen videos from MIX 09 demoing SketchFlow and was disappointed it wasn't in the first release of Blend 3. Finally I was going to get a chance to try it myself.

I had a few learning pains, working with the SketchFlow Map - the documentation was a little sketchy (pardon the pun). I also had a problem getting the navigation to work right until I learned about right-clicking on the button and going to the bottom of the menu to tell it where to navigate to. Once I learned that, it was amazingly easy to setup a prototype project. I was really pleased with the overall tool; I was able to build a nice prototype in a relatively short period of time.

My disappointment has come as I try to convert this prototype to production. I hadn't read ahead in the help documentation about how to do this because I assumed it would be relatively straight-forward. Imagine my surprise when I found a series of 16 steps that had me deleting files, navigating to obscure folders in the SDK to include references, and editing code in Notepad! The best advice I received in this series of steps was Step One: backup your project. After an hour of tedious effort, I got nothing. The project wouldn't run.

So I googled 'converting sketchflow to production' and tapped into a conversation within the Silverlight community about just how ready for primetime SketchFlow really is. Some people are happy to have such a powerful prototyping tool at their disposal, but say that when you are ready to build the application for real you will need to build it from scratch. Others range from disappointment to outrage, feeling Microsoft rushed Sketchflow out before it was really finished, not taking it through the final, critical step of conversion to production.

Me, I've turned my prototype project over to Steve, the Developer, to see how much he can convert and how much effort is required to convert it. We will let you know in a later post what we learn.

Meanwhile, I will be rebuilding the prototype from scratch, as a new project. I will also investigate getting access to the 'sketchy styles' from a normal Silverlight project. It seems to me that much of the value of SketchFlow would be delivered by the styles alone. Now if creating navigation was only as easy in a regular Silverlight project..........

Thursday, July 16, 2009

Autosizing of Grids, Canvasses and StackPanels

Deb: Moving from a relatively static environment, like Photoshop, to a dynamic environment like Silverlight/Blend, requires learning about autosizing and how visuals will behave when the browser is resized. One of the most challenging things for me to learn was the differences in behaviors between grids, canvasses and stackpanels.

I started with grids, the workhorse of the layout panels, and I got comfortable with setting the height and width to 'auto', and the alignment to stretch, and setting the margins to achieve the effect I was looking for. I learned how to set column and row sizes, locking certain ones and leaving others to size dynamically as the space changed.

I moved on to canvasses, learning how to place objects on the panel in a different way, but the autosizing aspect of the layout panel worked the same as the grid. I figured I had this dynamic sizing thing figured out. Then I got cocky and started sticking stackpanels into my grids, and suddenly things weren't working the way I expected. This video explains why:

I am not a developer, but I do have a strong intuitive understanding of hierarchies and trees. I had developed a mental model that said that if I picked 'auto' and 'stretch', my object would size to match the parent it was embedded in. Working with stackpanels forced me to reevaluate my mental model, to acknowledge that sometimes the sizing was driven by the children embedded in the object, and margins - as seen in the video - seem to be able to refer to both (in the example, the top and left margins are relative to the parent canvas but the right and bottom margins are relative to the 'children' buttons).

This is where trial and error has led me. If there is an explanation for these behaviors, a simplifying principle that can be applied in all cases, I would love to learn it -- I really like to understand why things are the way they are. Post a comment and let me know what you think!

Tuesday, July 14, 2009

The Designer's Perspective: Working with Silverlight and Expression Blend

Deb: For too long now, I've been sitting on the sidelines, content to edit Steve's posts. But it's time to start representing the designer's viewpoint when it comes to using Silverlight 3 and Expression Blend 3.

I was first attracted to Silverlight for two reasons: 1) visualization is very important to me and Silverlight lets me design some very compelling visualizations, and 2) Expression Blend promised me significantly more autonomy, and the ability to do whatever I could imagine without relying on a developer to translate my vision for me.

While I haven't become as autonomous as I would like, Blend keeps improving and for the most part, Blend 3 is a joy to work with. I have my moments of frustration, sure, but they are more than offset by the power and independence I've been given. The goal of my posts will be to reduce the frustration factor even further for fellow designers.

I can summarize my attitude toward XAML by saying this: in general, the only legitimate reason for me to go to the XAML is to finetune the sizing of rows and columns. Other than that, I shouldn't need to even look at it. I think this should be the ultimate test of Blend's ability to meet the needs of designers: no XAML coding required.

The point of view I represent is that of someone who has graphic arts training, who is adept at Adobe tools such as Photoshop, who is an extremely heavy user of the Internet, but who knows nothing about C# or .NET. Perhaps it may be difficult for some readers to understand someone with this background, but there are a lot of us out here. There are many things that may be obvious to a developer that can only be painfully learned through trial and error by a designer. Given the relative 'youth' of the Silverlight tools and their accompanying documentation (sparse), only the Internet, powerful search engines and the sharing of the Silverlight community make solving the problems we confront relatively painless. And it always helps to know that the pain of being an early adopter of Silverlight will be compensated for by the advantages that come from being among the first to learn these skills.

Saturday, July 4, 2009

Learning RIA Services: It's Great to be a Silverlight Developer

I have built WCF integrations to SQL Server as part of building Silverlight applications. Having created the basic elements of a CRUD (Create, Read, Update, Delete) using WCF (see Tim Heuer's blog on how to do this ), I had to ask myself if there wasn't a way to achieve the same result that required less code.

During MIX09 I saw Brad Adams' talks on .NET RIA Services (watch Brad Adams talk). On 3 separate occasions I worked through these examples until I began to understand how all the parts fit together. My basic feeling was that although these services are very powerful, they made a lot of assumptions about how I wanted to collect and consume the data. Too much magic going on behind the scenes for my purposes. Using the beta release of RIA Services, according to all the demos I could find on line, there was no way to loop through the returned records without binding it to the control.

The Problem Statement

I wanted to find out what I was doing wrong. Is there a way to just loop over the records returned from RIA services without binding to a control? I asked the question on Stack Overflow and in the Silverlight forums.




The Community to the Rescue

If I knew the answer, I would have asked a better question. Isn't that always the case? The members of the developer community offered answers to my ill-formed question:



No one gave me the exact answer. There was no code provided that I could just cut and paste, but I got great response in hours and their insight led me to the answer I was looking for:



Not long after I starting working on this problem, Silverlight 3 was released. RIA services have changed and I have not had time to investigate, but once I do I will add a new post.

It is Great to be a Silverlight Developer

I have been writing software since I was 18; I would have started sooner if I had access to a computer. Looking back over my 30+ years of programming, I am now having more fun than ever, and I think it is because of the times we live in -- Silverlight, the Internet, and the community of people who use them both. Silverlight has turned web applications into a rich and now, with OOB, sometimes even unconnected experience. Today's development culture is one of generosity and sharing. I am in contact with people around the world, Silverlight gurus and novices alike, and they all love to share. You get the sense that we are all looking to see how much we can accomplish together with Silverlight.

Friday, July 3, 2009

Bing Maps Silverlight Control Part 5: Calculating Distance

My dad was the first person to show me how to measure distances on a map using a pair of dividers and the scale printed on the map key. There is a much better way to do this using the Bing Maps Silverlight control -- it just takes a little programming and finding the right equation.



Here is the Bobcat Video I told you about:



Back to Programming...

Here is how you calculate the distance between points on the earth. You also need to know EarthRadiusInMiles = 3956.0;


/// <summary>
/// Calculate the distance between two geocodes. Defaults to using Miles.
/// </summary>
public static double CalcDistance(double lat1, double lng1, double lat2, double lng2)
{
return CalcDistance(lat1, lng1, lat2, lng2, GeoCodeCalcMeasurement.Miles);
}
/// <summary>
/// Calculate the distance between two geocodes.
/// </summary>
public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, GeoCodeCalcMeasurement m)
{
double radius = GeoCodeCalc.EarthRadiusInMiles;
if (m == GeoCodeCalcMeasurement.Kilometers) { radius = GeoCodeCalc.EarthRadiusInKilometers; }
return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0)))));
}

I created a Marker class to mark the end points of the distance calculation. To show the user that they are in distance calculation mode, I have also changed the cursor on the map control. In this case, I alternate between the arrow and the pointing finger.

PointsOfInterest m_oMarkers = new PointsOfInterest();

void cbMeasure_Checked(object sender, RoutedEventArgs e)
{
Map1.Cursor = Cursors.Hand;
m_oMarkers.Clear();
}
void cbMeasure_Unchecked(object sender, RoutedEventArgs e)
{
Map1.Cursor = Cursors.Arrow;
if ( m_oMarkers.Count > 1 )
AddLine(m_oMarkers,.5);
}

The code for collecting the user's selection of points is here:


void Map1_MouseClick(object sender, MapMouseEventArgs e)
{
var oLocation = Map1.ViewportPointToLocation(e.ViewportPoint);

PointOfInterest oPOI = null;
if (cbMeasure.IsChecked == true)
{
oPOI = new Marker() { Loc = oLocation };
m_oMarkers.Add(oPOI);
}
else
{
oPOI = new PointOfInterest() { Loc = oLocation };
m_oList.Add(oPOI);
}

RenderPoint(oPOI);

if (cbDraw.IsChecked == true)
try
{
double dRadius = double.Parse(txtRadius.Text);
AddCircle(oLocation, dRadius, .5);
}
catch( Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Using a polyline and a tool tip is great way to report the distance:


double DistanceInMiles(LocationCollection oLocs)
{
double dDistance = 0.0;
for (int i = 0; i < oLocs.Count() - 1; i++)
dDistance += DistanceInMiles(oLocs[i], oLocs[i + 1]);

return dDistance;
}

double DistanceInMiles(Location Loc1, Location Loc2)
{
double dMiles = GeoCodeCalc.CalcDistance(Loc1.Latitude, Loc1.Longitude, Loc2.Latitude, Loc2.Longitude);
return dMiles;
}

LocationCollection RenderLine(List<PointOfInterest> oList)
{

var oPOIs = (from oPOI in oList
where oPOI is Marker
select oPOI).ToList<PointOfInterest>();

var oLocs = new LocationCollection();
foreach (var oPOI in oPOIs)
oLocs.Add(oPOI.Loc);

return oLocs;
}


void AddLine(List<PointOfInterest> oList, double dOpacity)
{
MapPolyline polyline = new MapPolyline();
polyline.Stroke = new SolidColorBrush(Colors.Red);
polyline.StrokeThickness = 1;
polyline.Opacity = dOpacity;

//this works in miles
LocationCollection oLocs = RenderLine(oList);
polyline.Locations = oLocs;
Map1.AddChild(polyline);

double dDist = DistanceInMiles(oLocs);

ToolTipService.SetToolTip(polyline, string.Format("{0:0.0} mi \n {1:0.00} yd", dDist, dDist * 5280.0 / 3.0));
}

Monday, June 22, 2009

Virtual Earth Rebranded to Bing Maps

We've been doing a series of posts on Silverlight 3 and Virtual Earth.

Microsoft has recently rebranded Virtual Earth, renaming it "Bing Maps". Read about their thinking here:

http://www.bing.com/community/blogs/maps/archive/2009/05/28/rebranding-microsoft-virtual-earth-to.aspx

Sunday, June 21, 2009

Bing Maps Silverlight Control Part 4: Drawing Circles To Scale

When you add scaled geometry to the Virtual Earth application you need to remember 2 things:

1) The earth is not flat, so your math needs to account for the curvature of the earth.

2) The Virtual Earth API has, among other things, MapPolygon objects, which are treated as scaled geometry. In my example, I wanted to draw a circle around a point that showed what was on the map 10 miles in any direction. This video shows my reasons why:



I pulled all the parts to build this application from the web. You can find the formulas to calculate distances on a curved surface in Wikipedia: Great Circle Distance
Tim Heuer had a post on calculating the points in a circle using Java: Drawing a Radius
In my project I converted all the code to C# and separated the calculation of locations and the generation of a MapPolygon into two functions.

void AddCircle(Location oLoc, double dRadius, double dOpacity)
{
MapPolygon polygon = new MapPolygon();
polygon.Fill = new SolidColorBrush(Colors.Green);
polygon.Stroke = new SolidColorBrush(Colors.Blue);
polygon.StrokeThickness = 5;
polygon.Opacity = dOpacity;

//this works in miles
polygon.Locations = DrawACircle(oLoc, dRadius);
Map1.AddChild(polygon);
}

public LocationCollection DrawACircle(Location oLoc, double dRadius)
{
var oLocs = new LocationCollection();

var earthRadius = GeoCodeCalc.EarthRadiusInMiles;
var lat = GeoCodeCalc.ToRadian(oLoc.Latitude); //radians
var lon = GeoCodeCalc.ToRadian(oLoc.Longitude); //radians
var d = dRadius / earthRadius; // d = angular distance covered on earths surface

for (int x = 0; x<= 360; x++)
{
var brng = GeoCodeCalc.ToRadian(x); //radians
var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));
var lngRadians = lon + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));

var pt = new Location(180.0 * latRadians / Math.PI, 180.0 * lngRadians / Math.PI);
oLocs.Add(pt);
}
return oLocs;
}

Integrating this with the current UI was straight-forward -- I just added a little more code to the actions the application was already doing. So drawing circles around the current location became this:

void btnCircle_Click(object sender, RoutedEventArgs e)
{
var oMyLoc = from oItem in m_oList
where oItem is MyLocation
select oItem;

PointOfInterest oWhereIAm = oMyLoc.First<PointOfInterest>();
if (oWhereIAm == null)
{
MessageBox.Show("Add Where Am I to the Map");
return;
}

for (int i = 1; i <= 5; i++)
{
double dRadius = i * 10.0;
AddCircle(oWhereIAm.Loc, dRadius, .15);
}
}


And drawing a circle of a given radius about any mouse click became this:



void Map1_MouseClick(object sender, MapMouseEventArgs e)
{
var oLocation = Map1.ViewportPointToLocation(e.ViewportPoint);

var oPOI = new PointOfInterest() { Loc = oLocation };
m_oList.Add(oPOI);

RenderPoint(oPOI);

if (cbDraw.IsChecked == true)
try
{
double dRadius = double.Parse(txtRadius.Text);
AddCircle(oPOI.Loc, dRadius, .5);
}
catch( Exception ex)
{
MessageBox.Show(ex.Message);
}
}


In the next post I will show how to measure between two user-selected points.

Tuesday, June 16, 2009

Virtual Earth Silverlight Control Part 3: Locating Best Buy Stores

I was searching the web for sources of Lat/Long data I could mash-up to in a Virtual Earth application and I ran across Best Buy ReMix. The API to collect store location information was easy to use and returned Lat/Long info, so I integrated it into the application. Watch this video to see how I did it:



I like to start by testing the URL in the browser:


http://api.remix.bestbuy.com/v1/stores(area(44236,20))?apiKey=[YourKey]


The Remix API returned XML describing the stores within 20 miles of my zipcode. Here is some of what I got back:


<stores currentPage="1" totalPages="1" from="1" to="6" total="6" queryTime="0.004" totalTime="0.013" canonicalUrl="/v1/stores(area("44236",20))?apiKey=fbsn9dsqvgnm42vyk4x5hman">
<store>
<storeId>758</storeId>
<name>Macedonia OH</name>
<address>470 East Aurora Road</address>
<city>Macedonia</city>
<region>OH</region>
<postalCode>44056</postalCode>
<fullPostalCode>44056-1834</fullPostalCode>
<country>US</country>
<lat>41.313461</lat>
<lng>-81.521233</lng>
<phone>330-468-6850</phone>
<hours>10:00am - 9:00pm Monday - Friday, 11:00am - 6:00pm Saturday, 11:00am - 6:00pm Sunday</hours>
<distance>6.0</distance>
</store>


The next step is to verify that Silverlight can consume this data using the WebClient Class. The code is simple but it does not always work, based on permissions (see Tim Heuer's explanation of this). I test it with this code:


void LoadBestBuyStores()
{
WebClient oClient = new WebClient();

string sUri = string.Format("http://api.remix.bestbuy.com/v1/stores(area({0},20))?apiKey=fbsn9dsqvgnm42vyk4x5hman", txtZipCode.Text);
var oAddress = new Uri(sUri);
oClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(BestBuyStores_DownloadStringCompleted);
oClient.DownloadStringAsync(oAddress);
}

void BestBuyStores_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string sResult = e.Result;
MessageBox.Show(sResult);
}



To finish the application, I needed to parse the XML that was returned and map it into an object instance, so I defined a class 'BestBuyStores':



public class BestBuyStore : PointOfInterest
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string Phone { get; set; }
public string Hours { get; set; }
}


and then added LinqToXML code to build my objects:


void BestBuyStores_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null )
return;

string sResult = e.Result;

var doc = XDocument.Parse(sResult);

var oStores = from element in doc.Descendants("store")
select new BestBuyStore
{
Name = element.Element("name").Value,
Address = element.Element("address").Value,
City = element.Element("city").Value,
Region = element.Element("region").Value,
Phone = element.Element("phone").Value,
Hours = element.Element("hours").Value,
Loc = new Location(double.Parse(element.Element("lat").Value), double.Parse(element.Element("lng").Value))
};

foreach (BestBuyStore oStore in oStores.ToList<BestBuyStore>())
{
RenderPoint(oStore);
m_oList.Add(oStore);
}

frmBestBuy.Visibility = Visibility.Visible;
frmBestBuy.ItemsSource = oStores;
}


Finally by extending the RenderPoint method I was able to mark each location with a custom Best Buy graphic:


void RenderPoint(PointOfInterest oPOI)
{
if (oPOI is MyLocation)
{
Uri oSource = new Uri(@"Pushpin.png", UriKind.Relative);
var oBitmap = new BitmapImage(oSource);
var oImage = new Image() { Source = oBitmap, Width=23, Height=27 };
m_oLayer.AddChild(oImage, oPOI.Loc, PositionMethod.BottomCenter);
}
else if (oPOI is BestBuyStore)
{
Uri oSource = new Uri(@"BestBuy.png", UriKind.Relative);
var oBitmap = new BitmapImage(oSource);
var oImage = new Image() { Source = oBitmap, Width = 23, Height = 27 };
m_oLayer.AddChild(oImage, oPOI.Loc, PositionMethod.BottomLeft);
}
else
{
var oRect = new System.Windows.Shapes.Rectangle() { Fill = new SolidColorBrush(Colors.Red), Width = 10, Height = 10 };
m_oLayer.AddChild(oRect, oPOI.Loc, PositionMethod.Center);
}
}

Tuesday, June 9, 2009

Generic Type Conversion

While working on the Virtual Earth map application, I discovered the need to parse data from a string of characters. So I searched the net and found this answer on stack overflow: generic-type-conversion-from-string . I like this code example because it looks like the simplest and most general approach:

public class TypedProperty<T> : Property
{
public T TypedValue
{
get { return (T)Convert.ChangeType(base.Value, typeof(T)); }
set { base.Value = value.ToString();}
}
}
But if you cut and paste the code into a Silverlight 2 or Silverlight 3 application it does not run. This is because the two argument version of the ChangeType method is not part of the Silverlight runtime. I guess I could have used the three argument version, but instead I implemented a function the uses reflection:


public bool TryParse<T>(string sValue, out T oValue) where T : IConvertible
{
oValue = default(T);

try
{
string[] parts = sValue.Split(':');
MethodInfo oMethod = typeof(T).GetMethod("Parse", new Type[] { typeof(string) } );
oValue = (T)oMethod.Invoke(null, new object[] { parts[1].Trim() });
return true;
}
catch { }
return false;
}
Silverlight Size vs. Functionality

You find this all the time in Silverlight development -- some of the tools, functions and features you have come to rely on in .NET are just not part of the lightweight Silverlight runtime. I read on one blog that programming with Silverlight is like going camping with .NET. In fact, I first learned this the hard way trying to recompile some previous .NET code. I couldn't include the existing library -- it had to be rebuilt using the Silverlight libraries. When I attempted to recompile it, I found that it needed to be reengineered to use functionality available in the Silverlight runtime.

I have always been able to find a creative solution to simple programming issues like the one above, and I think the ability to deliver my solution over the web to any machine, without creating a custom install, makes it worth the effort to find these alternative approaches.

Sunday, June 7, 2009

Virtual Earth Silverlight Control Part 2: Locate Me

It was a post by Nikhil Kothari that inspired me to create a method to report my current location on the Virtual Earth Silverlight control.

http://www.nikhilk.net/Silverlight-Locate-Me.aspx

While I was traveling around the country (see http://www.wiredwalkabout.com/), I would run Nikhil's application to see the result. For the most part, it always reported my location about 20 miles from where I really was, which I think is a problem with the web service used and not the code calling it.

http://api.hostip.info/get_html.php?position=true




Nikhil's current live sample has been updated, but it is still not at good as a GPS. The application source code is worth looking at in detail. He uses techniques that are more advanced than calling WebClient, and as a programmer it is interesting to know how to do same thing many different ways (viewing this link requires you have Silverlight 3 installed):

http://www.nikhilk.net/Content/Posts/SilverlightLocateMe/LocateMe.htm

This post is about integrating this functionality into the mapping application I am currently developing. I have decided to create a separate class of objects to mark my current location. I did this mainly because I wanted to use a different symbol to mark myself on the map -- like a "You Are Here" marker. I have extended this class to collect a time stamp, and this -- in combination with persistence -- can be used to track my movements.


public class MyLocation : PointOfInterest



{



public DateTime TimeStamp { get; set; }



public MyLocation()



: base()



{



TimeStamp = DateTime.Now;



}







#region Serializer Methods



public override XElement PersistTo(ModelSerializer oSerializer, XElement oSelf)



{



XElement oXElement = base.PersistTo(oSerializer, oSelf);







oXElement.Add(new XAttribute("TimeStamp", TimeStamp.ToString()));







return oXElement;



}



public override IMapModel RecoverFrom(ModelSerializer oSerializer, XElement oElement)



{



var oResult = base.RecoverFrom(oSerializer, oElement);







string sTimeStamp = oElement.Attribute("TimeStamp").Value;



TimeStamp = DateTime.Parse(sTimeStamp);



return oResult;



}



#endregion



}
I needed to modify the rendering code in order to display a different picture for this class. With this quick change, objects of type MyLocation now show a pushpin instead of a red rectangle.


void RenderPoint(PointOfInterest oPOI)



{







if (oPOI is MyLocation)



{



Uri oSource = new Uri(@"Pushpin.png", UriKind.Relative);



var oBitmap = new BitmapImage(oSource);



var oImage = new Image() { Source = oBitmap, Width=23, Height=27 };



m_oLayer.AddChild(oImage, oPOI.Loc, PositionMethod.BottomCenter);



}



else



{



// Create a Rectangle Shape to Show as the "Pushpin"



// Set it's Size to 10x10



var oRect = new System.Windows.Shapes.Rectangle() { Fill = new SolidColorBrush(Colors.Red), Width = 10, Height = 10 };







// Add Rectangle to MapLayer



m_oLayer.AddChild(oRect, oPOI.Loc, PositionMethod.Center);



}



}
The code to query the web service using WebClient follows. This is an asynchronous call that returns a string delimited by line feeds, so I used Nikhil's trick of reading from a stream to parse out and convert the Lat/Long information.

void WhereAmI()



{



WebClient oClient = new WebClient();



var oAddress = new Uri(@"http://api.hostip.info/get_html.php?position=true");



oClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(oClient_DownloadStringCompleted);



oClient.DownloadStringAsync(oAddress);



}







void oClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)



{



string sResult = e.Result;







using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(sResult))))



{



try



{



sr.ReadLine();



sr.ReadLine();







double lat = 0;



TryParse<Double>(sr.ReadLine(), out lat);







double lng = 0;



TryParse<Double>(sr.ReadLine(), out lng);







var oPOI = new MyLocation() { Loc = new Location(lat, lng) };



m_oList.Add(oPOI);







RenderPoint(oPOI);







}



catch { }



}



MessageBox.Show(sResult);



}


Monday, June 1, 2009

Using the Virtual Earth Silverlight Control - Part One

Debra and I took a trip around the country last month, 7500 miles of driving over 25 days. You can read about it at http://www.wiredwalkabout.com/.

One of the goals of the trip was to fieldtest technology, including the Virtual Earth Silverlight control. You can see the MIX09 presentation here at http://videos.visitmix.com/MIX09/T34F.

So you might be thinking, why not just do this from home? I believe that you can see the issues more clearly if you put yourself into the situations in which you expect the software to be used. After all, Pixar animators traveled to South America as inspiration for the movie "UP", and traveled along Route 66 in preparation for the movie "Cars".

Along the way we were using a Garmin to plan our trip. This is an extremely valuable tool, but it was missing some features that would have been nice to have, so I decided to investigate the Virtual Earth Silverlight control to see what it would take to reproduce/extend the features in the Garmin. I have also done Telco applications in the past and I wanted to investigate how the mapping requirement in these applications could be accomplished in Silverlight.



Since this investigation was going to be more like building an application and less like an experiment, I am starting by writing code to persist my POCO domain model (Plain Old CLR Objects). I am using the OpenFileSave feature in Silverlight 3 to save my domain model as XML. The rest of this blog entry explains my model for persistence.

For this application I am implementing my own XML serializer. The result (so far) is as follows. As the implementation grows I expect this file format to change. XML gives me some flexibility in reusing data from older files.


<?xml version="1.0" encoding="utf-8"?>
<ModelSerializer>
<PointsOfInterest>
<PointOfInterest Latitude="39.730288305639185" Longitude="-83.310517678613749" />
<PointOfInterest Latitude="34.004710072445405" Longitude="-89.814423928613749" />
<PointOfInterest Latitude="32.572126585547366" Longitude="-96.669892678613763" />
<PointOfInterest Latitude="30.436679980300028" Longitude="-98.032197366113763" />
<PointOfInterest Latitude="31.304215774927229" Longitude="-103.74508799111376" />
<PointOfInterest Latitude="33.272989725895094" Longitude="-111.96286142861376" />
<PointOfInterest Latitude="36.445446009018042" Longitude="-118.24704111611376" />
<PointOfInterest Latitude="38.845986120977358" Longitude="-120.35641611611376" />
<PointOfInterest Latitude="37.463816739355629" Longitude="-122.11422861611376" />
<PointOfInterest Latitude="41.82646221909387" Longitude="-121.71872080361376" />
<PointOfInterest Latitude="45.511996049055" Longitude="-122.24606455361376" />
<PointOfInterest Latitude="47.478111391249712" Longitude="-122.20211924111376" />
<PointOfInterest Latitude="46.3676556511152" Longitude="-117.06051767861376" />
<PointOfInterest Latitude="45.604305565410648" Longitude="-110.68844736611376" />
<PointOfInterest Latitude="44.07759180028043" Longitude="-103.52536142861376" />
<PointOfInterest Latitude="43.410903675485677" Longitude="-96.582002053613763" />
<PointOfInterest Latitude="41.924626769221476" Longitude="-89.902314553613749" />
</PointsOfInterest>
</ModelSerializer>



The PointsOfInterest and PointOfInterest classes both implement the PersistTo XML and RecoverFrom XML methods defined in the IMapModel interface. Here is the code:



public interface IMapModel
{
XElement PersistTo(ModelSerializer oSerializer, XElement oSelf);
IMapModel RecoverFrom(ModelSerializer oSerializer, XElement oElement);
}

public class PointsOfInterest : List<PointOfInterest>, IMapModel
{
#region Serializer Methods
public virtual XElement PersistTo(ModelSerializer oSerializer, XElement oSelf)
{
XElement oXElement = new XElement(GetType().Name);
oSelf.Add(oXElement);

foreach (IMapModel oIMap in this)
oIMap.PersistTo(oSerializer, oXElement);

return oSelf;
}
public virtual IMapModel RecoverFrom(ModelSerializer oSerializer, XElement oElement)
{
string sType = GetType().Name;
XElement oRefElement = oElement.Element(sType);
if (oElement.HasElements)
foreach (XElement oSubElement in oRefElement.Elements())
{
IMapModel oChild = oSerializer.Import(this, oSubElement);

PointOfInterest oPOI = oChild as PointOfInterest;
if (oPOI != null)
this.Add(oPOI);
}

return this;
}
#endregion
}

public class PointOfInterest : IMapModel
{
public Location Loc { get; set; }

#region Serializer Methods
public virtual XElement PersistTo(ModelSerializer oSerializer, XElement oSelf)
{
XElement oXElement = new XElement(GetType().Name);
oSelf.Add(oXElement);

oXElement.Add(new XAttribute("Latitude",Loc.Latitude));
oXElement.Add(new XAttribute("Longitude", Loc.Longitude));

return oXElement;
}
public virtual IMapModel RecoverFrom(ModelSerializer oSerializer, XElement oElement)
{
Loc = new Location(double.Parse(oElement.Attribute("Latitude").Value), double.Parse(oElement.Attribute("Longitude").Value));
return this;
}
#endregion
}


This is not the first time I have implemented my own serializer. Most of my applications implement a highly interrelated domain model, combining objects sourced from many data sources into a domain model with many pointers and references. I have found the additional control I get over the serialization implementation to outweigh the effort of implementation. However, before this series of posts is finished, I plan on investigating the Silverlight 3 tools for persistence in XAML, XML and JSON.
Here is the code for ModelSerializer and the save and restore methods that start the process.


public class ModelSerializer
{

#region Compute Type

public Type ComputeType(XElement oElement)
{
string sType = oElement.Name.ToString();
return ComputeType(sType);
}

public Type ComputeType(object oObject)
{
if (oObject.GetType().IsSubclassOf(typeof(Type)))
return ComputeType(oObject as Type);
else
return ComputeType(oObject.ToString());
}

private Dictionary<String, Type> m_oTDictionary = null;
public Dictionary<String, Type> TypeHash
{
get
{
if (m_oTDictionary == null)
{
m_oTDictionary = new Dictionary<String, Type>();
m_oTDictionary.Add("double", typeof(Double));
m_oTDictionary.Add("string", typeof(String));
m_oTDictionary.Add("boolean", typeof(Boolean));
m_oTDictionary.Add("bool", typeof(Boolean));
m_oTDictionary.Add("integer", typeof(Int32));
m_oTDictionary.Add("int32", typeof(Int32));
m_oTDictionary.Add("object", typeof(object));
}
return m_oTDictionary;
}
set
{
m_oTDictionary = value;
}
}
#endregion


private XDocument m_oDocument = null;
public XDocument Document
{
get
{
if (m_oDocument == null)
m_oDocument = new XDocument();

return m_oDocument;
}
set
{
m_oDocument = value;
}
}

public void Write(StreamWriter oStream)
{
Document.Save(oStream);
}

public XDocument Read(StreamReader oStream)
{
Document = XDocument.Parse(oStream.ReadToEnd());
return Document;
}


public virtual IMapModel CreateObject(XElement oElement)
{
Type oType = ComputeType(oElement);
try
{
return Activator.CreateInstance(oType) as IMapModel;
}
catch (Exception ex)
{
oType = ComputeType(oElement);
string sType = oElement.Name.ToString();
string sMessage = string.Format("Type {0} not created. {1}", sType, ex.Message);
MessageBox.Show(sMessage);
}
return null;
}

public XElement Export(IMapModel oObject, XElement oParent)
{
return SerializeObject(oObject, oParent);
}


public IMapModel Import(IMapModel oObject, XElement oElement)
{
IMapModel oResult = CreateObject(oElement);
if (oResult != null)
DeSerializeObject(oResult, oElement);

return oResult;
}

public virtual void Serialize(IMapModel oObject)
{
Serialize(oObject, Document);
}
public virtual void Serialize(IMapModel oObject, XDocument oDocument)
{
XElement oElement = new XElement(GetType().Name);
oDocument.Add(oElement);
SerializeObject(oObject, oElement);
}


public virtual IMapModel DeSerialize(IMapModel oObject)
{
IMapModel oResult = DeSerialize(Document, oObject);
return oResult;
}

public virtual IMapModel DeSerialize(XDocument oDocument, IMapModel oTarget)
{
XElement oRoot = oDocument.Root;
IMapModel oResult = DeSerializeObject(oTarget, oRoot);
return oResult;
}

public virtual XElement SerializeObject(IMapModel oObject, XElement oElement)
{
return oObject.PersistTo(this, oElement);
}

public virtual IMapModel DeSerializeObject(IMapModel oObject, XElement oElement)
{
oObject.RecoverFrom(this, oElement);
return oObject;
}

}



void btnSave_Click(object sender, RoutedEventArgs e)
{

Button oButton = sender as Button;

SaveFileDialog sfd = new SaveFileDialog()
{
DefaultExt = "xml",
Filter = "XML files (*.xml)*.xmlAll files (*.*)*.*",
FilterIndex = 1
};

bool? result = sfd.ShowDialog();
if (result == true)
{
try
{
using (StreamWriter oStream = new StreamWriter(sfd.OpenFile()))
{
ModelSerializer oSerializer = new ModelSerializer();
oSerializer.Serialize(m_oList);
oSerializer.Write(oStream);
oStream.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

void btnLoad_Click(object sender, RoutedEventArgs e)
{
Button oButton = sender as Button;

OpenFileDialog ofd = new OpenFileDialog()
{
Filter = "XML files (*.xml)*.xmlAll files (*.*)*.*",
FilterIndex = 1
};

bool? result = ofd.ShowDialog();
if (result == true)
{
try
{
using (StreamReader oStream = ofd.File.OpenText())
{
ModelSerializer oSerializer = new ModelSerializer();
oSerializer.Read(oStream);
oSerializer.DeSerialize(m_oList);
oStream.Close();
}

foreach (PointOfInterest oPOI in m_oList)
RenderPoint(oPOI);

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}


All the source code is on Codeplex:

http://vearthmappingexample.codeplex.com/