Paul Ballard's WebLog

.NET All Day.... .NET All Night. The semi-coherent ramblings of a sleep deprived mind.

May 2005 - Posts

Microsoft Watch Jounalist Sells Credibility on EBay!!!

Okay, so not really.  I just thought I'd give the Microsoft Watch policy of sensational headlines followed by a story to the contrary a shot.  It seems to work for them after all.  Just a couple of days ago they published a news item with a headline of "Ouch! .NET Framework 2.0 Breaks Apps" with a short story claiming that Microsoft has "run into a brick wall in terms of getting applications written to the .NET Framework 1.1 to work with .NET Framework 2.0".  Then when you click the link to read more about the story, you go to an E-Week.com article about Microsoft recruiting developers to test their applications for compatability.

This story is just that, a story.  Stephen King could have written it and it probably would have been more interesting (to say nothing of selling millions of copies and movie rights).  Sensational headlines should be sensational for a reason.  You want to grab the reader's attention and say "Hey, Look at This!!!"  And so the user clicks and... NOTHING!  No sensational story about the .NET Framework causing applications to break.  No millions of dollars lost to reworking applications.  Not one word in the story cited a single example.  You the reader have been "bait-and-switched" and that is an express ticket to losing all credibility.  I'm sure it helped their page views and ad sales though.

Then today, they publish an news item with the title of ".NET Developers: 'What, Me Worry'?" with a subtitle of "Developers are taking in stride Microsoft's disclosure that it is encountering compatability problems with its next-gen .NET Framework".  This article, unlike the other goes into several examples of talking with developers who are not in the least worried about the breaking changes.  And why are they not worried?  Because they know what the changes are!  They're documented in detail.  Microsoft didn't "encounter" the breaking changes, they CREATED them and they knew when they made the change that it was a breaking change.  Every developer interviewed for the article considers the breaking changes "minor tweaks".  Finding no real drama or element of controversy, the journalist does what all good journalists do, they ask an analyst.  Ugh! 

There are a lot of great parts about working for TheServerSide.NET and bringing .NET related news items to our readers.  But for all the good things, my worst fear is that I will somehow get lumped in with "journalists" like this.

Posted: May 27 2005, 10:55 AM by PaulBallard | with no comments
Filed under:
A Quick Introduction to Text-To-Speech Synthesis in .NET 2.0

When I heard that the Indigo and Avalon Beta 1 RC had the new managed Speech APIs I decided I just had to take a look.  You see, I’ve been a geek for quite a while now and when I was but a wee lad (okay, I’ll admit it I was never actually all that wee of a lad), I saw this movie called “Tin Man”.  It was a universally panned film about a deaf man who creates a speaking computer called Osgood.  Then in the late 80s or so, when the old SoundBlaster sound cards came out with passable text-to-speech I used to sit at the keyboard and let my 5 year old son talk to my computer, which of course I called Osgood.  He’d say Hello and I’d type a response and the words would come out of the computer which literally amazed my son.  Ah, the magic of fatherhood.

 

So, I’ve been interested in text-to-speech (TTS) for quite some time.  In looking at the ease at which I could get the same TTS synthesize programmatically I thought of an interesting potential use of the technology and have implemented an example which I’ll go through in this article.

 

One of the most common uses of speech synthesis is to provide a friendlier interface for novice users.  In that vane, I decided to create a Windows Forms control that would play an instructional message via TTS when the user set the focus on any form control, with the message being set by the developer at design time.

 

I started by creating a new ClassLibrary project and adding the necessary references for Windows Forms and the System.Speech API.  The Speech.DLL should be located in the C:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\ directory.  I then added a new component class called SpeechTipProvider.  By deriving the control from System.ComponentModel.Component I can use the control in the Windows Forms Designer by dragging it off the tool palette onto a Form.  Visual Studio 2005 is fortunately smart enough to add the component to the palette automatically under a project specific tab.

 

What I needed to do next was to make it possible for a developer to be able to define an instructional message for each control on a Form.  I am able to do this thanks to one of the most powerful extensions to the Windows Forms architecture; the IExtenderProvider.  This interface, and an attribute or two, allows developers to add properties to existing controls.  It is how the ErrorProvider, ToolTip, and HelpProvider controls are implemented.  The interface defines only one property, CanExtend which takes a reference to the object being tested and returns a Boolean value that tells the Designer if that control can use the extender.  Figure 1 shows the implementation of CanExtend for the SpeechTipProvider class which allows it to be used with any control.

 

 

