An Introduction to KnockoutJS for XAML Developers

December 17, 2011 at 6:58 PMAdministrator

I've recently been working with KnockoutJS and have found it to be a very comfortable paradigm to use in constructing a web site.  Having worked with XAML a good bit over the last few years, the benefits of MVVM have been made apparent to me time and again and the approach is familiar.

KnockoutJS lets you use very nearly the same MVVM approach you're used to using in WPF or Silverlight development.  I've attached a sample application, below.  Let's have a look at it.

If you run the sample application, you'll see the following web page open in the browser.

RunningApp

There is an editable table that allows the user to edit existing contacts. Clicking the Add Contact button adds a new blank row to the table.

The ViewModel

The project contains a Javascript file named ViewModel.cs, in the Scripts directory.  The contexts of this file are listed below.

   1:  var viewModel = {
   2:      contacts : ko.observableArray([
   3:          { firstName : "Bob", lastName : "Marley" },
   4:          { firstName : "Larry", lastName : "The Cable Guy" },
   5:          { firstName : "Maxwell", lastName : "Smart" }
   6:      ]),
   7:   
   8:      addContact : function() {
   9:          this.contacts.push( { firstName : "", lastName : "" } );
  10:      }
  11:  }
  12:   
  13:  $(document).ready(function() {
  14:      
  15:      ko.applyBindings(viewModel); 
  16:  })
 
 

In the first code block, lines 1 – 11, we create a view model.  In WPF or Silverlight, out view model would contain properties and commands that we could bind to in our view.  The same is true with KnockoutJS.  Here, we have a property that is a collection of contacts and a function (command).  In lines 13 – 16, we ask Knockout to parse our view and connect the data bindings to our view model.

Notice that our property is an instance of an observableArray.  In XAML data binding, we have the concept of  the view and view model communicating with one another where events are raised when values change so that data and the controls bound to it stay in sync.  This same functionality is encapsulated in the observableArray for arrays and observable for simple properties.    So, modifying a bound property from the view model will cause a control in the view that is bound to the property to change, as well.  The reverse is also true, modifying the contents of the control will update the view model property.

The View

   1:  @{
   2:      Layout = null;
   3:  }
   4:   
   5:  <!DOCTYPE html>
   6:  <html>
   7:  <head>
   8:      <title>Index</title>
   9:      <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script>
  10:      <script src="../../Scripts/jQuery.tmpl.js" type="text/javascript"></script>
  11:      <script src="../../Scripts/knockout-1.2.1.js" type="text/javascript"></script>
  12:      <script src="../../Scripts/ViewModel.js" type="text/javascript"></script>
  13:  </head>
  14:  <body>
  15:      <div>
  16:          <fieldset>
  17:              <legend>Email Addresses</legend>
  18:   
  19:                  <table>
  20:                  <thead>
  21:                      <tr>
  22:                          <th>First Name</th>
  23:                          <th>Last Name</th>
  24:                      </tr>
  25:                  </thead>
  26:                  <tbody 
  27:                    data-bind='template: { name: "contactRowTemplate", foreach: contacts }'>
  28:                  </tbody>
  29:              </table>
  30:   
  31:              <button data-bind="click: addContact">Add Contact</button>
  32:   
  33:              <script type="text/html" id="contactRowTemplate">
  34:                  <tr>
  35:                      <td><input class="required" 
  36:                          data-bind="value: firstName, uniqueName: false"/></td>
  37:                      <td><input class="required" 
  38:                          data-bind="value: lastName, uniqueName: false"/></td>
  39:                  </tr>
  40:              </script>
  41:   
  42:          </fieldset>
  43:      </div>
  44:  </body>
  45:  </html>
 

We start by setting up our html table.  All probably looks familiar up to line 27.  In Xaml, we use Binding expressions to state what we want to bind to and how that binding is to behave.  KnockoutJS uses the "data-bind" attribute to specify these bindings.  In line 27 a binding is expresses that says, "for each item in the collection of contacts on the view model, create and populate an instance of the template named 'contactRowTemplate'".  You can see the template definition in lines 33 – 40.

