At PDC the WPF Tookit was made available which provides several new controls to WPF to help bring even more compatibility to the WPF and Silverlight story.  The new controls were the DatePicker, Calendar, DataGrid and the VisualStateManager concept to WPF.  Ribbon controls were also provided to the WPF Toolkit, but are not covered here.

With the introduction of this toolkit, developers now have a way to get some even more common XAML code-base between projects.

Let’s take a look at a very simple example of both UI and code sharing with WPF and Silverlight.  Here’s the simple scenario.  I’m going to use a common “Customer” class that I want to use in both a WPF and Silverlight implementation.  Since there is no binary compatibility for Silverlight and WPF, I’m going to maintain source-level compatibility.  In order to do this, I’m going to create a Silverlight Class Library project and implement my Customer class in that…here’s my simple customer class:

   1: namespace SilverlightClassLibrary1
   2: {
   3:     public class Customer
   4:     {
   5:         public string FirstName { get; set; }
   6:         public string LastName { get; set; }
   7:         public int CustomerId { get; set; }
   8:         public string CustomerLevel { get; set; }
   9:         public DateTime Birthdate { get; set; }
  10:  
  11:         public static ObservableCollection<Customer> GetCustomerData()
  12:         {
  13:             ObservableCollection<Customer> ppl = new ObservableCollection<Customer>();
  14:  
  15:             ppl.Add(new Customer() { FirstName = "Tim", LastName = "Heuer", CustomerId = 1, CustomerLevel = "Platinum", Birthdate = Convert.ToDateTime("9/18/1965") });
  16:             ppl.Add(new Customer() { FirstName = "Ray", LastName = "Ozzie", CustomerId = 1, CustomerLevel = "Gold", Birthdate = Convert.ToDateTime("1/12/1960") });
  17:             ppl.Add(new Customer() { FirstName = "Bill", LastName = "Gates", CustomerId = 1, CustomerLevel = "Silver", Birthdate = Convert.ToDateTime("12/1/1976") });
  18:             ppl.Add(new Customer() { FirstName = "Bart", LastName = "Simpson", CustomerId = 1, CustomerLevel = "Tin", Birthdate = Convert.ToDateTime("7/4/1985") });
  19:  
  20:             return ppl;
  21:         }
  22:     }
  23: }

Now in my Silverlight project and my WPF application I add a linked file to that class library for customer.  While not binary compat, each time I compile I’m using a common source compat for both projects.  Here’s what my project structure looks like and you can see the linked source code:

Now in my WPF project I want to add some XAML, so I’ll add this to my Window1.xaml:

   1: <Window x:Class="WpfSampler.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
   5:     Title="Window1" Height="600" Width="600">
   6:     <Grid>
   7:         <StackPanel Orientation="Vertical" Margin="15">
   8:             <TextBlock Text="Your Birthdate" />
   9:             <controls:DatePicker x:Name="MyBirthdate" Text="9/18/1965" />
  10:             <controls:DataGrid Height="400" x:Name="CustomerList"/>
  11:         </StackPanel>
  12:     </Grid>
  13: </Window>

Here’s the backing source code logic for the simple windows app:

   1: namespace WpfSampler
   2: {
   3:     /// <summary>
   4:     /// Interaction logic for Window1.xaml
   5:     /// </summary>
   6:     public partial class Window1 : Window
   7:     {
   8:         public Window1()
   9:         {
  10:             InitializeComponent();
  11:             Loaded += new RoutedEventHandler(Window1_Loaded);
  12:         }
  13:  
  14:         void Window1_Loaded(object sender, RoutedEventArgs e)
  15:         {
  16:             CustomerList.ItemsSource = SilverlightClassLibrary1.Customer.GetCustomerData();
  17:         }
  18:     }
  19: }

Now in my Silverlight project I’m going to paste the same XAML:

   1: <UserControl x:Class="SilverlightWpfReuse.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
   5:     xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
   6:     Width="600" Height="600">
   7:     <Grid x:Name="LayoutRoot" Background="White">
   8:         <StackPanel Orientation="Vertical" Margin="15">
   9:             <TextBlock Text="Your Birthdate" />
  10:             <controls:DatePicker x:Name="MyBirthdate" Text="9/18/1965" />
  11:             <data:DataGrid Height="400" x:Name="CustomerList"/>
  12:         </StackPanel>
  13:     </Grid>
  14: </UserControl>