Figure 1.

 

The next thing I did is to use the ProvidePropertyAttribute attribute to tell the Designer the name of the property I’m adding and for which types of objects.  This attribute is added at the class level.  Figure 2 shows the implementation of the ProvidePropertyAttribute for the SpeechTipProvider class.

 

Figure 2

 

The next task is to implement the actual SpeechTip property.  This property isn’t implemented as a normal property however because it requires a reference to the control that is being extended to be passed in to both the Get and Set.  Therefore, you implement this property using two methods with the exact naming standard of GetPropertyName and SetPropertyName.  So for the SpeechTip property the methods are GetSpeechTip and SetSpeechTip. 

 

The SetSpeechTip method has two parameters, a reference to the control being extended and the String value of the SpeechTip property.  When Set is called I place the String that the developer defined for the SpeechTip into a private StringDictionary with the control’s name as the key.  I then add a new EventHandler for the control’s Enter event passing in the ControlEnter method from the SpeechTipProvider class.  By wiring up a method to that event, the SpeechTipProvider class will know whenever the user enters a control for which it has a SpeechTip set.  If the value passed into the SetSpeechTip method is null or String.Empty, I remove any previously set value from the StringDictionary and remove the event handler from the control’s Enter event.

 

The GetSpeechTip method is very simple, and just returns the current value stored in the StringDictionary for the control passed into the method as a parameter.  Figure 3 shows the implementation of the Get and Set SpeechTip methods.

 

Figure 3

 

Now that all of the plumbing work for the IExtenderProvider is done, I can get to the fun part of working with the Speech APIs.  I start by adding a reference to a new System.Speech.Synthesis.SpeechSynthesizer to the class, initializing it in the constructor.  Windows XP comes with a TTS synthesizer and there are also engines installed as part of Windows XP Tablet Edition and Microsoft Office 2003.  You can download or buy others as well.  This example uses the standard Windows synthesizer. 

 

In the ControlEnter method, the implementation of TTS is very simple.  I merely cast the sender as a Control and call the synthesizer’s SpeakAsync method passing in the text tip from the StringDictionary for that control.  Figure 4 shows the source code for the ControlEnter method.

 

Figure 4.

 

Next I wanted to add some features to the SpeechTipProvider to allow the developer to specify which of the built-in voices to use for the TTS as well as allowing the developer to turn off the speech as to not irritate power users.  There are three voices installed in the standard Windows XP synthesizer named Michael, Michelle, and Sam.  Each voice has a slightly different tone to it based on gender and age.  I added an enumeration to the project called Voices for each of the preinstalled voices and then added a property to the SpeechTipProvider to allow the developer to set which voice he/she wanted to use.  In order for the property to be viewable within the Designer, I added attributes to specify that the property is Browsable and that set a Category for it in the Properties window.  In the Set for the Voice property I set the synthesizer’s default voice to the selected voice using the static InstalledVoices method.

 

I then added another property called Enabled which would allow the developer to turn off TTS at runtime.  In the ControlEnter method in Figure 4 you’ll see that I’m testing the private field associated with that property before playing the tip.  Figure 5 shows the source code for these two properties.

 

Figure 5.

 

Now that the implementation of the SpeechTipProvider is complete, I added a new Windows Form project with a Form having several controls.  I added TextBoxes for first and last names, a DatePicker control for selecting a BirthDate, and a group box with two radio buttons for selecting Gender.  I then dragged the SpeechTipProvider control off the toolbox palette and onto the form.  After doing that, I was able to use the Properties Window for each control to set the SpeechTip property.  Figure 6 shows the Properties Window with the last name Textbox selected.

 

Figure 6.

 

All that’s left to do is Build and Eecute and Osgood has once again come to life, this time smarter than ever.  The uses for the Speech APIs range from the somewhat trivial like this example to full fledged voice recognition and response systems written entirely in managed code.  Watch my blog at http://weblogs.asp.net/PaulBallard for more experiments with voice recognition and other synthesizers.

 