Compare this binding with a similar Xaml binding and you'll find:

  • It's possible to do an ItemsSource type binding in KnockoutJS where an item is generated for each entry in an array.
  • Adding or removing an item from the array causes row corresponding to that item to appear or disappear respectively.
  • A template can be specified for how each item in the list will be constructed.  Compare this with the ability to specify an ItemTemplate in Xaml (note that you can organize the container of the list as you wish which is very similar to ControlTemplate's.
  • In Xaml, the data context flows in a predictable way through all an item's sub-items.  Note that the binding on the <tbody> element is to an array and the bindings inside the template are to a single item in the array.  This behaves the same as in Xaml.

The button in line 31 uses a click binding to bind the click event to the function we defined on our view model.

Yes, the binding syntax is different and there are many more binding types available than are used in this simple sample, but the library is very well documented here and the familiarity of the MVVM approach to constructing a UI make KnockoutJS a very easy library to get started with.

Download File - KnockoutSampleSolution

Posted in: .Net | ASP.Net | JavaScript | Silverlight | WPF

Tags: , , , ,

Dynamically Loading XAML

November 14, 2011 at 4:13 AMAdministrator

Occasionally it's useful to be able to load a piece of XAML as a string. This allows you to be able to decide, at runtime, what the UI will look like. This XAML can be composed beforehand and stored away in a file or the database, or it can be generated on-the-fly at runtime. In any case, the XAML can be a simple string and loaded into the the visual tree at runtime.

There are a few things you'll need to keep in mind, however.

Remove the Class attribute

The Class attribute is specified on the root element of a XAML document and specified the code file containing the code-behind code that is associated with the document. If this attribute is specified, it is expected that the class exists. Using code-behind kind of defeats the purpose of dynamically loading your XAML.

Explicitly Specify Namespaces and Assemblies

A good rule of thumb is that if you have to add a namespace to your document in order to be able to access its contents, you need to also specify the assembly containing the namespace. For example:

xmlns:cnvtr="clr-namespace:MyConverters.Sub.Xaml.Converters;assembly=MvcConverters"

 

Load the XAML

Once you have clean XAML in a string format, you can load it as below:

            var stringReader = new StringReader(value);
            var xmlTextReader = new XmlTextReader(stringReader);
 
            var control = XamlReader.Load(xmlTextReader);

In the above, "value" is a string that contains the XAML. We set it ass the source of a StringReader, which is set as the source of an XmlTextReader. This is passed into the XamlReader's Load method. Here, the XAML is parsed and a visual tree is constructed.

The Load method returns the root element of your control as a type "object". All that's left to do is to either set the control's DataContext or inject it into an already existing visual tree through, say, binding.

Posted in: .Net | WPF

Tags: , , ,

View PDF files in Silverlight Applications

August 12, 2010 at 8:27 AMmgordon

I’ve amassed quite a collection of ebooks in PDF format and I recently had the idea to make them available from my server at home over the internet so I could access them from wherever I happened to be.  I wanted to write the application in Silverlight, but how to actually view the PDF’s?

The solution to this problem is possible because of the tight integration between Silverlight and the browser DOM.  Recall that if I enter the URL of a PDF document into the browser address bar, and I have a PDF viewer installed, an instance of the PDF viewer is created in the browser and it renders the PDF document.  We can use this behavior to render the PDF and make it look as though the PDF document is being viewed from the Silverlight application.

 

The HTML

Since we want to be able to leverage the browser’s HTML rendering behavior to show the PDF file, we need to create some HTML for the browser to render.  In the page that’s hosting the Silverlight application, create an iframe or div tag:

 

 

<iframe id="pdfFrame" style="visibility:hidden; position:absolute"><b>No Content</b></iframe>

 

 

Control Via Silverlight

No, in the Silverlight application, you need to create a control to “host” the IFrame.  You can’t actually insert the IFrame into the Silverlight visual tree, but we’re going to tie the IFrame and this control together in such a way that it looks like this control is hosted inside our application.  For our purposes, a grid will do nicely.

 

<Grid x:Name="LayoutRoot">
        <Grid.Background>
            <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
                <GradientStop Color="#FF101010" Offset="0.98299998044967651" />
                <GradientStop Color="Black" Offset="0" />
                <GradientStop Color="White" Offset="0.546999990940094" />
            </LinearGradientBrush>
        </Grid.Background>
        <Grid x:Name="pdfHost" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
              Margin="10" Background="White" LayoutUpdated="Grid_LayoutUpdated">
        </Grid>
    </Grid>

 

Notice that I’ve hooked the LayoutUpdated event.  In the handler for this event, we can tie the size and shape of our IFrame with that of the grid.

 

 

private void Grid_LayoutUpdated(object sender, EventArgs e)
       {
           ShowPreview();
       }
       private void ShowPreview()
       {
           GeneralTransform gt = pdfHost.TransformToVisual(Application.Current.RootVisual as UIElement);
           Point offset = gt.Transform(new Point(0, 0));  // Here you find your Panel Top/Left position
           int controlLeft = (int)offset.X;
           int controlTop = (int)offset.Y;
           HtmlElement m = HtmlPage.Document.GetElementById("pdfFrame");  // Find your HTML DIV
           if (m != null)
           {
               m.SetStyleAttribute("left", controlLeft.ToString() + "px");  // Set the Div position
               m.SetStyleAttribute("top", controlTop.ToString() + "px");
               m.SetStyleAttribute("visibility", "visible");
               m.SetStyleAttribute("height", pdfHost.ActualHeight.ToString() + "px");
               m.SetStyleAttribute("width", pdfHost.ActualWidth.ToString() + "px");
               m.SetStyleAttribute("z-index", "1000");
           }
       }

Here, you find the location of your grid and set the style of the IFrame to match its position and size.  In effect, every time the grid is resized or repositioned, the IFrame will follow it.  Notice how we use the HtmlPage class to access the host page’s DOM and find our IFrame.

 

Show the PDF

No that we have the IFrame tied to our grid, how do we actually show the PDF in the IFrame?

 

HtmlElement el = HtmlPage.Document.GetElementById("pdfFrame");
           el.SetProperty("src", "showpdf.aspx?key=" + key.ToString());

First, get a reference to the IFrame, again, and then set it’s src property.  Here, I have an aspx page that pulls the pdf file from another location and streams it to my IFrame.  If the PDF is located within your web directory structure, you can bypass the aspx page and just specify the path to you pdf file.

Posted in: Silverlight | ASP.Net | PDF

Tags: , ,

Three Ways to Handle Silverlight Asynchronous Service Calls

March 16, 2010 at 8:48 AMmgordon

In Each of the examples, below, I’ll show the code that in the ViewModel and also in a service layer class that handles talking to the actual service.

One – Callback

public class ViewModel
{
    var _dataService = new DataService();

    private void GetData()
    {
        _dataService.GetCustomers(GetCustomersCallback);
    }

    private void GetCustomersCallback(object sender, GetCustomersCompletedEventArgs e)
    {
        Customers = e.Result;
    }
      
}

 

public class DataService
{
    var _client = new MyWCFServiceClient();

    public void GetCustomers(EventHandler<GetCustomersCompletedEventArgs> callback)
    {
        _client.GetCustomersCompleted += new EventHandler<GetCustomersCompletedEventArgs>(callback);
        _client.GetCustomersAsync();
    }
}

 

This approach really just extends the model that is generated in the proxy file for the service.  The ViewModel is called back on the callback method and the results of the call are available there for use.  To use a lambda expression and avoid having to write the separate callback method, you could do something like the below in the ViewModel.

 

public class ViewModel
{
    var _dataService = new DataService();

    private void GetData()
    {
        _dataService.GetCustomers( (o, e) =>
        {
            Customers = e.Result;
        });
    }
}

 

Two – Event

public class ViewModel
{
    var _service = new DataService();

    public ViewModel()
    {
        _service.CustomersLoaded += CustomerLoadCompleted;
    }

    private void CustomerLoadCompleted(object sender, ObservableCollection<Customer> customers)
    {
        Customers = customers;
    }
}

 

public class DataService
{
    var _client = new MyWCFServiceClient();

    public event EventHandler<<ObservableCollection<Customer>> CustomersLoaded;

    public DataService()
    {
        _client.GetCustomersCompleted += GetCustomersLoadedHandler;
    }

    void GetCustomersLoadedHandler(object sender, GetCustomersCompletedEventArgs e)
    {
        if (CustomersLoaded != null)
        {
            CustomersLoaded(sender, e.result);
        }
    }
    public void  GetCustomers()
    {
        _client.GetCustomers();
    }
}

 

Here, we let the data service raise an event to the ViewModel when the service call is completed.  This approach doesn’t gain us much unless there are multiple objects that need to be notified when the service call completes.

 

Three – Fake Synchronous Call

 

public class ViewModel
{
    var _service = new DataService();

    public ViewModel()
    {
        Customers = _service.GetCustomers();
    }

}

public ObservableCollection<Customer> GetCustomers()
{
    ObservableCollection<Customer> customers;

    _client.GetCustomersCompleted += (o, e) => 
    {
        customers = e.Result;
        re.set;
    }

    _client.GetCustomersAsync();
    re.WaitOne(1000);
    return customers;
}

 

So, here we’re using a ManualResetEvent to wait for the service call to complete.  We’ll wait for one second for the event to be set before we abandon the results and just return.  Use caution when using this approach as blocking a browser thread will totally freeze the UI.

Posted in: Silverlight

Tags:

The Value of Converters

December 1, 2009 at 5:55 AMmgordon

Over time, I’ve discovered more and more uses for value converters in my Silverlight and WPF applications.  In this post, we’ll look at what they are and some scenarios where they are useful.

What are value converters

If you’ve done any development in Silverlight or WPF, you know that when using these technologies, the user interface is specified in XAML, a dialect of XML.  Since we are using XML, or essentially strings, to defined our GUI the XAML parser needs to know how to turn the strings we specify into types.  For example, when we specify at attribute on a control such as IsEnabled=”True”, there needs to be away for the string “True” to be understood as a Boolean value.  When we say Background=”Black”, the string “Black” needs to be understood as Brush of color #FF000000. 

Both of these examples are common occurrences so there are built in converters that take care of these.  However, in our applications, there are likely going to be cases where we need to specify a value that the XAML parser has no idea how to interpret.  For these cases, we can create our own value converter to specify what type a string needs to be converted to.

An Example

Suppose you have a DataGrid in your UI that contains orders.  Each of these orders has a status property and you want to display an icon in each row that depicts the status of the order.  The grid is bound to a collection in the ViewModel.  How do you achieve this?

 

First, you’d need to define the column for the icon.  Perhaps, something like below.

<data:DataGridTemplateColumn Header="Status" CellTemplate="{StaticResource statusTemplate}" 
                                            IsReadOnly="True" Width="50" 
                                            CanUserSort="False"/>

 

Here, we specify the column’s layout in a template, statusTemplate.

<DataTemplate x:Key="stausTemplate">
    <Image Height="32" Width="32" Source="{Binding StatusCode, Converter={StaticResource statusToImage}, 
        Mode=OneWay}"/>