and here’s the Silverlight source code logic as well:

   1: namespace SilverlightWpfReuse
   2: {
   3:     public partial class Page : UserControl
   4:     {
   5:         public Page()
   6:         {
   7:             InitializeComponent();
   8:             Loaded += new RoutedEventHandler(Page_Loaded);
   9:         }
  10:  
  11:         void Page_Loaded(object sender, RoutedEventArgs e)
  12:         {
  13:             CustomerList.ItemsSource = SilverlightClassLibrary1.Customer.GetCustomerData();
  14:         }
  15:     }
  16: }

So if you have a keen eye you’ll see it’s not full cut/paste as you note the xmlns declarations in the root control.  Since the implementations are in two different assemblies (System.Windows.Controls.dll for Silverlight and Microsoft.Windows.Controls.dll for WPF Toolkit) we have to make sure they are referenced accordingly.  We can still use the same prefix though of “controls” so that we have greater re-use across our XAML.  For DataGrid, we need to do an additional step since for Silverlight it is on its own assembly.

If we run these applications here is what we get:

WPF Application:

Silverlight application:

Sure we had to do some maneuvering (xmlns stuff) but if you have some great XAML/code you can still get some good re-use out of those implementations with little effort and share code!

So what about VisualStateManager?  How do you use that in WPF if you already have something in Silverlight?  Let’s simplify it to the most common/basic control – Button.  Using Blend we’ve styled a simple Button control and Blend has generated the appropriate VSM resources for us (note that I am not a designer and just used Blend to quickly make 2 changes that probably look horrible :-):

   1: <Style x:Key="StyledButton" TargetType="Button">
   2:             <Setter Property="Background" Value="#FF1F3B53"/>
   3:             <Setter Property="Foreground" Value="#FF000000"/>
   4:             <Setter Property="Padding" Value="3"/>
   5:             <Setter Property="BorderThickness" Value="1"/>
   6:             <Setter Property="BorderBrush">
   7:                 <Setter.Value>
   8:                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
   9:                         <GradientStop Color="#FFA3AEB9" Offset="0"/>
  10:                         <GradientStop Color="#FF8399A9" Offset="0.375"/>
  11:                         <GradientStop Color="#FF718597" Offset="0.375"/>
  12:                         <GradientStop Color="#FF617584" Offset="1"/>
  13:                     </LinearGradientBrush>
  14:                 </Setter.Value>
  15:             </Setter>
  16:             <Setter Property="Template">
  17:                 <Setter.Value>
  18:                     <ControlTemplate TargetType="Button">
  19:                         <Grid>
  20:                             <vsm:VisualStateManager.VisualStateGroups>
  21:                                 <vsm:VisualStateGroup x:Name="CommonStates">
  22:                                     <vsm:VisualState x:Name="Normal"/>
  23:                                     <vsm:VisualState x:Name="MouseOver">
  24:                                         <Storyboard>
  25:                                             <DoubleAnimationUsingKeyFrames Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity">
  26:                                                 <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
  27:                                             </DoubleAnimationUsingKeyFrames>
  28:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
  29:                                                 <SplineColorKeyFrame KeyTime="0" Value="#F2FFFFFF"/>
  30:                                             </ColorAnimationUsingKeyFrames>
  31:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)">
  32:                                                 <SplineColorKeyFrame KeyTime="0" Value="#CCFFFFFF"/>
  33:                                             </ColorAnimationUsingKeyFrames>
  34:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)">
  35:                                                 <SplineColorKeyFrame KeyTime="0" Value="#7FE61212"/>
  36:                                             </ColorAnimationUsingKeyFrames>
  37:                                             <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="Background" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)">
  38:                                                 <SplineColorKeyFrame KeyTime="00:00:00" Value="#FFEE4B4B"/>
  39:                                             </ColorAnimationUsingKeyFrames>
  40:                                         </Storyboard>
  41:                                     </vsm:VisualState>
  42:                                     <vsm:VisualState x:Name="Pressed">
  43:                                         <Storyboard>
  44:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="Background" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)">
  45:                                                 <SplineColorKeyFrame KeyTime="0" Value="#FFF40B0B"/>
  46:                                             </ColorAnimationUsingKeyFrames>
  47:                                             <DoubleAnimationUsingKeyFrames Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity">
  48:                                                 <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
  49:                                             </DoubleAnimationUsingKeyFrames>
  50:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
  51:                                                 <SplineColorKeyFrame KeyTime="0" Value="#D8FFFFFF"/>
  52:                                             </ColorAnimationUsingKeyFrames>
  53:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
  54:                                                 <SplineColorKeyFrame KeyTime="0" Value="#C6FFFFFF"/>
  55:                                             </ColorAnimationUsingKeyFrames>
  56:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)">
  57:                                                 <SplineColorKeyFrame KeyTime="0" Value="#8CFFFFFF"/>
  58:                                             </ColorAnimationUsingKeyFrames>
  59:                                             <ColorAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)">
  60:                                                 <SplineColorKeyFrame KeyTime="0" Value="#3FFFFFFF"/>
  61:                                             </ColorAnimationUsingKeyFrames>
  62:                                         </Storyboard>
  63:                                     </vsm:VisualState>
  64:                                     <vsm:VisualState x:Name="Disabled">
  65:                                         <Storyboard>
  66:                                             <DoubleAnimationUsingKeyFrames Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity">
  67:                                                 <SplineDoubleKeyFrame KeyTime="0" Value=".55"/>
  68:                                             </DoubleAnimationUsingKeyFrames>
  69:                                         </Storyboard>
  70:                                     </vsm:VisualState>
  71:                                 </vsm:VisualStateGroup>
  72:                                 <vsm:VisualStateGroup x:Name="FocusStates">
  73:                                     <vsm:VisualState x:Name="Focused">
  74:                                         <Storyboard>
  75:                                             <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
  76:                                                 <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
  77:                                             </DoubleAnimationUsingKeyFrames>
  78:                                         </Storyboard>
  79:                                     </vsm:VisualState>
  80:                                     <vsm:VisualState x:Name="Unfocused"/>
  81:                                 </vsm:VisualStateGroup>
  82:                             </vsm:VisualStateManager.VisualStateGroups>
  83:                             <Border x:Name="Background" Background="White" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3">
  84:                                 <Grid Margin="1" Background="{TemplateBinding Background}">
  85:                                     <Border x:Name="BackgroundAnimation" Opacity="0" Background="#FF448DCA"/>
  86:                                     <Rectangle x:Name="BackgroundGradient">
  87:                                         <Rectangle.Fill>
  88:                                             <LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
  89:                                                 <GradientStop Color="#FFFFFFFF" Offset="0"/>
  90:                                                 <GradientStop Color="#F9FFFFFF" Offset="0.375"/>
  91:                                                 <GradientStop Color="#E5FFFFFF" Offset="0.625"/>
  92:                                                 <GradientStop Color="#C6FFFFFF" Offset="1"/>
  93:                                             </LinearGradientBrush>
  94:                                         </Rectangle.Fill>
  95:                                     </Rectangle>
  96:                                 </Grid>
  97:                             </Border>
  98:                             <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" x:Name="contentPresenter" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
  99:                             <Rectangle x:Name="DisabledVisualElement" IsHitTestVisible="false" Opacity="0" Fill="#FFFFFFFF" RadiusX="3" RadiusY="3"/>
 100:                             <Rectangle Margin="1" x:Name="FocusVisualElement" IsHitTestVisible="false" Opacity="0" Stroke="#FF6DBDD1" StrokeThickness="1" RadiusX="2" RadiusY="2"/>
 101:                         </Grid>
 102:                     </ControlTemplate>
 103:                 </Setter.Value>
 104:             </Setter>
 105:         </Style>