Download the source for this post.

Posted: May 25 2005, 03:11 AM by PaulBallard | with 3 comment(s)
Filed under:
Dallas Geek Dinner -- Beer, Games, and Hold 'Em
I attended the Dallas Geek Dinner this past Monday night here in Dallas (the bustling metropolis of Grapevine actually).  Although turnout was light, it was a lot of fun and I'm looking forward to the next one.  Next time though, we should play poker for MSDN Universal IDs. :-)
Posted: May 18 2005, 12:24 AM by PaulBallard | with no comments
Filed under:
Speaking at Visual Studio 2005 Dev Con

I'm going to be delivering the Building Databound Smart Clients talk in the Building Smart Clients with Visual Studio 2005 track at Dallas Visual Studio 2005 DevCon on June 16th.  This is going to be a great one day conference focused on Visual Studio 2005 and .NET 2.0.  The registration fee is only $99.00 (that's about $1799 less than TechEd) and you could win a Creative Zen Portable Media Center. 

Visual Studio® 2005 and the Microsoft® .NET Framework 2.0 represent the next step in the evolution of the Microsoft developer platform. Be one of the first to dive deep into its many new features including improved Web and Smart Client development, advanced Web Services functionality and improved deployment and management features.

Come and see the all new Visual Studio Team System Suite — a set of highly integrated tools that creates a collaborative team development environment. These tools address the needs of the application architect, infrastructure architect, project manager, tester, and developer.  Learn how Team System will change the way your organization designs and develops software.

Join us for a full day of in-depth presentations from Microsoft and industry experts.  Topics include:

  • New features in VS2005 and the Microsoft .NET framework
  • New features for the developer in SQL Server™ 2005
  • New Lifecycle Tools in Visual Studio Team System
  • Development using the next version of Windows®, code-named Longhorn

The Dev Con consists of three tracks:

  • Building Smart Clients with Visual Studio 2005
  • ASP.NET v2.0
  • Visual Studio Team System

Register today:
5/26/2005 St. Louis, MO (Marriott West) Agenda Detail
6/1/2005 Chicago, IL (Donald E. Stephens Convention Center, Rosemont, IL) Agenda Detail
6/1/2005 Minneapolis, MN (Hilton Minneapolis) Agenda Detail
6/1/2005 Kansas City, (Sheraton Hotel, Overland Park, KS) Agenda Detail
6/2/2005 Omaha, NE (Omaha Marriott Hotel) Agenda Detail
6/14/2005 Houston, TX (Westchase Marriott) Agenda Detail
6/16/2005 Detroit, MI (Dearborn Hyatt, Dearborn, MI) Agenda Detail
6/16/2005 Dallas, TX (InterContinental Hotel, Addison, TX) Agenda Detail
All attendees will also receive:
  • Visual Studio 2005 Team Suite Beta 2 DVD
  • The Latest SQL Server 2005 CTP
  • Visual Studio/MSDN® Transition Guide

Leave prepared!

* Offer good only to registered event attendees. Limit one of each gift per person while supplies last. This offer is not redeemable for cash. Taxes, if any, are the sole responsibility of the recipient. Free gifts must be claimed on-site; any unclaimed books will be forfeited.

** No purchase necessary. Open only to US residents. Winners selected following each event. For full rules and alternate method of entry click here.

© 2005 Microsoft Corporation. All rights reserved. Microsoft, Microsoft Press, MSDN, Visual Basic, Visual Studio, the Visual Studio logo, and Windows are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.
Posted: May 11 2005, 10:57 PM by PaulBallard | with no comments
Filed under:
Mobile Development Coolness and Smartphone Envy

So those of you paying attention may have noticed that the subtitle for my little piece o' the blogsphere is ".NET All Day.. .NET All Night..."  This would be one of those "All Night" posts as it's currently 2:32 am and I just finished going through some content for TheServerSide.NET that we'll be posting tomorrow afternoon which is generally in the realm of .NET Mobile Development.  Forgive the subterfuge, but I can't say what it is just yet but keep an eye on the site later this afternoon and you'll be sure to see it.

What I did see however was some of the new tools for Mobile .NET Development that make me question my decision to drop my way cool Audiovox SMT 5600 phone for the even cooler (if not Windows) Treo 650