</DataTemplate>

 

In our template, we’re specifying an Image element, but binding its Source property to the status code.  Somehow, that status code has to be converted into a reference to an image file.  To handle that conversion, we specify a converter in our binding.  Let’s look at how to implement that converter.

To start, we need to create a new class and have it implement the interface IValueConverter.  Our class would look like this.

public class StatusToImageConverter : IValueConverter
    {

        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public object ConvertBack(object value, Type targetType, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

 

Implementing the IValueConverter interface requires us to implement two methods, Convert and ConvertBack.  The Convert method will be called to convert the original value to the desired value.  In our example, this method will be responsible for converting the status code to and image reference.  The ConvertBack method, does just the opposite.  If the converter is used in a TwoWay binding, the ConvertBack method will be called to convert a value specified back into a value of the original type.  In other words, if in our example the user were able to select an image, that image would be converted back into a status code before being pushed back into the order.

In our case, we’re only interested in using the converter in a OneWay binding, so we can leave the ConvertBack method un-implemented.  The finished class might look like the below.

public class StatusToImageConverter : IValueConverter
    {

        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            char statusCode = value.ToString()[0];

            switch (statusCode)
            {
                case 'A':
                    return "/Images/active.png";
                case 'W':
                    return "/images/working.png";
                case 'F':
                    return "/images/filled.png";
                case 'P':
                    return "/Images/paid.png";
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, 
            System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

 

With the converter built, we just need to reference it in our XAML as a resource.

<converters:StatusToImageConverter x:Key="statusToImage"/>

 

Other Uses

What if in our grid, we also have a column that contains a dropdown containing the shipper that we will be using to ship our order.  The ItemsSource for the dropdown is bound to a collection of Shipper objects and its SelectedValue property is bound to a ShipperId.  That shipperId needs to be converted into a Shipper object in order for the binding to work.  Using what we know about value converters, we can create a converter that takes the id and looks up the corresponding object in a list.

Converters are incredibly flexible and powerful allowing you to inject logic into your UI easily.     

Posted in: Silverlight | WPF

Tags: ,

More about Silverlight RawFaults

July 31, 2009 at 3:32 PMmgordon

While writing the Silverlight applications at FunkyTools.com, I quickly found that not being able return exceptions from my WCF calls was a huge problem for me.  I located a solution that had been released by the Silverlight WCF team and, at the time, learned just enough about the code to get it integrated into my solution.  I documented how to use the code in a previous blog post. Recently, however, I’ve taken the time to dig into the code and learn more about how it works.

 

Why Faults Don’t Get Returned To Silverlight In The First Place

When Silverlight makes a WCF call, the communication with the server does not happen from the Silverlight application.  Rather, Silverlight uses the browser API to originate the call and the response comes back to the browser, first, and it passes it back to our application.  When an exception occurs in the WCF service, the return HTTP Response Code is changed from 200 to some other value to indicate the call was problematic.  If a response code other than 200 is returned to the browser, Silverlight gets handed back a “Not Found”" Exception from the browser and the actual exception information is lost.

 

The Sample Solution

The sample solution contains 4 projects.

  • SilverlightFaultBehavior – Defines a behavior that is added to a customer binding we can use to enable to have out exceptions returned.
  • SilverlightMessageInspector – Defines a new Channel and Binding for use in the solution.  This Project wires up the Channel, Binding,  and behavior.  These first two projects define all that’s needed to set up the server side of the functionality we desire.
  • SilverlightRawFaults – Defines a single class that is of interest which is a message inspector – more on this in a second.
  • SilverlightRawFaults.Web – A test web site to host the sample service.

 

SilverlightFaultBehavior Project

This project defines a single class, SilverlightFaultBehavior, which extends BehaviorExtensionElement and IEndPointBehavior.  Basically, it adds an additional behavior that we can tie to our bindings.  In this class, there is a method IDispatchMessageInspector.BeforeSendReply that contains the following code:

 

if (reply.IsFault)
{
   HttpResponseMessageProperty property = new HttpResponseMessageProperty();
   property.StatusCode = System.Net.HttpStatusCode.OK; // 200

   reply.Properties[HttpResponseMessageProperty.Name] = property;
}

 

So, basically, just before the response is returned to the browser, this behavior sets the return code to 200 if it’s any other value.

 

SilverlightMessageInspector Project

This project is basically a plumbing project.  It composes the behavior, channel and the message inspector into a binding, which when used, is pre-configured to return the exceptions we desire.

 

SilverlightRawFaults Project

This project contains file SilverlightFaultRawMessageInspector.cs and defines the two classes needed on the client side.  The file contains two classes; RawFaultException, a custom CommunicationException and SilverlightFaultMessageInspector which implements IClientMessageInspector.  In the latter of these classes a method, AfterReceiveReply, contains this code:

 

if (reply != null && reply.Version == MessageVersion.Soap11)
{
    if (reply.IsFault)
    {
        throw new RawFaultException(reply.GetReaderAtBodyContents());
    }
}

 

So, when the reply is received by Silverlight, but before it is returned to our application, this method checks to see if the reposonse contains a Soap 1.1 Fault and if it does, and instance of the custom CommunicationException is created that contains the fault details (error code, message and stack trace) and that exception instance is thrown into our application.

 

So, our response is inspected on the server and its response code is set back to 200 so the browser will interrupt the flow of our fault.  Once Silverlight gets the response, our client side message inspector checks to see if a fault is contained in the response and if so, the fault is converted into an exception and thrown back to the method in our application that originated the call.  

Posted in: .Net | Silverlight

Tags: ,

Coloring a ListBoxItem In Silverlight

July 6, 2009 at 10:47 AMmgordon

I was recently trying to display ListBoxItems with different colors based on criteria in the items the list was bound to.  More specifically, the challenge was not in changing the background colors, but with getting the color to fill the entire item.  After much searching, I found a blog post, here, by Ric Robinson that reminded me that I already knew how to fix the problem.

 

Why Setting HorizontalContentAlignment Doesn’t Work

As Ric explains, in the default ControlTemplate the alignment for the item is hard coded to “Left” which means that no matter what you do with the properties of the ListBox or ListBoxItem, the contents of the item will never stretch to fill the container.

 

The Solution

Since Silverlight allows you to override the style of any control, the solution is to create a style that is an exact copy of the default style and modify the HorizontalAlignment property to bind to the HorizontalContentAlignment value specified on the ListBox.  While I was at it, I modified several of the style attributes to suit my liking.

Posted in: Silverlight

Tags:

Funkytools.com is Live

June 28, 2009 at 10:18 AMmgordon

I’ve been working for months on a Silverlight application for task management that draws from the Getting Things Done philosophy among others.  I’ve created a web site to host the application at http://www.funkytools.com.  If you get the chance and are so inclined, check it out.

Posted in: General | Silverlight

Tags: ,

Silverlight and WCF Serialization Woes

December 29, 2008 at 4:58 AMmgordon

So, it's been a good while since my last post.  It's been a crazy several months and time has been scarce to devote to blogging, but my situation has changed and I'm back in the saddle.

After spending the past couple of years working on .Net 2.0 project, I've spent the last few months playing catchup on some of the latest fodder to roll out of Microsoft...Linq, Silverlight and WCF.  I have a long way to go on each, but with time and a bit of effort I should be caught up in good time.  As is my usual style, I'll be posting about the stumbling blocks I encounter and how I managed to either solve or mitigate them.  This first post concerns a problem I faced working with WCF services from a Silverlight project.  Here are the details.

Being a newbie to Silverlight, I cruised over to Microsoft's Silverlight web site, watched the videos and read the tutorials before getting started.  I felt I had a pretty good grasp on things and started work on an idea I had.  I created a Silverlight project and allowed Visual Studio to create a web project for me, as well.  I, then, added a third project to host my service layer which would be made up of Linq to Sql calls, so I also created the dbml file, there, that represented my database.  The idea was that the Silverlight project would call WCF services hosted in the web project which would in turn call methods in the Service layer to query and effect changes in the database.

I started work and created several methods on my WCF service.  Each time I added functionality to the service, I would right-click the service reference and select "Update Service Reference" to allow the new methods to be generated in my proxy (I'm aware that using Visual Studio to generate WCF client code is emerging as an anti-pattern.  I will, at some point, replace the generated code with code of my own creation...stay tuned for more on this).  After several days with no problems, I repeated this process again and had several warnings raised saying that no proxy could be generated and that my service was not Silverlight compatible.  Hmmmm.

I rechecked all the classes I was sending over the wire to make sure the right serialization attributes were present.  I ran svcutil.exe against my service and it generated a proxy, just fine.  As a last ditch effort, I recreated the web project tried again only to have the problem reoccur.  I was truly stumped.  So, I reverted my service reference code to a good state and continued to look around and found nothing until I had a revelation.  When you right click the service reference, there is an option to "Configure Service Reference".  This opens a dialog that allows you to tweak the settings for the service that control what generic classes among other things.

servicerefconfig

In the lower part of the box, you are allowed to chose which types to reuse.  Best I can tell, this reuse involves serialization since the types would be "reused" on both sides of the service binding.  Upon inspection, I found that some Telerik assemblies were included in the list.  If I chose the lower radio button and unselected the Telerik assemblies, my service reference was generated properly.  Apparently, some of the types in one or more of these assemblies could not be serialized.

This is an example of the kind of thing that can happen and the frustration it can cause when you know just enough about a technology to be dangerous.  I'll be working hard to remedy this in the coming days.

Posted in: .Net | Linq | Silverlight | WCF

Tags: , , ,