On Learning New Technology – Controls Toolkit & Examples

 ScottPDC I had a great PDC.  I arrived with a schedule filled with sessions I intended to go to, and (shhhhh!) I skipped them all (except, ScottGu’s keynote and Scott Hanselman’s BabySmash both of which were (predictably) great). 

The reason I skipped all the others (and there were a lot of excellent talks that I will now have to settle for seeing the recordings)  is that I had the singular opportunity to not only talk with, but truly engage with some amazing developers. 

Microsoft puts on 3 major shows a year, and PDC is the most “forward looking” of the three, which means, ultimately, that a lot of what you see at PDC you probably can’t put into production code today.  Given that and the “all in” cost of attending (admission, transportation, accommodations and missed work) it tends to attract some very serious geeks. And that can make for some very interesting conversations on any number of topics.

Terse, Complex or Dead Simple?

One topic that came up a lot was the universal sense of being overwhelmed by the sheer volume of new technology, and how each developer coped with learning all the new material. This lead to some fascinating discussions about how technology is taught, documented, and explained. It was very clear that there would never be consensus on the "best" method; there can be no one best way to teach because there  is no single learning style. That said, there did seem to be two schools of thought about examples. One group seemed to strongly favor real-world examples even at the cost of a little complexity;  the other to strongly favor incredibly simple examples even at the cost of being overtly artificial.

It is clear that I favor dead-simple examples, and I can trace that directly to learning to program what turned out to be a terrific ISAM package in C in the mid 1980s. The problem, though was in their documentation. Each of their examples was incredibly cool, but amazingly opaque. It was only after I stripped away all the indirection and programming elegance that I realized that there were just half a dozen relatively straight-forward function calls to manage. I ended up creating my own set of examples that were 1/10 the size of theirs and 10 times easier to grok. It was a lesson that is seared into my writing center.

Revisiting the Controls Toolkit Examples

The incredible folks in Shawn Burke’s group have released the Silverlight Control Toolkit and it comes with what I consider to be really terrific documentation, plus source code plus examples and so Bob’s Your Uncle and you’re done.  But while I really like what they’ve done, I’m going to take a shot at revisiting some of the samples to really tear it down to the simplest and most visible moving parts, and along the way I’ll also take a look at using the controls in both Blend and Visual Studio.  This will be an on-going effort, over a few weeks and depending on your comments and what else comes up, we’ll take some detours and delve into some of the nuances of how these controls can be used in unexpected ways.

Please click here to download the Controls Toolkit