I've always been a fan of Windows mobile platforms.  I've owned four different Windows CE PDAs starting with the first Cassiopeia 11-A clamshell all the way up to an IPaq 4150.  After spending months watching Microsofties of the world expound the virtues of the Audiovox phone I decided I just had to have one.  I loved the form factor of the Audiovox, it was as small as an older model Nokia but had a decent sized screen with good color.  The rectangular navigation control was a bit awkward to use but I got used to it after a while.  And of course having my Exchange email, contacts and calendar at my fingertips was a great feature.  Applications for the device were scarce though which meant anything I wanted to run on it I'd have to develop myself.  But of course the ability to do development for the phone in .NET was part of why I wanted to have it.

Unfortunately though, the Audiovox came with a fairly sizable flaw.  Cingular Wireless.  Actually it was AT&T for about three weeks before Cingular bought AT&T and the service went into the toilet.  I travel quite a bit and the reception was terrible.  It's a digital only phone and so there were some parts of the country where I had no service at all.  The final straw in this camel's back was when my phone failed to boot while traveling in California.  I took it to a local Cingular dealer where I was told that there was nothing they could do because "It's an AT&T phone".  "But you bought AT&T" I replied still they kept repeating that it was an AT&T phone like some sort of demented cockatoo.  So then I asked if they have any loaner phones I could use to get me home, to which they said no.  I then asked if they had a phone I could buy and put my AT&T Sim card into to limp myself home.  No, AT&T sims don't work in their phones.  Finally I ended up getting a cheap prepaid phone and forwarding my number to that account until I could get home and get rid of Cingular.

I then went on a hunt for the next great PDA phone and I'm pretty confident that I've found it.  The Treo 650 is a bit bigger than the Audiovox which makes it a tad less convenient for carrying in my pocket.  The screen is great though and the full QWERTY keyboard makes sending email a breeze.  I was concerned about losing the features of having my Exchange Server info available on my phone but the Treo 650 comes with Exchange ActiveSync and will connect to my server in the same way that the Audiovox did.  So I basically had parity in a slightly larger form factor.  Then I looked at software.  Wow, is there a lot of software for this phone.  I've added a Trillian like instant messaging client that allows me to use AOL, MSN, and Yahoo services.  I've added an RSS news reader that automatically keeps this and MSDN blogs downloaded.  And I've even added VPN and Terminal Services software so I can remotely connect to my servers from anywhere.  Add to that some nice games and Sprint's much better network and service and this phone is officially "The Bomb".

But alas, all is not well in Treo Land.  You see, I am first and foremost a Windows developer.  And after looking at the awesome new tools available in Visual Studio 2005 including SQL Server Mobile, the designer that uses highly detailed device images, and managed support for features like digital cameras and other devices, I'm having a hard time seeing my Treo in quite the same way.  Perhaps later this week at MEDC somebody will announce a slick new Windows Smartphone on a real network like Verizon or Sprint.

Posted: May 10 2005, 03:02 AM by PaulBallard | with 1 comment(s)
Filed under:
Using Data From SharePoint 2003 Lists

A buddy of mine asked me to help him figure out how to get the data from a SharePoint list for a project he’s working on.  I thought rather than showing him one way, I’d show him three and let him decide which method is best for his application.  Now I think I’ll share this with the rest of the world in case anybody else is doing the same sort of thing.

 

SharePoint 2003 has several web services that you can use to access information stored in a SharePoint site, all of which are located in the _vti_bin directory of a server running SharePoint 2003.  Here’s a quick list of the services available (taken from Using Microsoft Windows SharePoint Services with the Microsoft Office System)

 

 

Service

Web Reference URL

Description

Administration

http://server_name:5966/_vti_adm/Admin.asmx

Administrative methods for managing a deployment of Microsoft Windows SharePoint Services, such as for creating or deleting site collections.

Alerts

http://server_name/_vti_bin/Alerts.asmx

Methods for working with alerts for list items in a SharePoint site.

Data Retrieval Service

http://server_name/_vti_bin/DspSts.asmx

Methods for retrieving schemas and data

Document Workspace

http://server_name/_vti_bin/DWS.asmx

Methods for managing Document Workspace sites and the data they contain

