The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








Latest Diggs - Programming

Internet Blogs - Blog Top Sites

Site Hits

Total: 2,502,482
since: 19 Jan 2005

WPF/XAML and LINQ - A match made in heaven

posted Tue 06 Jun 06
Here is a step-by-step procedure indicating what I did in order to get this all to compile. The issue is that I can't use a C# 3.0-enabled compiler against the WPF application directly, because WPF won't compile (at least it wouldn't for me). So, the trick was to create a LINQ class library and reference it from a WPF application. Even though the LINQ class library uses C# 3.0 to compile - it still generates IL that is bound to version 2.0 of the .NET Framework - making it compatible with everything else that runs on 2.0, including WPF/WCF and WWF.
  1. Create a new WinFX Windows Application
  2. Click "File", then "Add" then "New Project" (make sure you don't just do File/New)
  3. Select the "LINQ Console" (C#) template. I had trouble getting this to show up , so I copied the "LINQ Console.zip" file to about 10 different spots in my Templates directory until it showed up ;) Three cheers for brute force!
  4. At this point you should have a WPF Windows app and a C# 3.0/LINQ app. Here's the rub: you will get ridiculous compile errors if your WPF app doesn't reference the LINQ assemblies. So, add the following assemblies to your WPF app's References:
    1. System.Data.DLinq
    2. System.Data.Extensions
    3. System.Query
    4. System.Xml.XLinq
  5. The library that I use to return a list of data to WPF actually returns data of type ObservableCollection, which has hooks in it to notify the WPF GUI when things change. In order for your LINQ class library to be able to see ObservableCollection, you need to add a reference to:
    1. WindowsBase
Now that your solution is set up, I can get to showing you the code. First, let's create the the class library. This library is going to consume a list of RSS feeds and return an aggregated list of all items, sorted descending. This essentially uses a couple of quick lines of XLinq to grab all the news you're interested in and cram it into one list.

using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Data.DLinq;
using System.Xml.XLinq;

namespace RssLibrary
{
public static class LanguageExtender
{
public static string SafeValue(this XElement input)
{
    return (input == null) ? string.Empty : (string)input.Value;
}

public static DateTime SafeDateValue(this XElement input)
{
    return (input == null) ? DateTime.MinValue : DateTime.Parse(input.Value);
}
}


public static class RssConsumer
{
static Func<XElement, XElement, XElement> selectDate = (date1, date2) => ( date1 == null) ? date2 : date1 ;

private static string[] feeds = {                    
    "http://feeds.feedburner.com/dotnetaddict"        
};

public static List<RssArticle> GetArticles(DateTime origin)
{
    XName xn = XName.Get("{http://purl.org/dc/elements/1.1/}creator");  
    XName xn2 = XName.Get("{http://purl.org/dc/elements/1.1/}date");
    var feedQuery =
        from item in GetFeedItems()                               
        select new RssArticle {
            Title = item.Element("title").SafeValue(),
            Description = item.Element("description").SafeValue(),
            Link = item.Element("link").SafeValue(),
            PublicationDate = selectDate(item.Element(xn2), item.Element("pubDate")).SafeDateValue(),
            Author = item.Element(xn).SafeValue(),
            BlogName = item.Parent.Element("title").SafeValue(),
            BlogAddress = item.Parent.Element("link").SafeValue()               
    };

    feedQuery =
        from item in feedQuery
        where item.PublicationDate >= origin
        orderby item.PublicationDate descending               
        select item;

   return feedQuery.ToList();
}


public static IEnumerable<XElement> GetFeedItems()
{
    foreach (var str in feeds)
    {
        var feed = XDocument.Load(str);
        var items = feed.Root.Element("channel").Elements("item");
        foreach (var item in items)
            yield return item;
    }

}
}
}


There are a couple of really cool things going on here. The method GetFeedItems is using the new 2.0 yield return statement to easily create results of type IEnumerable. There are two language extension methods in this file (you can read more about language extensions here) that provide some logic on handling nulls. And finally, there's an XLinq query that tears through the list of XElement objects returned by the GetFeedItems method, sorts them by date, and grabs all the pertinent information. There is only one RSS feed in the array for this demo, but you can easily add as many as you like. I've run it with about 50 RSS feeds in it without a significant slowdown. Any more than that and you'll want to look into upgrading this code to asynchronous fetching :)

Next is the fun part: The XAML. Take a look at the XAML for my main window:

<Window x:Class="WPFApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:lib="clr-namespace:RssLibrary;assembly=RssLibrary"
    Title="RSS Aggregation Viewer (WPF)" Height="410" Width="680"   
    Loaded="Window_Loaded"
    >
  <Window.Resources>
    <ObjectDataProvider ObjectType="{x:Type lib:RssArticleList}" x:Key="articleProvider" />

    <DataTemplate x:Key="articleTemplate">
      <Grid>       
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="75"/>
          <ColumnDefinition Width="125" />
          <ColumnDefinition Width="200" />
          <ColumnDefinition Width="250" />         
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="30" />
        </Grid.RowDefinitions>
          <TextBlock Text="{Binding Path=Author}" Grid.Column="0" HorizontalAlignment="Center" />
          <TextBlock Text="{Binding Path=PublicationDate}" Grid.Column="1" HorizontalAlignment="Center" />
          <TextBlock Text="{Binding Path=Title}" Grid.Column="2" HorizontalAlignment="Left"/>
          <TextBlock Text="{Binding Path=BlogName}" Grid.Column="3" HorizontalAlignment="Center" />       
      </Grid>
    </DataTemplate>
       
  </Window.Resources>
  <StackPanel>
    <ListBox Name="articleList" Height="400" Width="650" ItemsSource="{Binding Source={StaticResource articleProvider}}"
             ItemTemplate="{DynamicResource articleTemplate}">
      <ListBox.BitmapEffect>
        <DropShadowBitmapEffect Color="Gray" ShadowDepth="3"/>
      </ListBox.BitmapEffect>
    </ListBox>   
  </StackPanel>
</Window>


The ObjectDataSource resource is used to create a link between the GUI and an instance of an object. The instance, in this case, is created automatically by WPF. You can either load the data in the constructor (as I've done here, though I don't recommend it for production apps), or you can specify the name of a method to be called on the instance to fetch the data. You can also programmatically obtain the instance and then manually associate it with the object data source, as I did with some of my earlier WPF examples based on the Feb CTP.
This little bit of code:
xmlns:lib="clr-namespace:RssLibrary;assembly=RssLibrary"
is the magic gem that tells WPF that the object type to which I am binding is in the RssLibrary namespace, which can be found in the RssLibrary assembly. The only thing left to do is to create the RssArticle class, which is just a collection of properties, and the RssArticleList class, shown below:
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Text;

namespace RssLibrary
{
    public class RssArticleList : ObservableCollection<RssArticle>
    {
        public RssArticleList()
        {
            List<RssArticle> theList = RssConsumer.GetArticles(DateTime.MinValue);
            foreach (RssArticle article in theList)
            {
                this.Add(article);
            }
        }
    }
}


This is the object to which the GUI is bound. When it is first instantiated, it fetches all the feed items using the RssConsumer class (which in turn uses XLinq!!) and self-populates its own ObservableCollection instance. This gives WPF the data it needs to bind to the interface and generate the output shown below:

WPF Interface bound to data retrieved from XLinq

I shouldn't need to tell you just how unbelievable the impact of this is. Couple the extremely powerful GUI/rendering capabilities of WPF with the easy, powerful, re-usable, scalable binding syntax, and add to that the ability to retrieve data from anywhere using DLinq, LINQ, or XLinq - and you've got one unbelievable combination that I think is the future of data-driven development on the Windows Vista platform - and you can quote me on that.

tags:                  

links: digg this    del.icio.us    technorati    reddit




Tag Related Posts

Building Model Classes in C# and Cocoa

Sun 15 Jun 08 3:13 P GMT-05
tags:            

CLINQ v1.1.0.0 Released!

Fri 02 May 08 5:38 P GMT-05
tags:          

CLINQ v1 Demo - Network Message Filtering

Wed 09 Jan 08 7:47 P GMT-05
tags:        

LINQ to REST - A much better name for Astoria

Tue 11 Dec 07 1:23 P GMT-05
tags:        

Unexpected and Unsafe behavior in LINQ

Wed 14 Nov 07 8:09 P GMT-05
tags:    

Building a Ledger Style for WPF Grids

Tue 21 Aug 07 3:30 P GMT-05
tags:    

Continuous LINQ - Can I write games with it?

Mon 13 Aug 07 3:09 P GMT-05
tags:        

Continuous LINQ

Mon 06 Aug 07 1:21 P GMT-05
tags:    

Dynamic, Observable LINQ Views

Tue 31 Jul 07 1:21 A GMT-05
tags:        

The dreaded language bleed-over has begun

Tue 19 Jun 07 6:23 P GMT-05
tags:        

My first "Acropolis" Application

Mon 04 Jun 07 1:40 P GMT-05
tags:      

Exploring the Delegate Design Pattern

Mon 14 May 07 6:30 P GMT-05

Installing Orcas Beta 1 - VMware Style

Mon 23 Apr 07 12:16 P GMT-05

Will Silverlight be DOA?

Mon 16 Apr 07 8:02 P GMT-05

Exploring the MVC Pattern in WPF

Tue 10 Apr 07 12:51 P GMT-05
tags:                      

Authorness

Thu 15 Mar 07 1:44 P GMT-05

WPF Bindings == WTF Bindings?

Mon 12 Mar 07 6:31 P GMT-05

On MUDs

Thu 08 Mar 07 5:00 A GMT-05
tags:                    

Visual Studio "Orcas" - March CTP is Available

Wed 28 Feb 07 12:28 P GMT-05
tags:            

Objective-C Categories vs C# 3.5 Language Extensions

Mon 26 Feb 07 1:05 P GMT-05
tags:                

Cocoa Programming vs. WPF : NIB vs XAML

Tue 20 Feb 07 2:09 P GMT-05

Cocoa Bindings vs. WPF Binding

Thu 15 Feb 07 5:41 P GMT-05
tags:                

Ulysses Agenda Makes Redmond Developer News

Wed 29 Nov 06 7:10 P GMT-05
tags:                

Is the continuous beta the new model for Vista?

Tue 21 Nov 06 8:51 P GMT-05
tags:              

Ulysses Agenda - Network Engine Test 1

Mon 09 Oct 06 2:26 A GMT-05
tags:              

Ulysses Agenda : First Cut Networking Design

Thu 14 Sep 06 12:46 A GMT-05
tags:                  

First Impressions of Windows Vista RC1

Thu 07 Sep 06 1:30 P GMT-05
tags:                      

Localizing a WPF Application

Tue 22 Aug 06 11:39 A GMT-05
tags:            

WPF Slide Show and Photo Album

Fri 18 Aug 06 6:48 P GMT-05

Is Windows Workflow Foundation Too Complex?

Fri 18 Aug 06 12:15 P GMT-05

ADO.NET Entity Framework Announced Today!

Wed 16 Aug 06 11:08 A GMT-05