There is nothing more we have to do in Silverlight since VSM is a part of the core.  We just annotate our Button with the style:

   1: <Button Content="Styled Button" Style="{StaticResource StyledButton}" />

Now in WPF we can use those same resources by adding them to our Grid.Resources node in our Window1.xaml file.  The thing is that we don’t need the vsm: prefix.  A simple find/replace removes them and we have our style button.  The resulting full XAML for the style looks the exact same (minus any vsm: prefix) and the XAML for the button also looks the same.

I think there has to be a better way to use linked files here as well, so I’ll think about that and report back if I find one.  I know this is a very simple demonstration of only a bit beyond Hello World, but I hope at least it gets you thinking of how you can get source-level compatibility and some XAML re-use out of your Silverlight and WPF applications. 

What are your thoughts?  Have you found better ways?  What are the stumbling blocks you are facing in code sharing?

This is a public service announcement for my Flickr4Writer project.  It was recently brought to my attention that Flickr has some privacy settings that users can opt-in for in their account to protect their images.  Some users felt that my plug-in for Writer was not honoring these settings.  Truly, I didn’t know about them.  You can read the thread on the discussion lists here if you are so inclined.  For me it came down to a couple of items:

    • Flickr enables users to set a flag to prevent “blogging” of their images
    • Flickr enables users to be hidden from 3rd Party/API searches