Forms

http://server_name/_vti_bin/Forms.asmx

Methods for returning forms used in the user interface when working with the contents of a list

Imaging

http://server_name/_vti_bin/Imaging.asmx

Methods that enable you to create and manage picture libraries

Lists

http://server_name/_vti_bin/Lists.asmx

Methods for working with lists and list data

Meetings

http://server_name/_vti_bin/Meetings.asmx

Methods that enable you to create and manage Meeting Workspace sites

Permissions

http://server_name/_vti_bin/Permissions.asmx

Methods for working with Windows SharePoint Services security

Site Data

http://server_name/_vti_bin/SiteData.asmx

Methods used by search services to extract and crawl data from SharePoint sites.

Sites

http://server_name/_vti_bin/Sites.asmx

Method for returning information about the collection of site templates on the virtual server.

Users and Groups

http://server_name/_vti_bin/UserGroup.asmx

Methods for working with users, site groups, and cross-site groups

Versions

http://server_name/_vti_bin/versions.asmx

Methods for working with file versions

Views

http://server_name/_vti_bin/Views.asmx

Methods for working with views of lists

Web Part Pages

http://server_name/_vti_bin/WebPartPages.asmx

Methods to send information to and retrieve information from XML Web services.

Webs

http://server_name/_vti_bin/Webs.asmx

Methods for working with sites and subsites

 

To retrieve information from a List we’ll use the Lists.asmx web service and the GetListItems method in particular.  To do this, add a web reference to your application setting the URL to the location of the Lists web service.  The screen should look something like this:

 

 

After adding the web reference to your application you can use the following code to retrieve the list data from the web service as an XmlNode.

 

Private Function GetListNode() As XmlNode

    Dim RCPILists As New RCPILists.Lists

    RCPILists.Credentials = New System.Net.NetworkCredential("PaulBallard", _

"password")

    Dim node As XmlNode = RCPILists.GetListItems("Vacation Calendar", _

       String.Empty, Nothing, Nothing, String.Empty, Nothing)

    Return node

End Function

 

Let’s take a look at what the XML returned looks like when querying a list of events.

 

<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"

       xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema" xmlns="http://schemas.microsoft.com/sharepoint/soap/">

       <rs:data ItemCount="24">

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="Tulsa .NET User Group Meeting"

                     ows_EventDate="2004-09-27 18:00:00" ows_EndDate="2004-09-27 21:00:00" ows_Title="Tulsa .NET User Group Meeting"

                     ows_ID="2" ows_owshiddenversion="1" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="INETA Board Meeting in Vegas"

                     ows_EventDate="2004-10-01 16:00:00" ows_EndDate="2004-10-03 16:00:00" ows_Title="INETA Board Meeting in Vegas"

                     ows_ID="1" ows_owshiddenversion="2" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="Dallas .NET User Group" ows_EventDate="2004-10-14 18:00:00"

                     ows_EndDate="2004-10-14 21:00:00" ows_Title="Dallas .NET User Group" ows_ID="5" ows_owshiddenversion="2" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="New Orleans .NET User Group Weekend"

                     ows_EventDate="2004-10-16 09:00:00" ows_EndDate="2004-10-18 21:00:00" ows_Title="New Orleans .NET User Group Weekend"

                     ows_ID="3" ows_owshiddenversion="2" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="OOPSLA" ows_EventDate="2004-10-23 00:00:00"

                     ows_EndDate="2004-10-28 00:00:00" ows_Title="OOPSLA" ows_ID="4" ows_owshiddenversion="2" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="Oklahoma City .NET User Group"

                     ows_EventDate="2004-11-01 00:00:00" ows_Title="Oklahoma City .NET User Group" ows_ID="6"

                     ows_owshiddenversion="1" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="DevConnections" ows_EventDate="2004-11-07 00:00:00"

                     ows_EndDate="2004-11-10 23:00:00" ows_Title="DevConnections" ows_ID="11" ows_owshiddenversion="1" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="Thanksgiving" ows_EventDate="2004-11-25 00:00:00"

                     ows_Title="Thanksgiving" ows_ID="7" ows_owshiddenversion="1" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="CES - Las Vegas" ows_EventDate="2005-01-06 00:00:00"

                     ows_EndDate="2005-01-07 23:00:00" ows_Title="CES - Las Vegas" ows_ID="12" ows_owshiddenversion="2" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="Devscovery Austin" ows_EventDate="2005-01-11 00:00:00"

                     ows_EndDate="2005-01-13 23:00:00" ows_Title="Devscovery Austin" ows_ID="14" ows_owshiddenversion="1" />

              <z:row ows_fRecurrence="0" ows_Attachments="0" ows_LinkTitle="TechEd 2005 in Orlando" ows_EventDate="2005-06-03 00:00:00"

                     ows_EndDate="2005-06-11 00:00:00" ows_Description="Remember to have new liver standing by"

                     ows_Title="TechEd 2005 in Orlando" ows_ID="24" ows_owshiddenversion="1" />

       </rs:data>