I set out today to begin work on the AutoComplete control, which has all sorts of interesting nooks and crannies, but I quickly discovered that I wanted to create my own data to explore this control (and others), and this is where ADD is either a curse or a blessing, because I spent quite a bit of time spelunking through the open file dialog, and the scroll viewer (not to mention deciding on the right kind of generic collection.  So, since it is the weekend, let’s actually start there and we’ll get to the AutoComplete control (part 1 of many) in the next entry.

Creating A Data Set of Words

What I wanted was a very large set of words (so that when we’re working with the auto-complete you can start typing (e.g., ) acc and get quite a few words that match (accent, accompany, accordand, accordingly, accosted, account, accounted, accumulate, accurate, accustomed)  That lets you continue to narrow, adding an o knocks out accent and accumulate, accurate and accustomed. adding an r gets you down to just two words: accordand and accordingly.  You see the point.

On the other hand, I did not want you to have to download a large file just to make this work, and besides, it is more interesting if you can put in your own set.  So! it doesn’t take much to write a program that reads a file from your disk (Silverlight doesn’t mind reading from your disk, it is writing to your disk where it is very careful).  We take the file and read in all the words and after a bit of hocus pocus create a collection of unique words (that is, we eliminate all the duplicates and all the punctuation).  Sort the collection and we have just what we need.

Here is how I chose to do it.

First, i needed a file, so I went to Project Gutenberg (a wonderful project well worth your time) where I was able to download a text version of Swan’s way by Marcel Proust (public domain).  That will serve nicely as a source of words (and some interesting words at that!)

Second, I created a very simple UI as this is really just a utility. The UI has a button to open the file dialog, a TextBlock to let me know what is happening, and then two large TextBlocks encased inside ScrollViewers to show me the words I’ve retrieved. The left ScrollViewer displays the words in the order they were retrieved, the right displays them in alphabetical order,

AutoFill
(Figure cropped)

Notice that I’ve only captured 4,298 unique words. That is because I told the code to stop retrieving words once it hit 100,000 characters – why sit around all day on a weekend and 4K words is plenty.

Here’s the code walkthrough; nothing terribly complicated, though with luck you’ll find something new or useful…

<UserControl x:Class="AutoFill1.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="600">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Button x:Name="OpenFile" Content="Open File"
                 Height="30" Width="60" Margin="10"
                 HorizontalAlignment="Left" VerticalAlignment="Top"  />
        <TextBlock x:Name="Totals" Width="200" Height="30" 
            Text="Waiting..."  Grid.Column="1" VerticalAlignment="Top" 
            Margin="0,20,0,0"/>

        <ScrollViewer x:Name="WordDisplayViewer"  BorderBrush="Black" 
          BorderThickness="1"  Grid.Row="1" Grid.Column="0" Margin="5" 
          Background="Bisque" VerticalScrollBarVisibility="Auto" 
          HorizontalScrollBarVisibility="Hidden" VerticalAlignment="Stretch" 
          Width="190" >
            <TextBlock x:Name="WordDisplay" TextWrapping="Wrap" Text="Words" Width="160" />
        </ScrollViewer>
        <ScrollViewer BorderBrush="Black" BorderThickness="1" Grid.Row="1" 
            Grid.Column="1" Margin="5" Width="190" Background="Wheat" 
            VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden"
            VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
             <TextBlock x:Name="SortDisplay" TextWrapping="Wrap" Width="160"  
               Text="SortDisplay" />
        </ScrollViewer>
    </Grid>
</UserControl>

The key to having a nice scrolling block of text is to have the width of the text box be less than the width of the scrollViewer – enough to allow for the scroll bars.

In Page.xaml.cs I declare two private members, a StringBuilder and a List<String> and in Page_Loaded I provide the button’s event handler, which opens the file dialog,

public partial class Page : UserControl
{
   private StringBuilder sb = new StringBuilder();
   private List<string> words = new List<string>();

   public Page()
   {
      InitializeComponent();
      Loaded += new RoutedEventHandler( Page_Loaded );
   }

   void Page_Loaded( object sender, RoutedEventArgs e )
   {
      OpenFile.Click += new RoutedEventHandler( OpenFile_Click );
   }

The job of the event handler is to present the file dialog and then if hte user picks a file to open that file as a file stream, read through each line and using the string builder create a nice long string of up to 100,000 characters. 

void OpenFile_Click( object sender, RoutedEventArgs e )
{
   OpenFileDialog openFileDialog1 = new OpenFileDialog();

   // Set filter options and filter index.
   openFileDialog1.Filter = "Text Files (.txt)|*.txt|All Files (*.*)|*.*";
   openFileDialog1.FilterIndex = 1;

   openFileDialog1.Multiselect = false;

   // Call the ShowDialog method to show the dialog box.
   bool? userClickedOK = openFileDialog1.ShowDialog();

   // Process input if the user clicked OK.
   if ( userClickedOK == true )
   {
      // Open the selected file to read.
      System.IO.Stream fileStream = openFileDialog1.File.OpenRead();

      using ( System.IO.StreamReader reader = new System.IO.StreamReader( fileStream ) )
      {
         string temp = string.Empty;
         try
         {
            do
            {
               temp = reader.ReadLine();
               sb.Append( temp );
            } while ( temp != null && sb.Length < 100000 ) ;
         }
         catch
         {
         }
      }
      fileStream.Close();

(Notice that this is not exactly industrial-strength code. )

SwanBook 

In any case, once I have my string I break it into words by using the regular expression "\b" which separates the string into words which I put in an array of strings called AllWords.

If you are interested in regular expressions but you find books and articles on them too abstract, I can’t recommend RegEx Buddy highly enough.  You put in some text, and it (a) helps you build up your regular expression and (b) lets you test and (c) explains what works, what doesn’t and why.  Caveat: it is not free.

 

RegExBuddy

(Regex Buddy at work – from their site)

 

In any case, once my string is broken into words, I then iterate through each word to see if I have it already (I want only one copy of each word) and if not, I check to see if it is "junk" (has punctuation, numerals, etc.).  If it passes muster, I add it to my list<string> and once done I display the results,

fileStream.Close();
string pattern = "\\b";
string[] allWords = 
System.Text.RegularExpressions.Regex.Split( 
                  sb.ToString(), pattern );
foreach ( string word in allWords )
{
   if ( words.Contains( word ) == false )
   {
      if ( word.Length > 0 &&  ! IsJunk( word ) )
      {
         words.Add( word );
      }     // end if not junk
   }        // end if unique
}           // end for each word in all words
Totals.Text = words.Count + " unique words added.";
Display();
}           // end if user clicked OK in dialog
             // end method

To be able to display and use the words, I’ve created two properties,

public List<string> Words
{
   get { return this.words; }
}
 
public List<string> SortedWords
{
   get 
   { 
      List<string> temp = this.words;
      temp.Sort();
      return temp;
   }
}

Here are the two missing helper methods:

private void Display()
{
   WordDisplay.Text = string.Empty;
   SortDisplay.Text = string.Empty;
   foreach ( string s in Words )
   {
      WordDisplay.Text += " " + s;
   }
   foreach ( string s2 in SortedWords )
   {
      SortDisplay.Text += " " + s2;
   }
}
 
private bool IsJunk( string theWord )
{
   foreach ( char c in theWord.ToCharArray() )
   {
      if ( char.IsPunctuation( c ) || char.IsDigit( c ) || char.IsSymbol( c ) || char.IsSeparator( c ) )
         return true;
   }
   return false;
}

The very next thing to do (now that this works) is to refactor into three classes: the original page, the code that opens the dialog box and the code that creates the data structure of words.  I’ll do that next time and use that data structure to illustrate how to use the autocomplete box.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in z Silverlight Archives. Bookmark the permalink.