First, a note on the “blogging” flag.  This is a setting under your Flickr account privacy tab labeled Who can blog your photos or videos?.  To me, this setting is a little misleading because the description of it actually reads:

This means that anyone who does not match at least the contact level you select will not be able to see the "Blog This" button. (Source: http://www.flickr.com/account/prefs/blogging/?from=privacy 07 NOV 2008)

This setting is clearly for the “Blog This” functionality that shows up if you are logged into Flickr as a non-anonymous user and browse photos.  There is some functionality for them to integrate directly with your blog engine to do some one-click blogging of photos and videos.  Because of the way the setting is named however, some users interpret “blogging” in the broader sense.  flickr4writer was challenged as one violating the principal of this setting.  Since the setting ONLY enables authenticated users to even blog (the setting options go from any Flickr user (non-anonymous) to your specific friends/family settings.  flickr4writer does not use any authentication, so browsing any photos has the appearance to violate this term if the plugin enables an anonymous user to browse and select photos in a tool that is build for “blogging.”  While I draw the correlation that flickr4writer is basically a shell to the web site and does not do anything different than an anonymous user being able to browse and grab an image URL, it is the essence of the rule that I was alleged to violate.  One challenge here is also that the API is poorly designed in this regard because the “canblog” setting is returned only at the photo level even though it is an uber setting for the user’s account.  I think it should be a filter param of the photos.search API call.

The second setting about 3rd Party/API blocking from searches gets even more interesting.  First, this totally makes sense.  Again, it was a setting I wasn’t aware of.  You can change your setting under a section titled Hide your photostream from searches on 3rd party sites that use the API?  Great.  You’d think that once a user selected this setting that any search would filter out their photos/vidoes at the API level right?  Wrong.  flickr4writer uses photos.search calls to query data (actually technically the library that Flickr4Writer uses does).  Again, by definition of this API, only public searchable photos will be returned.  UNLESS you specify a user name.  What?!?!  Yes, that’s right…if you specifc a user name, their results will come back in the API call.  Read that again.  If you specify a user name in flickr.photos.search it will not honor the user’s privacy setting.  So this sucks for me as an API developer/consumer who wants to honor those settings.

So on to the resolutions.  First, I added authentication.  flickr4writer now requires you to have a valid Flickr account to even use it (their accounts are free).  This helps with the first part about blogging.  If a user has specified they do not want their content to be blogged, I honor that and will alert the flickr4writer user with a message that the user they searched prevents blogging and no search results will display.  I feel that this complies with that setting within my application.  If a user wants to bypass my app and copy/paste the URL to the photo/video still…that’s Flickr’s problem now, not mine.  Adding authentication also enabled me to comply with the blogging settings of users because it identifies who the user is and whether or not they can blog the content.

The second thing I modified was to only return content that had Creative Commons or No known license attributes.  This actually makes sense and I needed to do it for a while.  The licenses I filter for are identified in the flickr.photos.licenses.getInfo API.  So if a user has content that is “All rights reserved” then it will no longer show up in the search…even if you are the owner of that content.  I’m interested in feedback on this one if you think I should do a check to see if you are the owner and allow you to see licensed content…leave a comment on how you feel about this one.

For the setting of hiding from 3rd parties…I cannot resolve this.  There is no setting for me to look at.  I’m quite disappointed that Flickr isn’t doing this at the API level as I think that they are violating the user preferences by enabling a loophole.  Should they enable a setting for this (I think they just need to fix the API), I will enable my application to comply even more.  Please if you are a Flickr user who has set this privacy setting, let Flickr know that you want it to be honored.

The authentication adds some initial screens to the use of flickr4writer.  When you launch the plug-in from within Writer, you’ll now see some prompts to authorize the application with Flickr.  There will be a button that will take you to Flickr to authorize the action.  This is only required one time and you won’t see it anymore unless you de-authorize the application on your account (which you have the complete control to perform).

Please upgrade to the latest version.  You will have to uninstall any previous version before installing, but will not lose any settings.  Thanks for your assistance in helping keeping flickr4writer compliant.

Flickr4Writer is a plug-in for Windows Live Writer, a blog authoring tool available free to anyone.  The plug-in enables a Flickr user (Flickr account is required) to browse their own photos as well as others and insert the content into a blog post using Writer.

The plug-in enables you to search all your content, photosets, or search by tags.  It is available for free and can be downloaded from Codeplex.  For developers, the code is also made available if you want to contribute fixes and features (that comply with Flickr API terms of use). 

UPDATE: Plugin has been updated to follow new Flickr API terms of use.  Get the latest version here.

I hope that you enjoy the plug-in!

Sometimes I think reading materials get overlooked in SDKs and we miss some hidden gems.  One such gem I’d like to bring to your attention is the ability to add some subtle styling to an AutoCompleteBox from the Silverlight Toolkit to provide you with a cheap version of an editable ComboBox.

Sure Silverlight 2 has a ComboBox as part of the core controls now, but as I’ve previously noted, the ComboBox in current form exhibits only DropDownList behaviors.  I’m sure this will likely change with future versions, but if you have a need for an editable ComboBox feel, here’s a start.

If you think about the AutoCompleteBox functionality, it provides a list of items that are filtered when the user starts typing in there.  Well, that’s one function of a ComboBox right?  The other exhibited behavior we need is to be able to click to activate the DropDownList functionality and select.  Here’s where styling comes in (with a little help from some properties on AutoCompleteBox as well).

Let’s start by adding the items we’ll be dealing with: AutoCompleteBox and a data class (I’ll use a local hard-coded data class for portability in this code).  To use the AutoCompleteBox, make sure you’ve downloaded the Silverlight Toolkit and then add a reference in your Silverlight project to Microsoft.Windows.Controls.dll.  Then add the xmlns decoration in your UserControl…here’s mine (I use “toolkit”):

   1: <UserControl x:Class="ACBEditCombo.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:toolkit="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
   5:     Width="400" Height="300">
   6:     <Grid x:Name="LayoutRoot" Background="White">
   7:         <toolkit:AutoCompleteBox MinimumPrefixLength="0" 
   8:                                  MinimumPopulateDelay="200" 
   9:                                  x:Name="FruitChoice"
  10:                                  Width="200" Height="25" />
  11:     </Grid>
  12: </UserControl>

Here’s the Fruit class I’ll be using:

   1: #region Fruit Class
   2:     public class Fruit
   3:     {
   4:         public string Name { get; set; }
   5:         public override string ToString()
   6:         {
   7:             return this.Name;
   8:         }
   9:  
  10:         public static List<Fruit> GetFruitChoices()
  11:         {
  12:             List<Fruit> choices = new List<Fruit>();
  13:  
  14:             choices.Add(new Fruit() { Name = "Apple" });
  15:             choices.Add(new Fruit() { Name = "Apricot" });
  16:             choices.Add(new Fruit() { Name = "Banana" });
  17:             choices.Add(new Fruit() { Name = "Orange" });
  18:             choices.Add(new Fruit() { Name = "Pineapple" });
  19:             choices.Add(new Fruit() { Name = "Mango" });
  20:             choices.Add(new Fruit() { Name = "Papaya" });
  21:             choices.Add(new Fruit() { Name = "Pumpkin" });
  22:  
  23:             return choices;
  24:         }
  25:     }
  26:     #endregion

As you can see it is simple, but I wanted to make sure I was using more than just a string (yes I know essentially it is an object with only a string, but think outside the box :-)).  Now we can wire up this code here to get our AutoCompleteBox behavior working:

   1: public partial class Page : UserControl
   2: {
   3:     List<Fruit> choices = Fruit.GetFruitChoices();
   4:  
   5:     public Page()
   6:     {
   7:         InitializeComponent();
   8:         Loaded += new RoutedEventHandler(Page_Loaded);
   9:     }
  10:  
  11:     void Page_Loaded(object sender, RoutedEventArgs e)
  12:     {
  13:         FruitChoice.ItemsSource = choices;
  14:         FruitChoice.ItemFilter = (prefix, item) =>
  15:             {
  16:                 if (string.IsNullOrEmpty(prefix))
  17:                 {
  18:                     return true;
  19:                 }
  20:  
  21:                 Fruit f = item as Fruit;
  22:  
  23:                 if (f == null)
  24:                 {
  25:                     return false;
  26:                 }
  27:  
  28:                 return f.Name.ToLower().Contains(prefix.ToLower());
  29:             };
  30:     }
  31: }

Here we are using the same ItemFilter method I noted previously about AutoCompleteBox.  Once we have this when we start typing we’ll get:

Now all we need to do is make it look and act also like a ComboBox.  Here we can take styles and apply them.  We’re going to do three things using Style resources:

    1. Modify the control template for our toolkit:AutoCompleteBox to include a ToggleButton
    2. Create a style for the ToggleButton that looks like an arrow to cue the user what to do with it
    3. Style the list displayed a little bit

Best of all – we’re not going to change any of our logic code.  None.  First let’s modify the control template.  I’m putting these in my App.xaml resources so my app could use them globally.  Here’s the full style for the new control template which has new content in there and a ToggleButton added (I’ll keep it collapsed here for the sake of making this post less verbose than it already is):

   1: <Style x:Key="EditableComboStyle" TargetType="toolkit:AutoCompleteBox">
   2:             <Setter Property="SearchMode" Value="StartsWith" />
   3:             <Setter Property="Background" Value="#FF1F3B53"/>
   4:             <Setter Property="IsTabStop" Value="False" />
   5:             <Setter Property="HorizontalContentAlignment" Value="Left"/>
   6:             <Setter Property="BorderBrush">
   7:                 <Setter.Value>
   8:                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
   9:                         <GradientStop Color="#FFA3AEB9" Offset="0"/>
  10:                         <GradientStop Color="#FF8399A9" Offset="0.375"/>
  11:                         <GradientStop Color="#FF718597" Offset="0.375"/>
  12:                         <GradientStop Color="#FF617584" Offset="1"/>
  13:                     </LinearGradientBrush>
  14:                 </Setter.Value>
  15:             </Setter>
  16:             <Setter Property="Template">
  17:                 <Setter.Value>
  18:                     <ControlTemplate TargetType="toolkit:AutoCompleteBox">
  19:                         <Grid Margin="{TemplateBinding Padding}">
  20:                             <VisualStateManager.VisualStateGroups>
  21:                                 <VisualStateGroup x:Name="PopupStates">
  22:                                     <VisualStateGroup.Transitions>
  23:                                         <VisualTransition GeneratedDuration="0:0:0.1" To="PopupOpened" />
  24:                                         <VisualTransition GeneratedDuration="0:0:0.2" To="PopupClosed" />
  25:                                     </VisualStateGroup.Transitions>
  26:                                     <VisualState x:Name="PopupOpened">
  27:                                         <Storyboard>
  28:                                             <DoubleAnimation Storyboard.TargetName="PopupBorder" Storyboard.TargetProperty="Opacity" To="1.0" />
  29:                                         </Storyboard>
  30:                                     </VisualState>
  31:                                     <VisualState x:Name="PopupClosed">
  32:                                         <Storyboard>
  33:                                             <DoubleAnimation Storyboard.TargetName="PopupBorder" Storyboard.TargetProperty="Opacity" To="0.0" />
  34:                                         </Storyboard>
  35:                                     </VisualState>
  36:                                 </VisualStateGroup>
  37:                             </VisualStateManager.VisualStateGroups>
  38:                             <TextBox IsTabStop="True" x:Name="Text" Style="{TemplateBinding TextBoxStyle}" Margin="0" />
  39:                             <ToggleButton 
  40:                                 x:Name="DropDownToggle" 
  41:                                 HorizontalAlignment="Right"
  42:                                 VerticalAlignment="Center"
  43:                                 Style="{StaticResource ComboToggleButton}"
  44:                                 Margin="0"
  45:                                 HorizontalContentAlignment="Center" 
  46:                                 Background="{TemplateBinding Background}" 
  47:                                 BorderThickness="0" 
  48:                                 Height="16" Width="16"
  49:                                 >
  50:                                 <ToggleButton.Content>
  51:                                     <Path x:Name="BtnArrow" Height="4" Width="8" Stretch="Uniform" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z " 
  52:                                           Margin="0,0,6,0" HorizontalAlignment="Right">
  53:                                         <Path.Fill>
  54:                                             <SolidColorBrush x:Name="BtnArrowColor" Color="#FF333333"/>
  55:                                         </Path.Fill>
  56:                                     </Path>
  57:                                 </ToggleButton.Content>
  58:                             </ToggleButton>
  59:                             <Popup x:Name="Popup">
  60:                                 <Border x:Name="PopupBorder" HorizontalAlignment="Stretch" Opacity="0.0" BorderThickness="0" CornerRadius="3">
  61:                                     <Border.RenderTransform>
  62:                                         <TranslateTransform X="2" Y="2" />
  63:                                     </Border.RenderTransform>
  64:                                     <Border.Background>
  65:                                         <SolidColorBrush Color="#11000000" />
  66:                                     </Border.Background>
  67:                                     <Border HorizontalAlignment="Stretch" BorderThickness="0" CornerRadius="3">
  68:                                         <Border.Background>
  69:                                             <SolidColorBrush Color="#11000000" />
  70:                                         </Border.Background>
  71:                                         <Border.RenderTransform>
  72:                                             <TransformGroup>
  73:                                                 <ScaleTransform />
  74:                                                 <SkewTransform />
  75:                                                 <RotateTransform />
  76:                                                 <TranslateTransform X="-1" Y="-1" />
  77:                                             </TransformGroup>
  78:                                         </Border.RenderTransform>
  79:                                         <Border HorizontalAlignment="Stretch" Opacity="1.0" Padding="2" BorderThickness="2" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="3">
  80:                                             <Border.RenderTransform>
  81:                                                 <TransformGroup>
  82:                                                     <ScaleTransform />
  83:                                                     <SkewTransform />
  84:                                                     <RotateTransform />
  85:                                                     <TranslateTransform X="-2" Y="-2" />
  86:                                                 </TransformGroup>
  87:                                             </Border.RenderTransform>
  88:                                             <Border.Background>
  89:                                                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  90:                                                     <GradientStop Color="#FFDDDDDD" Offset="0"/>
  91:                                                     <GradientStop Color="#AADDDDDD" Offset="1"/>
  92:                                                 </LinearGradientBrush>
  93:                                             </Border.Background>
  94:                                             <ListBox x:Name="SelectionAdapter" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemTemplate="{TemplateBinding ItemTemplate}" />
  95:                                         </Border>
  96:                                     </Border>
  97:                                 </Border>
  98:                             </Popup>
  99:                         </Grid>
 100:                     </ControlTemplate>
 101:                 </Setter.Value>
 102:             </Setter>
 103:         </Style>

Now if you look you’ll see that the ToggleButton itself has a Style attribute, so here’s the Style we use for that:

   1: <Style x:Name="ComboToggleButton" TargetType="ToggleButton">
   2:             <Setter Property="Foreground" Value="#FF333333"/>
   3:             <Setter Property="Background" Value="#FF1F3B53"/>
   4:             <Setter Property="Padding" Value="0"/>
   5:             <Setter Property="Template">
   6:                 <Setter.Value>
   7:                     <ControlTemplate TargetType="ToggleButton">
   8:                         <Grid>
   9:                             <Rectangle Fill="Transparent" />
  10:                             <ContentPresenter
  11:                             x:Name="contentPresenter"
  12:                             Content="{TemplateBinding Content}"
  13:                             ContentTemplate="{TemplateBinding ContentTemplate}"
  14:                             HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  15:                             VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
  16:                             Margin="{TemplateBinding Padding}"/>
  17:                         </Grid>
  18:                     </ControlTemplate>
  19:                 </Setter.Value>
  20:             </Setter>
  21:         </Style>

Now the only thing we need to do is add the Style attribute for our AutoCompleteBox as reflected here (notice the added Style attribute):

   1: <UserControl x:Class="ACBEditCombo.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:toolkit="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
   5:     Width="400" Height="300">
   6:     <Grid x:Name="LayoutRoot" Background="White">
   7:         <toolkit:AutoCompleteBox MinimumPrefixLength="0" 
   8:                                  MinimumPopulateDelay="200" 
   9:                                  x:Name="FruitChoice"
  10:                                  Width="200" Height="25"
  11:                                  Style="{StaticResource EditableComboStyle}"/>
  12:     </Grid>
  13: </UserControl>

And now when we look at our application running we have what looks to be an editable ComboBox:

The ComboBox behavior is enabled now via the ToggleButton and the MinimumPrefixLength attribute on the AutoCompleteBox (try changing it to 1 and you’ll see that you don’t get exactly the same behavior).

It’s little gems like this that should cause you to pay attention to the finer details of SDKs, samples, etc.  I hope you found this helpful…if you have, please consider subscribing for more samples like this (and no I’m not planning on just emitting all the SDK samples :-)).  You can download my code for this above in a complete solution here: ACBEditCombo.zip.