</listitems>

 

Many of you may recognize this XML format as that of an ADO Recordset with the columns of the records prefixed with “ows_’.  While a relatively simple format, it’s not conducive to working with the data in .NET.  So then, here are the three examples of working with this data to fill a Windows Forms ListBox:

 

1.      Convert it to a Dataset (Easiest)

While the format of the ADO Recordset doesn’t match that of an ADO.NET Dataset, the Dataset can read the data in as XML and infer the layout of the tables.  To read the XML data into a Dataset you need to create an XmlTextReader that wraps the XmlNode returned by the web service and then call the Dataset’s ReadXml() method passing in the XmlTextReader.  Here is what that code might look like:

 

Private Sub btnGetDS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGetDS.Click

    Dim node As XmlNode = GetListNode()

    Dim xr As New XmlTextReader(node.OuterXml, XmlNodeType.Element, Nothing)

    Dim ds As New DataSet

    ds.ReadXml(xr)

    ListBox1.DataSource = ds.Tables("row")

    ListBox1.DisplayMember = "ows_Title"

End Sub

 

When you read this XML into the dataset, there will be two tables created.  The first one, at index 0, will be the “data” table and will contain one record with the ItemCount in it.  The second table will be the “row” table and will contain the data from the list with a reference to the parent “data” record.  Also remember that columns in the table will be prefixed with “ows_”.

 

2.      Access Data Using XPath Queries (Easy)

You can use XPath to locate a single element or a group of elements from the node retrieved using the XmlNode.SelectNodes and XmlNode.SelectSingleNode methods.  To search for a single element in the list the code would look something like this:

 

xpq = "//*[@ows_Title='Thanksgiving']"

Dim node2 As XmlNode = node.SelectSingleNode(xpq)

 

However, to retrieve all of the rows returned using XPath, we can’t simply use “//data/rows” because of the namespace prefixes.  You could create an XPathNavigator loading up the proper namespaces but that struck me as a lot of work.  So instead I would use this code to issue an XPath query that would return all rows.

 

Dim xpq As String = "//*[local-name() = 'data' and namespace-uri() = 'urn:schemas-microsoft-com:rowset']/*[local-name() = 'row' and namespace-uri() = '#RowsetSchema']"

Dim nodes As XmlNodeList = node.SelectNodes(xpq)

For idx As Int32 = 0 To nodes.Count - 1

   ListBox1.Items.Add(nodes(idx).Attributes("ows_Title").Value)

Next

 

3.      XmlSerialization (Not-So Easy)

It’s often preferable to work with data in terms of objects with properties.  Datasets and XML nodes are relatively large objects and take time to build where as custom business objects contain only the code you decide is there.  The key in using XML Serialization for list items are the Xml Attributes defined in the System.Xml.Serialization namespace.

To start we create the generic shell classes that every list will use.  These classes represent the “<listitems>” element and “<rs:data>” elements.  To specify how the XmlSerializer should deserialize the XML from the Web Service into one of these objects we will apply the XmlElementAttribute to each class specifying the ElementName and Namespace properties.  Here is what the code for that looks like:

 

#Region "Generic List Classes, should not need to be changed"

<XmlRoot(ElementName:="listitems", Namespace:="http://schemas.microsoft.com/sharepoint/soap/")> _

Public Class ListResults

    <XmlElement(ElementName:="data", Namespace:="urn:schemas-microsoft-com:rowset")> _

    Public Data As ListData

End Class

 

Public Class ListData

    Public Sub New()

    End Sub

    <XmlAttributeAttribute("ItemCount")> _

    Public ItemCount As Int32

 

    <XmlElement(ElementName:="row", Namespace:="#RowsetSchema")> _

    Public Items As ListItem()

End Class

#End Region

 

Next we have to specify the ListItem class that defines the columns in the list with the properties and attributes that we want to use within our application.  Any property in the XML that doesn’t have a corresponding property in the class is ignored.  In specifying this class we can also remap the column names to remove the “ows_” prefix.  Again, we’ll make heavy use of the XmlAttributeAttribute.  Here is the code for a record of an Events list in SharePoint 2003.

 

'Customize this class for your list. 

Public Class ListItem

    Private m_LinkTitle As String

    Private m_EventDate As String

    Private m_EventEndDate As String

    Private m_Recurrence As Int32

    Private m_ID As Int32

    Private m_Title As String

 

    Public Sub New()

    End Sub

 

    <XmlAttributeAttribute("ows_LinkTitle")> _

    Public Property LinkTitle() As String

        Get

            Return m_LinkTitle

        End Get

        Set(ByVal Value As String)

            m_LinkTitle = Value

        End Set

    End Property

 

    <XmlAttributeAttribute("ows_EventDate")> _

    Public Property EventDate() As String

        Get

            Return m_EventDate

        End Get

        Set(ByVal Value As String)

            m_EventDate = Value

        End Set

    End Property

 

    <XmlAttributeAttribute("ows_EndDate")> _

    Public Property EventEndDate() As String

        Get

            Return m_EventEndDate

        End Get

        Set(ByVal Value As String)

            m_EventEndDate = Value

        End Set

    End Property

 

    <XmlAttributeAttribute("ows_fRecurrence")> _

    Public Property Recurrence() As Int32

        Get

            Return m_Recurrence

        End Get

        Set(ByVal Value As Int32)

            m_Recurrence = Value

        End Set

    End Property

 

    <XmlAttributeAttribute("ows_Title")> _

    Public Property Title() As String

        Get

            Return m_Title

        End Get

        Set(ByVal Value As String)

            m_Title = Value

        End Set

    End Property

 

    <XmlAttributeAttribute("ID")> _

    Public Property ID() As Int32

        Get

            Return m_ID

        End Get

        Set(ByVal Value As Int32)

            m_ID = Value

        End Set

    End Property

End Class

 

Once the class is defined, we can use the XmlSerializer to convert the XML from the web service into an instance of this object.  Here is the code to does that and then binds the data to a ListBox.

 

Private Sub btnGetObj_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGetObj.Click

    Dim node As XmlNode = GetListNode()

    Dim xr As New XmlTextReader(node.OuterXml, XmlNodeType.Element, Nothing)

    Dim ser As New XmlSerializer(GetType(ListResults))

    Dim lResults As ListResults = CType(ser.Deserialize(xr), ListResults)

    ListBox1.DataSource = lResults.Data.Items

    ListBox1.DisplayMember = "Title"

End Sub

 

Which to Choose

Now of course the question is which one should you use?  That I leave up to you fair reader, but here are some things to consider about each approach.

 

Datasets

Datasets are large objects and take up quite a bit of memory, but can be very useful if you are planning to bind the data directly to a UI control or if you want the user to have the ability to add/edit items in the list.  You can mitigate the memory overhead by caching the list results in a multi-user environment.

 

XML

The XML produced by the web service is difficult to work with and manipulate as compared to a Dataset or business object.  XmlNode objects also take up a lot of memory.  However, if you are looking for a single item in the list, this is a very expedient way to get it as it requires no other objects be created or managed. 

 

Xml Serialization

XML Serialization is useful for occasions when you need to work with the data returned as an object and apply business rules or logic to the object while it’s being used.  Keep in mind that the data returned in the recordset format cannot be automatically converted into .NET types by the XmlSerializer.  For example, the date format used in the XML will not parse into a DateTime property.  Therefore you may need to create a wrapper around these classes to provide better type safety and usefulness.

 

Download Sample Code

Posted: May 08 2005, 11:16 PM by PaulBallard | with 10 comment(s)
Filed under:
More Posts