Remember DreamSpark, the program for students in higher education to get access to the developer tools and platforms from Microsoft at no charge?  How about some love for individuals in startup organizations trying to create the next great innovation in technology as a service, an application, some Silverlight love perhaps :-)?

Done.

A new program has just launched for startups.  From the site it is described as:

Microsoft® BizSpark™ is a global program designed to help accelerate the success of early stage startups by providing key resources when they need it the most:

  • Software. Receive fast and easy access to current full-featured Microsoft development tools, platform technologies, and production licenses of server products for immediate use in developing and bringing to market innovative and interoperable solutions. There is no upfront cost to enroll.
  • Support. Get connected to Network Partners around the world — incubators, investors, advisors, government agencies and hosters — that are equally involved and vested in software-fueled innovation and entrepreneurship who will provide a wide range of support resources.
  • Visibility. Achieve global visibility to an audience of potential investors, clients and partners
    As a Microsoft BizSpark member, you’ll be tapping into a rich, vibrant ecosystem of peers, partners and support resources around the globe, helping you grow and succeed. Microsoft BizSpark is the quickest way to get your Startup fired up.

Source: http://www.microsoftstartupzone.com/BizSpark/Pages/At_a_Glance.aspx

So what do you get as benefits in the program?

    • All the software included in the Microsoft® Visual Studio® Team System Team Suite (VSTS) with MSDN® Premium subscription
    • Expression® Studio Version 2
    • VSTS Team Foundation Server (standard edition)
    • Production use rights to host a “software as a service” solution (developed during participation in the BizSpark Program, on any platform) over the Internet, with regard to products including:
      • Microsoft Windows Server® (all versions up to and including Enterprise)
      • Microsoft SQL Server® (all versions)
      • Microsoft Office SharePoint® Portal Server
      • Microsoft System Center
      • Microsoft BizTalk® Server

There are key requirements for qualifying, so be sure to read about them.

To qualify for BizSpark, your startup must be:

  • Actively engaged in development of a software-based product or service that will form a core piece of its current or intended business[1],
  • Privately held,
  • In business for less than 3 years[2], and
  • Generating less than USD $1 million in annual revenue[3].

To be eligible to use the software for production and deployment of hosted solutions, startups must also be developing a new "software as a service" solution (on any platform) to be delivered over the Internet. Source: http://www.microsoftstartupzone.com/BizSpark/Pages/Do_I_Qualify.aspx

You can read all about the program details on the BizSpark information site.  So if you are a startup and looking to accelerate, take a look at this program.  It looks like the BizSpark team is also creating BizSparkDB, a center for startups to be profiled and visible.

What are you waiting for?  See if you qualify and then enroll!