Silverlight Controls – Look Nicer, Taste Great

 

 

In my earlier column i referred to the nice new look of the Silverlight 2 controls. I couldn’t resist throwing together a very fast application (using Blend to lay  it out, and Visual Studio to bind the data) showing off a few of the controls.

SL2Controls

Not an ideal layout, but just enough to show the bit of extra polish that has been added to the controls at release.

Happy Battle of Hastings Anniversary.

-j

Posted in z Silverlight Archives | Comments Off on Silverlight Controls – Look Nicer, Taste Great

Silverlight 2 Releases Tomorrow

SLLogo

I’m very pleased to confirm that we’ll be releasing Silverlight 2 tomorrow.

eWeek has an article that mentions the release and our funding for an open-source project to create an Eclipse plug-in for Silverlight.  My personal opinion is I can’t imagine using Eclipse for Silverlight development on the PC given the availability of Visual Studio…. but on the Mac, if/when such a thing is available… that would be very different. (Of course, this is a personal preference)

ZDNet covers the story and mentions that Silverlight penetration is approaching 50% in some countries, and that during the Olympics, NBCOlympics.com, powered by Silverlight had 50 million unique visitors, 1.3 billion page views.  Here’s what ZDNet has to say about the Eclipse announcement

Microsoft is going to be working with Soyatec to bring Silverlight development capabilities to Eclipse, which in theory means that we could finally have Mac and possibly even Linux development tools for Silverlight. According to the press release this will also be free. A technology preview of the tool is available now

Computer World’s coverage includes these morsels

Microsoft is also releasing for free a set of programming templates called the Silverlight Control Pack under its Microsoft Permissive License, as well as the technical specification for Silverlight’s Extensible Application Markup Language (XAML) vocabulary via Microsoft’s Open Specification Promise…[and] letting users of the free Visual Web Developer 2008 Express Edition write Silverlight applications

Posted in z Silverlight Archives | Tagged | Comments Off on Silverlight 2 Releases Tomorrow

Data Binding – Data Validation

SLLogoWords

The third and final Data Binding topic I’d like to cover for now is Data Validation. I’ll modify the code discussed yesterday we’re going to validate the Quantity on Hand field, already marked as two-way (that is, the user can enter a new value and it is stored in the underlying business object – the current book).

 DataBindingArchitectureTwoWay

Our goal is to capture and handle two types of errors

  • Exceptions thrown from the Binding Engine
  • Exceptions thrown from the Binding Source

An example of an exception the Binding Engine would throw would be were the user to enter letters where an integer value is expected

Binding Source exceptions would be created by the author of the Binding Source class (the Book Class) to ensure data integrity. In our case, we might throw an exception if we are handed a negative value for Quantity on hand. Thus, we might modify the set accessor for Quantity on hand as follows,

 

   1: public class Book : INotifyPropertyChanged
   2: {
   3:    private int quantityOnHand;
   4:      // Snip 
   5:  
   6:    public int QuantityOnHand
   7:    {
   8:       get { return quantityOnHand; }
   9:       set
  10:       {
  11:          if ( value < 0 )
  12:          {
  13:             throw new Exception( "Quantity on hand cannot be negative!" );
  14:          }
  15:          quantityOnHand = value;
  16:          NotifyPropertyChanged( "QuantityOnHand" );
  17:       }       // end set
  18:    }
  19:  

To manage both of these types of errors need to take 3 steps

  1. Identify the error handler either in the control or higher in the visiblity hierarchy (e.g., a container; in this case the grid that contains the text box)
  2. Set NotifyOnValidationError and ValidateOnException to true. The latter tells teh Binding Engine to create a validation error event when an exception occurs. The former tells the Binding Engine to raise teh BindingValidationError event when a validation error occurs.
  3. Create the event handler named in step 1

Identify the Handler

We’ll put the identifier of the handler in the grid so that it may be shared by all the controls within the grid.

<Grid x:Name="LayoutRoot" Background="White" BindingValidationError="LayoutRoot_BindingValidationError" >

Set NotifyOnValidationError and ValidateOnException

These are set as part of the binding syntax in the control itself,

   1: <TextBox x:Name="QuantityOnHand"   
   2:     Text="{Binding Mode=TwoWay, Path=QuantityOnHand, 
   3:         NotifyOnValidationError=true,  ValidatesOnExceptions=true }"
   4:     VerticalAlignment="Bottom"
   5:     HorizontalAlignment="Left"
   6:     Height="30" Width="90"
   7:     Grid.Row="4" Grid.Column="1" />

Create the Event Handler

The event handler named in the grid is implemented in the associated code-behind file,

   1: public partial class Page : UserControl
   2: {
   3:    private bool clean = true;  // new flag
   4:  
   5:    void Change_Click( object sender, RoutedEventArgs e )
   6:    {  // if not clean, don't change books
   7:       if ( !clean )
   8:          return;
   9:       // snip
  10:    }
  11:  
  12:    private void LayoutRoot_BindingValidationError( 
  13:       object sender, ValidationErrorEventArgs e )
  14:    {
  15:       if ( e.Action == ValidationErrorEventAction.Added )
  16:       {
  17:          QuantityOnHand.Background = new SolidColorBrush( Colors.Red );
  18:          clean = false;
  19:       }
  20:       else if ( e.Action == ValidationErrorEventAction.Removed )
  21:       {
  22:          QuantityOnHand.Background = new SolidColorBrush( Colors.White );
  23:          clean = true;
  24:       }
  25:    }

Delegation of Responsibility

Notice the careful delegation of responsibility. 

The Binding class knows that it is handling validation and some first approximation of validity (integers aren’t text) but it turns to the Book class for further validation (e.g, Quantity on Hand can’t be negative). If it finds that the value is invalid it raises an event, but it is up to the UI to handle that event and decide how to display that there is invalid data to the user.

Page.xaml in this case manages that by refusing to move to the next book and turning the input box red. There are a thousand other things it could do, but the key here is separation of responsibility. The UI doesn’t know why the data is invalid, only that it is.

Here is what it looks like when you enter invalid data and hit the Change Book button

InvalidData

As you can see, the entry box turns red, and the book does not change until the user fixes the data entered.

For those of you who want to copy and paste, here are all the changes

// page.xaml.cs

private bool clean = true;


private void LayoutRoot_BindingValidationError( 
   object sender, ValidationErrorEventArgs e )
{
   if ( e.Action == ValidationErrorEventAction.Added )
   {
      QuantityOnHand.Background = new SolidColorBrush( Colors.Red );
      clean = false;
   }
   else if ( e.Action == ValidationErrorEventAction.Removed )
   {
      QuantityOnHand.Background = new SolidColorBrush( Colors.White );
      clean = true;
   }
}



// page.xaml

<Grid x:Name="LayoutRoot" Background="White" BindingValidationError="LayoutRoot_BindingValidationError" >

<TextBox x:Name="QuantityOnHand"   
    Text="{Binding Mode=TwoWay, Path=QuantityOnHand, 
        NotifyOnValidationError=true,  ValidatesOnExceptions=true }"
    VerticalAlignment="Bottom"
    HorizontalAlignment="Left"
    Height="30" Width="90"red
    Grid.Row="4" Grid.Column="1" />


// book.cs

public int QuantityOnHand
{
   get { return quantityOnHand; }
   set
   {
      if ( value < 0 )
      {
         throw new Exception( "Quantity on hand cannot be negative!" );
      }
      quantityOnHand = value;
      NotifyPropertyChanged( "QuantityOnHand" );
   }       // end set
}
Posted in z Silverlight Archives | 1 Comment

Data Binding – Data Conversion

 

One of the more interesting topics that arise in Data-binding is that of Data Conversion. So much can be accomplished without explicit conversion that it is easy to overlook… until you hit the real world.

To see this, I’ll make a small modification to the code I wrote  for the Silverlight Tutorial on Data Binding. The premise of this simplified example is that you are interacting with a bookstore or a library, displaying information about one book at a time.

We assume that you get the book information from wherever it is held via a web service,  but that you will create Business Objects from that data, and it is these business objects that you will bind to (this is not the only way to do it, but it is the most flexible).

Briefly, the architecture in the original example looked like this

DataBindingArchitecture

What we’re trying to accomplish here: bind the data from a Book object to a Silverlight control (textBlock, etc.)

The Book class  is the business object. It is presumably filled from a call to a web-service, and the properties of the book are bound to the UI objects.  As discussed yesterday this is accomplished by setting the binding in the Xaml,

<TextBlock x:Name=”Title”
    Text=”{ Binding Title,  Mode=OneWay }”  />

The value that is bound, Title, is a property of the Book class

   1: public class Book : INotifyPropertyChanged
   2: {
   3:    private string bookTitle;
   4:     //...
   5:  
   6:    public string Title
   7:    {
   8:       get { return bookTitle; }
   9:       set
  10:       {
  11:          bookTitle = value;
  12:          NotifyPropertyChanged( "Title" );
  13:       }       // end set
  14:    }          // end property
  15:    //...
  16:  

 

In this case the binding target is a TextBlock also named Title. 

The Mode on the Binding is set to OneWay indicating that the Title will come from the BindingSource (the Book object) to be bound to the TextBlock, but changes made in the UI will not be sent back to the Binding Source.

<TextBlock x:Name=”Title”
    Text=”{ Binding Title,  Mode=OneWay }”  />

Converting Data – Dates for a Calendar

If the BookObject had a property for publication date that was of type DateTime,

public DateTime PublicationDate
{
   get {//…}
   set {//…}
}

You could easily bind it to a Calendar object,

<basics:Calendar x:Name=”pubDateCal”
    IsTodayHighlighted=”False” 
     SelectedDate=”{ Binding PublicationDate }”
     DisplayDate= “{ Binding PublicationDate }”
     /> 

… A calendar control expects a DateTime for its Selected and Display date Dependency Properties. But what do you do if the service you are using does not provide that for you. What if, instead, it provides only a month and a year and yet, you’d still like to show the calendar?

You have a number of options. Certainly, you could have the Book convert the month and year into a DateTime before the binding, creating a PublicationDate property that returns a DateTime ready to be bound.

An alternative is to create a converter that takes a month and a year and converts it to the type needed by your control when displaying it, and converts it back when storing data to the business object.

Okay, to make the contrivance just a bit worse, Converters are happier taking a single type and converting to another type, so we’re going to have the Book class maintain its publication Month and Year as integers, but I’ve taken the liberty™ of having the PublicationDate property return a new class MonthAndYear that we can then convert to DateTime.

This is either due to spec requirements beyond the scope of this article, or to my wanting a simple example that has now gotten completely out of hand.

In any case, here is the class we’ll use:

   1: public class MonthAndYear
   2: {
   3:    public int Month { get; set; }
   4:    public int Year { get; set; }
   5:    public MonthAndYear( int m, int y )
   6:    {
   7:       Month = m;
   8:       Year = y;
   9:    }
  10: }

Value Converters have real value

You can imagine many plausible situations where you might decide that your business object has data in a form that does need conversion and yet the business class ought not be doing that conversion. It is not unreasonable to delegate that responsibility to the UI level if it is only the UI level that needs the conversion.

Silverlight provides an Interface IValueConverter, which is designed for this situation. The interface requires that you implement the Convert and the ConvertBack methods, and our architecture becomes more like this,

DataBindingArchitectureExtended 

The Book class is modified to add the members but this time they do not each have their own property, but rather there is a single property, PublicationDate that manages them via the creation or reception of a MonthAndYear object,

   1: public class Book : INotifyPropertyChanged
   2: {
   3:    private string bookTitle;
   4:    private int pubMonth;
   5:    private int pubYear;
   6:    // other members
   7:  
   8:    public MonthAndYear PublicationDate
   9:    {
  10:       get { return new MonthAndYear( pubMonth, pubYear ); }
  11:       set
  12:       {
  13:          pubMonth = value.Month;
  14:          pubYear = value.Year;
  15:          NotifyPropertyChanged( "PublicationDate" );
  16:       }
  17:    }
  18:  

This greatly simplifies matters as we can not convert from one type (MonthAndYear) to another DateTime (and back, if we like).

Here is the complete DateToMonthAndYear.cs

   1: using System;
   2: using System.Windows.Data;
   3:  
   4: namespace DataBinding
   5: {
   6:    public class DateToMonthYear : IValueConverter
   7:    {
   8:  
   9:       public object Convert( 
  10:          object value,           // source data
  11:          Type targetType,        // type of dataa expected by target d.p.
  12:          object parameter,       // optional param to help conversion
  13:          System.Globalization.CultureInfo culture )
  14:       {
  15:          MonthAndYear temp = value as MonthAndYear;
  16:          if ( temp != null )
  17:          {
  18:             return new DateTime( temp.Year, temp.Month, 1 );
  19:          }
  20:          else
  21:          {
  22:             throw new ArgumentException( "DateToMonthYear conversion failed," + 
  23:                 "trying to convert " + value.ToString() );
  24:          }
  25:                
  26:       }
  27:  
  28:  
  29:       public object ConvertBack( 
  30:          object value, 
  31:          Type targetType, 
  32:          object parameter, 
  33:          System.Globalization.CultureInfo culture )
  34:       {
  35:          MonthAndYear retVal = null;
  36:          try
  37:          {
  38:             DateTime dt = (DateTime) value;
  39:             retVal =  new MonthAndYear( dt.Month, dt.Year );
  40:          }
  41:          catch ( Exception e )
  42:          {
  43:             throw new ArgumentException( "Exception thrown in DateToMonthYear.ConvertBack. Value = " + 
  44:                value.ToString(), e );
  45:          }
  46:          return retVal;
  47:       }
  48:    }
  49: }

 

 

What we’re trying to accomplish: The book class now has a property, PublicationDate that returns a MonthAndYear, we want to bind that to a Calendar that expects a DateTime object. The Converter will make the conversion back and forth.

We modify Page.xaml like this,

   1: <UserControl x:Class="DataBinding.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   2:     xmlns:local="clr-namespace:DataBinding"  Width="433" Height="416" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
   3:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" 
   4:     xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls">
   5:     <UserControl.Resources>
   6:         <local:DateToMonthYear x:Key="Converter1" />
   7:     </UserControl.Resources>
   8:         <Grid x:Name="LayoutRoot" Background="White">
   9:             <!-- Snip! -->
  10:         <basics:Calendar x:Name="pubDateCal"
  11:             FontFamily="Georgia" 
  12:             Grid.Column="1" Grid.Row="5" Grid.RowSpan="3" 
  13:             IsTodayHighlighted="False" 
  14:             VerticalAlignment="Top" HorizontalAlignment="Left"
  15:              SelectedDate="{ Binding PublicationDate, Mode=OneWay, 
  16:                                 Converter={StaticResource Converter1} }"
  17:              DisplayDate= "{ Binding PublicationDate, Mode=OneWay, 
  18:                                 Converter={StaticResource Converter1} }"     />        
  19:                         
  20:     </Grid>
  21: </UserControl>

We create two new namespaces. On line 4 basics is created to bring in the System.Windows.Controls namespace and is used on line 10 to create the Calendar.

On line 2 local is created to create a namespace for the application itself.

On lines 5 through 7 a Resources section is created, inside of which a resource of type DateToMonthYear is declared using the local namespace; this ties the resource to the class we created, and assigns it a static resource key of converter1.

The SelectionDate and the DisplayDate of the Calendar control are bound to the PublicationDate and given the Converter to use, identified using the resource by that key (converter1) on lines 16 and 18. 

Putting It Together

When the Binding engine tries to bind PublicationDate to the SelectedDate or DisplayDay Dependency Properties of the Calendar, it will see that the converter is referencing the staticResource whose key is Converter1. It will look at the resource section and see that the key resolves to a namespace that identifies which assembly to look in, and an identifier that tells the Binding engine which class to use.  That class must implement IValueConverter, and it will call Convert, passing in the MonthAdnYear object and getting back a DateTime that it will then use to bind to the Dependency Properties.  Piece of cake.

The example is a bit contrived, but that allows for some simplicity in tracing it through. The source code is here.

Thanks.

-j

Posted in z Silverlight Archives | Tagged | Comments Off on Data Binding – Data Conversion

Data Binding – Margin Notes

 

We have a couple tutorials and videos about Data-binding, and once you grok the basics, it is reasonably straightforward.

There are a couple interesting concepts at the edges that are worth calling out, however.  The topics I’d like to cover in this and a couple more blog posts include

  • Binding and DataContext (today, as a gentle intro)
  • The role of Value Converters & Data Validation (next)
  • Property Paths (fun)

DataContext

  One of the first places that folks become confused when it comes to data-binding is in the relationship between the Binding Xaml syntax, and the DataContext set in code.  The way I think about this is that at design time I know what properties I want to bind to but not which object I’ll have in my hand.  At run time, I’ll have the specific object, and I’d like all the properties there and ready to link to, like neurotransmitter receptors standing by to absorb Serotonin. [1]

For example, I might have a library full of books, and I might know that each book has a Title, and Author, and a collection of chapters.   But I don’t know which book I’ll be displaying when the application is running.

I can lay out my page and bind the control that I’ll use to display these three fields even without knowing which book the user will request; all I need to know is that the Book class defines these three properties. It is the property that I bind to.

   1: <TextBlock x:Name="TitlePrompt" Text="Title:  " 
   2:     VerticalAlignment="Bottom"
   3:     HorizontalAlignment="Right"
   4:     Grid.Row="0"  Grid.Column="0" />
   5:  
   6: <TextBlock x:Name="Title" 
   7:     Text="{Binding Title, Mode=OneWay }" 
   8:     VerticalAlignment="Bottom"
   9:     HorizontalAlignment="Left"
  10:     Grid.Row="0" Grid.Column="1" />
  11:     
  12: <TextBlock x:Name="AuthorPrompt" Text="Author: " 
  13:     VerticalAlignment="Bottom"
  14:     HorizontalAlignment="Right"
  15:     Grid.Row="1"  Grid.Column="0" />
  16:  
  17: <TextBlock x:Name="Author" 
  18:     Text="{Binding Author, Mode=OneWay }" 
  19:     VerticalAlignment="Bottom"
  20:     HorizontalAlignment="Left"
  21:     Grid.Row="1" Grid.Column="1" />
  22:  
  23: <TextBlock x:Name="ChapterPrompt" Text="Chapters:  "  
  24:     VerticalAlignment="Bottom"
  25:     HorizontalAlignment="Right"
  26:     Grid.Row="2" Grid.Column="0"  />
  27:  
  28: <ListBox x:Name="Chapters" 
  29:     ItemsSource="{Binding  Chapters, Mode=OneWay}"
  30:     VerticalAlignment="Bottom"
  31:     HorizontalAlignment="Left"
  32:     Height="60" Width="200"
  33:     Grid.Row="2" Grid.Column="1" />
  34:  

Mode

When I bind I tell the binding engine that I’m binding in one of three modes:

  1. OneTime (bind and then I’ll never change it)
  2. OneWay (bind from the datasource to the UI, and keep the UI up to date)
  3. TwoWay (bind from the datasource to the UI and if either changes, update the other)

There is a trade off in efficiency vs.. freshness of data that I’ll return to in another entry.

Context

Having set the property of the object at design time, (e.g., Binding Title) I need to inform the TextBlock (or whichever control I’m binding to) which object’s property to bind to – that is “okay, bind to the Title, but whose??”  That is decided at run time, typically by retrieving a specific book based on a user’s action.

The context is a bit odd; it is tied to the visual hierarchy and contained objects inherit the context from outer objects, though you are free to supply them with their own context.  If you only need a single object for everything in the grid, you can set the DataContext for the grid, and then all the controls within that grid will inherit it (note that this is different from class inheritance)

Thus, you could set up the DataContext in code by writing,

Title.DataContext = currentBook;
Author.DataContext = currentBook;
Chapters.DataContext = currentBook;

or you could just write

LayoutRoot.DataContext = currentBook;

In the former case, you are setting the DataContext for each control, in the latter, you are setting the DataContext for the container control (LayoutRoot is the grid), and the contained controls (Title, Author, Chapters) inherit that context).

Once the context is set, the Binding can be completed. You’ll remember that the TextBlock  named Title looked like this:

   1: <TextBlock x:Name="Title" 
   2:     Text="{Binding Title, Mode=OneWay }" 
   3:     VerticalAlignment="Bottom"
   4:     HorizontalAlignment="Left"
   5:     Grid.Row="0" Grid.Column="1" />

Having now set the data context, the binding resolves to the Title property of currentBook which is of type Book and thus resolves to a specific property of a specific object with a very specific value to be displayed.

The list of chapters is handled very similarly, except it is the ItemSource that was set to the property Chapters. If we examine the Book class we find that Chapters is a collection (a List<string>) and serves well as an ItemSource.

In the next column, I’ll turn to Value Converters and Data Validation.

-jesse

(note, the code for this entry is taken from the code used for the tutorial linked above)

—

[1] This analogy has been reported to the Department of Metaphors and will be investigated for appropriateness. You will be notified.

Posted in z Silverlight Archives | Comments Off on Data Binding – Margin Notes

Custom Controls – The Dénouement

Silverlight Logo 

 

 

Over the past month I’ve posted half a dozen min-articles about creating custom controls that can interact with the Visual State Manager, and the underlying engine that makes it work; especially Dependency Properties and the Parts and States Model.

With this background, we’re ready to move on to the steps of implementing visual states in your custom control. The key concept that we’ve been building to is this: there is a strict separation between the logic of your control, and the visuals of your control. The place that this becomes most clear is often when you click on a control.

Visually, many controls respond to a click with some form of visual change. Logically they respond with some behavior- or they raise an even that allows “listeners” to respond. Keeping these separate is key to the Parts and States model.

The Custom Control that we’re going to create (in a whirlwind fashion here, more carefully and slowly in a forthcoming tutorial and videos) is derived from Control, draws its own shape, is designed to be skinnable, and supports the parts and states model.

As such, it defines three CommonStates

  • Normal
  • MouseOver
  • Pressed

It also defines two CustomStates

  • Normal
  • On

Notice that because the StateGroups (CommonStates and CustomStates) are orthogonal, there is no problem having a NormalState in both.

State Changes.

The custom control appears as a grey ball when it is in its Normal CommonState and in its Normal CustomState, but its visual appearance changes noticeably when it moves into any of the other states.  In addition, I’ve added a template that changes its appearance in its normal state and in its other visual states and to cap it off the control publishes an event for when it is clicked allowing the page that houses the control (templated or not) to respond to the click. <whew!>

While we’ll save the nitty gritty for the tutorial and the videos, the point to focus on here is this: how does the control convey its visual contract to the Visual State Manager (and to Expression Blend?). 

That is accomplished using attributes. These are placed above the definition of the CustomControl and constitute the contract between the control and the Visual State Manager; they assert the state groups and the states within those state groups,

   1: [TemplatePart       ( Name = "Core",      Type = typeof( FrameworkElement ) )]
   2: [TemplateVisualState( Name = "Normal",    GroupName = "CommonStates" )]
   3: [TemplateVisualState( Name = "MouseOver", GroupName = "CommonStates" )]
   4: [TemplateVisualState( Name = "Pressed",   GroupName = "CommonStates" )]
   5: [TemplateVisualState( Name = "On",        GroupName = "CustomStates" )]
   6: [TemplateVisualState( Name = "Norm",      GroupName = "CustomStates" )]
   7: public class CustomControl : Control
   8: {

 

Line 1 defines the one part of our control; we give its name and its type (in this case, the type is FrameworkElement—which is general enough to encompass any UIControll and, moreover any element that might participate in the Silverlight layout system, have a lifetime and might need support for databinding.

The five TemplateVisualState attributes define two state groups: CommonStates and CustomStates.

Converting CLR events into state changes is the job of the custom class. For example, the CLR knows nothing at all about “mouse over” – that is not a state recognized by Windows or Silverlight. However, Silverlight does recognize MouseEnter and MouseLeave, which gives us all we need. 

We begin by creating three member variables,

private FrameworkElement corePart;
private bool isMouseOver;
private bool isPressed;

The first of these is for the body of our control, which we’ll obtain immediately upon applying the template,

public override void OnApplyTemplate()
{
   base.OnApplyTemplate();
   CorePart = GetTemplateChild( “Core” ) as FrameworkElement;

(This of course relies on naming the appropriate element “Core” in the Xaml, which we, of course, do…)

<Ellipse
   x:Name=”Core”
    Width=”200″
    Height=”200″ RenderTransformOrigin=”0.5,0.5″ >

With the CorePart in hand, and our two private variables, we can set the event handlers, and make the conversion from CLR events to visual state changes. But borrowing from smarter developers we’re going to be tricky; we’ll set the event handler for Core when we set Core’s property, and we’ll do so in a fail-safe manner (allowing for the possibility that either Core doesn’t currently exist or that we’ve been handed a null core).  Here’s the complete property that fronts the data member,

   1: private FrameworkElement CorePart
   2: {
   3:    get
   4:    {
   5:       return corePart;
   6:    }
   7:    set
   8:    {
   9:       FrameworkElement oldCorePart = corePart;
  10:       if ( oldCorePart != null )
  11:       {
  12:          oldCorePart.MouseEnter -= new MouseEventHandler( corePart_MouseEnter );
  13:          oldCorePart.MouseLeave -= new MouseEventHandler( corePart_MouseLeave );
  14:          oldCorePart.MouseLeftButtonDown -= new MouseButtonEventHandler( corePart_MouseLeftButtonDown );
  15:          oldCorePart.MouseLeftButtonUp -= new MouseButtonEventHandler( corePart_MouseLeftButtonUp );
  16:       }
  17:       corePart = value;
  18:       if ( corePart != null )
  19:       {
  20:          corePart.MouseEnter += new MouseEventHandler( corePart_MouseEnter );
  21:          corePart.MouseLeave += new MouseEventHandler( corePart_MouseLeave );
  22:          corePart.MouseLeftButtonDown += new MouseButtonEventHandler( corePart_MouseLeftButtonDown );
  23:          corePart.MouseLeftButtonUp += new MouseButtonEventHandler( corePart_MouseLeftButtonUp );
  24:       }
  25:    }
  26: }

This isn’t as scary as it looks. It just says “if i’m setting the Core property, I first make a copy of my existing member variable. If that member variable is not null, i unregister all its event handlers. Next, if I was given a non-null new corepart, I register event handlers for the new value.

The event handlers look for MouseEnter/MouseLeave and for buttonDown and buttonUp.

These are translated into visual events, and each time I experience one I call a private method called GoToState. Thus,

void corePart_MouseEnter( object sender, MouseEventArgs e )
{
   isMouseOver = true;
   GoToState( true );
}

You can read this: “The clr tells me the mouse just passed over the core, that means I want to enter MouseOver state, set the flag and call my GoToState method, passing in the flag saying I do want to use transitions (that is, I want to go to the new state but using the transition timings so it doesn’t look goofy)

GoToState calls the Visual State Machine and tells it what state to transition to based on (you guessed it) the flags and whether you said to use the transitions or not)

   1: private void GoToState( bool useTransitions )
   2: {
   3:    if ( isPressed )
   4:    {
   5:       VisualStateManager.GoToState( this, "Pressed", useTransitions );
   6:  
   7:    }
   8:    else if ( isMouseOver )
   9:    {
  10:       VisualStateManager.GoToState( this, "MouseOver", useTransitions );
  11:    }
  12:    else
  13:    {
  14:       VisualStateManager.GoToState( this, "Normal", useTransitions );
  15:    }
  16:  
  17:  
  18:    
  19:    if ( IsOn )
  20:    {
  21:       VisualStateManager.GoToState( this, "On", useTransitions );
  22:    }
  23:    else
  24:    {
  25:       VisualStateManager.GoToState( this, "Norm", useTransitions );
  26:    }
  27: }

When the Visual State Manager goes to the new state it uses the storyboards that you wrote either in your generic.xaml or in your template. For example, generic.xaml’s response to isMosueOver is captured in its VisualState MouseOver which causes the control to bounce in an appealing way,

   1: <vsm:VisualState x:Name="MouseOver">
   2:     <Storyboard x:Key="Bounce" RepeatBehavior="forever" >
   3:         <DoubleAnimationUsingKeyFrames 
   4:          BeginTime="00:00:00" 
   5:          Duration="00:00:01" 
   6:          Storyboard.TargetName="Core" 
   7:          Storyboard.TargetProperty="(UIElement.RenderTransform).
   8:          (TransformGroup.Children)[3].(TranslateTransform.Y)">
   9:             <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
  10:             <SplineDoubleKeyFrame KeyTime="00:00:00.25" Value="25"/>
  11:             <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="0"/>
  12:             <SplineDoubleKeyFrame KeyTime="00:00:00.75" Value="50"/>
  13:             <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
  14:         </DoubleAnimationUsingKeyFrames>
  15:     </Storyboard>
  16: </vsm:VisualState>

The template takes the same state and does something a bit different with it, as you’d expect,

   1: <vsm:VisualState x:Name="MouseOver">
   2:     <Storyboard>
   3:         <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
   4:                 Duration="00:00:00.0010000" 
   5:                 Storyboard.TargetName="Core" 
   6:                 Storyboard.TargetProperty="(UIElement.RenderTransform).
   7:                                        (TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
   8:             <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1.25"/>
   9:         </DoubleAnimationUsingKeyFrames>
  10:         <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
  11:                  Duration="00:00:00.0010000" 
  12:                  Storyboard.TargetName="Core" 
  13:                  Storyboard.TargetProperty="(UIElement.RenderTransform).
  14:                                        (TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
  15:             <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1.25"/>
  16:         </DoubleAnimationUsingKeyFrames>
  17:     </Storyboard>
  18: </vsm:VisualState>

 

 

I have to confess, this is what I really like about custom controls; the closer you look, the more there is to see. That said, I think we’ve gone about as far as we can without exploring this in a video, so I’ll turn my attention in coming blog posts to some other emerging features.

(This is the run-up to RTW and so posting may not be as reliable as I might like, please bear with us as we work quickly to ensure that RTW happens without a hitch!)

Thanks

-jesse

Posted in z Silverlight Archives | Tagged | Comments Off on Custom Controls – The Dénouement

Tutorial & Video Info

 

I've received quite a bit of mail this week about advanced tutorials, so here's a quick note of what is planned.

As you know we have RTW on the way, but I believe these plans will not be held up by that.

1. All the existing tutorials will be updated to appear in two versions: one native to C# and one native to VB and all future tutorials will appear in two versions as well.

2. One tutorial will be released in each of October, November and December. Then, starting in January and running until at least July, I'll be publishing two somewhat shorter, more focused tutorials every month.

3. The vast majority if not all of the tutorials will be on the 300 or 400 level – that is intermediate to advanced.

4. Very short tutorials and walkthroughs will be appearing in my blog, which I am committed to publishing daily with useful info.

5. All the tutorials will be supplemented by videos, and I anticipate building to a schedule that will allow me to create approximately 75 videos per year. I will be increasing the post-production value (zoom, call-outs) while reducing the time-to-market.

5. Both the tutorials and videos will be showing more cross-platform and cross-browser material depending on interest.

Posted in z Silverlight Archives | Comments Off on Tutorial & Video Info

Is seeing demo programs on the Mac or Firefox Important?

Please take a moment to fill out this 4 question survey on whether we should be demonstrating cross-platform and cross-browser capabilities in Silverlight.

silverlight on safari

Thanks.

Posted in z Silverlight Archives | Comments Off on Is seeing demo programs on the Mac or Firefox Important?

Tag Property – The Diplomatic Pouch of Silverlight

    [updated 18:22 Eastern (utc-5) Oct 6.]

I received feedback from a few sources recently expressing confusion about the Tag property, which resides in the class FrameworkElement. 

Notice, first that Control (the base for all visible controls) derives directly from FrameworkElement and thus every control is-a Framework Element and thus every control has-a Tag. For free. No extra charge. Included. Gratis.

The Tag property is like a pocket you can stuff something into and be assured that it will be there when you need it later. It isn't type safe, but it is water-tight. This can be very handy (and can prevent a lot of needless sub-classing).

A tech reviewer on my book wrote that it can only hold strings, so I wrote what was supposed to be a quick and dirty program to show that, no, it can hold instances of classes. Worked great except for two problems:

  • Getting it to look nice took too much of the morning
  • After I was done I looked in the documentation and (as an unnamed vp candidate might say) "well shucks, say it ain't so, the docs sure do say it is string only…. dang!"

[Update: the string-only restriction was Beta 1, docs to be fixed soon]

The UI looks like this when it comes up:

TagYoureItClean

(I've added 4 numbers in blue along the right hand side so that I can refer to areas of the screen)

Digression: I really liked Algerian as a font for my drop down but it turns out that it is not a native font for Silverlight, so I had to "embed it" in the application. Blend makes this quite easy to do and it is documented in the Blend User Guide (Help file) – but be sure to read the section that applies to Silverlight 2. Briefly you bring up the Font Manager, check the font you want (in my case, Algerian), check the subset you want (I chose All glyphs) and click OK.

When you return to Visual Studio, you'll find a folder in the project named Fonts with your font in it. Very nice.

QuiteFontOfIt

That makes the drop down list go from this

BoringButton

to this

InterestingButton

 

 

(which may or may not be an improvement)

</digression>

How the Tag works

Tagging is pretty straight forward; you just assign an object to the control's tag, and you fish it out later, remembering that the Tag is of type object and so you must cast back to the "real" (e.g., runtime) type of whatever you've stored.

Setting up a test that will actually mean something is not always trivial.

Our test form works like this: you choose to check or not, the check box, to pick a radio button and to choose one of the Prog-Metal bands in the drop down.  You also fill in the fields that we'll use to create a new Person object. The Person object is defined in Person.cs like this:

   1: public class Person
   2: {
   3:    public string Name { get; set; }
   4:    public int Age { get; set; }
   5:    public string Email { get; set; }
   6:    public string Skype { get; set; }
   7:    public Person( string name, int age, string email, string skype 
   8:    {
   9:       Name = name;
  10:       Age = age;
  11:       Email = email;
  12:       Skype = skype;
  13:    }
  14: }

When you push the pushy button the first time, I gather the tags (if they are not null) and I disable all the controls except the button. When you push it the second time, I report what is in the tags using the Message text box. Being a Quick and Dirty program, if you don't touch a control, I don't set the tag, so be cooperative and touch all the controls before pressing Pushy.  A typical run looks like this:

– Check Wicked Cool (you are wicked cool, right?)

– Check Thing Two (I always liked Thing Two better)

– Drop the drop down and pick the group you like. For some reason, I find the arrow keys work best)

– Fill in the four fields, or at least all but Skype

– Press Pushy

– Press Pushy again

– Send me email with all the ways I should have improved this demo (which of course I will when I make this a video). 

You'll actually get the most out of this if you step through the code (which is here).

The key is that the second push of the Pushy button extracts all the tags, including the tag that has the Person object.  I have a note in to the developers asking if I'm misleading you showing that this seems to work. I'll get back to you, but it does seem to work.

I'm lying about my age, I'm 34. I swear.

Yes, But What Are They For?

Just before posting I received a wonderful email from Austin Avrashow who shared this anecdote from his blog

once asked a lady with an English accent working behind a sandwich counter about an ingredient of one of the sandwiches. I've heard of chutney, I told her, but what can you use it on?
She brightened up and said, "Everything!" I told her that didn't really help me

Right! I've told you that it works, and how to use it, but not what it is for.  And that, of course, is the failing in so many technical articles.  The answer is… you use it when you need to stash away information that will help you make a decision later but you have no place to put it.

Here's an example.  In my forthcoming book, I want to be able to iterate through all the objects on a page and tell which ones are my controls and which are not.

I'll be adding controls and removing controls, and there will be layout controls and all sorts of controls in the page.   There are a lot of ways to do this, but one quick and easy way is for me to put what amounts to a cookie into each of my control's Tag properties  that says "I'm what you're looking for." 

I can then pull out each control from the Children collection of the page and if they have the cookie, I know they're mine and I can take the appropriate action.

Very nice, very easy, and no need for a derived class of "ControlsICareAbout."

More soon.

Thanks.

-j

Housekeeping Notes

Quick note to readers:  While I will be able to post (nearly) every day, the 1pm deadline is not going to happen. So let's make it 5pm Eastern and I'll be early rather than late.

Second, I've received quite a bit of "where is that advanced material? email.  My response is "Yup, you're right!" With RC0 and RTW on the way I can't guarantee it will be immediate, but I can tell  you it is a priority (as is a steady stream of introductory material). 

Thanks again.

Posted in z Silverlight Archives | Comments Off on Tag Property – The Diplomatic Pouch of Silverlight

Silverlight Controls, Controls Team, and Testing

SLLogo

This blog will rarely be the place for breaking news, but there are two related pieces of information I want to ensure you know about at the risk of repeating news that has been blogged by others.  First, Shawn Burke’s team has announced their intention to release the following new Silverlight controls at PDC 08:

  • DockPanel
  • WrapPanel
  • ViewBox
  • Label
  • HeaderedContentControl
  • Expander
  • TreeView
  • NumericUpDown
  • AutoComplete
  • Accordion

Really more important than this list is the fact that Scott Guthrie (ScottGu) has asked one of our smartest and most productive managers, Shawn Burke to create and build the Controls Team (as Shawn explains here). Their new release model is well worth reading about, and they have some very exciting plans for building controls that will make coding in Silverlight that much easier.

I’m particularly interested in how the Label might interact with the PresentationControl, how the TreeView andExpander might interact with data in a client-only architecture and how the new layout controls will change the look and feel of Silverlight applications.

More than any of that, I’m eager to set aside some time to play with the Unit Test Framework they’ve developed that Shawn introduces here,  as that may well become a central application building tool.

I highly recommend reading Jeff Willcox’s writeup of his testing tool; don’t let this slip by without taking note. 

You’ll find everything you need to get started with the test tool here including additional tutorials, videos and so forth. 

Enjoy

 

-j

Posted in z Silverlight Archives | Comments Off on Silverlight Controls, Controls Team, and Testing

Template Browser – an indispensible tool

SLLogoWords

Follow up posted at bottom of article

One way that many developers like to create styles and templates for controls is to modify the existing styles and templates. An unusual feature of Silverlight is that every control in the toolbox is lookless – its appearance is determined by its own generic.xaml, just like the custom controls you create.  

Finding this generic.xaml can be a bit tricky, as it is stored in a specially named resource file, but it is all documented on MSDN.  That would be pretty good, but David Anson (aka "Delay") has created a wonderful desktop tool for inspecting (and copying) the styles and templates of the Silverlight controls

 TemplateBrowser
(Click on image for full size)

This is a really nice piece of work, allowing you to expand and collapse and spelunk the templates and styles and also to copy either or both to the clipboard to bring into your own code. I’ve added the circles and arrows to point out that the outline of elements is entirely collapsible and that the bottom of the browser offers a number of useful options.

I find this particularly useful in finding best practices for working around apparent limitations such as the changes to the ContentPresenter discussed on Sept 28.

Note that the link to the standalone tool includes the source code – but the program was written in WPF, not Silverlight.  If you want the Clickonce version, you’ll want this link.

Follow link to related artcile

 
More Templates and Custom Controls

Posted in z Silverlight Archives | Comments Off on Template Browser – an indispensible tool

Dependency Properties – Precedence

Today we wrap up dependency properties by talking about one of the most important, though typically invisible aspects: DP precedence order. This is critical because dependency properties, by their nature, are designed to have their final value set from more than one influence (e.g., resources, data binding, animation, etc.)

The rule is rigid, sensible and straight forward…

Animation uber alles.

If there is an animation affecting the value, that will take precedence over all other values

Local values over everything but Animation

In the absence of an animation, use the local value to override any other values

Templates/Styles if no animation or local values

If there is no animation or local value, use the value from the template, and if there is no template then from the style.

When all else fails, use the default

If none of the above apply, use the default value for that type.

The Default Value

It is the last rule that demands that every type have a default look, and it is because of that rule that you crated generic.xaml – so that if your custom control has a value that is not controlled by an animation, a local value, a template or a style, the Dependency Property System will turn to the default values in generic.xaml and use those.

Our First Application

With this, we're ready to examine the code to our first application.  As you may remember, we have three projects in one solution.  In CustomControl.cs we declare the visual logic of our control and we will, eventually declare the logic (event handlers, etc.) as well. For now, the listing is quite simple:

   1: using System.Windows.Controls;
   2:  
   3: namespace ClassLibrary
   4: {
   5:    public class CustomControl : Control 
   6:    {
   7:       public CustomControl()
   8:       {
   9:          DefaultStyleKey = typeof( CustomControl );
  10:       }
  11:    }
  12: }

The simplest logic you can add to get the default style to show. The default style, as you remember is in the file generic.xaml which in turn is in the Themes folder,

   1: <ResourceDictionary
   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:ClassLibrary" 
   5:     xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" 
   6:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
   7:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   8:     mc:Ignorable="d">
   9:     <Style TargetType="controls:CustomControl">
  10:         <Setter Property="Template" >
  11:             <Setter.Value>
  12:                 <ControlTemplate TargetType="controls:CustomControl">
  13:                     <Grid x:Name="LayoutRoot">
  14:                         <Ellipse x:Name="Core" 
  15:                             Width="75" Height="75" 
  16:                             Stroke="Blue" StrokeThickness="2" >
  17:                             <Ellipse.Fill>
  18:                                 <RadialGradientBrush>
  19:                                     <GradientStop Color="#FFE0E1F0" Offset="0.004"/>
  20:                                     <GradientStop Color="#FF8080B1" Offset="1"/>
  21:                                     <GradientStop Color="#FF05052B" Offset="0.911"/>
  22:                                 </RadialGradientBrush>
  23:                             </Ellipse.Fill>
  24:                         </Ellipse>
  25:                     </Grid>
  26:                 </ControlTemplate>
  27:             </Setter.Value>
  28:         </Setter>
  29:     </Style>
  30: </ResourceDictionary>

Remember to set the BuildAction property of this file to Resource

SetAsResource

Page.xaml will create an instance of your custom control and an instance of a TextBlock

   1: <UserControl x:Class="SkinnableCustomControl.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:ClassLibrary;assembly=ClassLibrary"              
   5:     Width="400" Height="300">
   6:     
   7:     <Grid x:Name="LayoutRoot" Background="White">
   8:         <Grid.RowDefinitions>
   9:             <RowDefinition Height=".7*" />
  10:             <RowDefinition Height=".3*" />
  11:         </Grid.RowDefinitions>
  12:         <Controls:CustomControl x:Name="CustomCtrl" Grid.Row="0" />
  13:         
  14:         <TextBlock Text="Skinnable Custom Control" 
  15:                    FontFamily="Georgia" FontSize="18" 
  16:                    HorizontalAlignment="Center" Grid.Row="1" />
  17:     </Grid>
  18: </UserControl>

When you run this, the custom control is drawn (along with the text block)

SkinnableCustomControlRunning

At the moment it doesn't respond to visual events such as mouse over, nor does it have any logic to handle clicks, but it does draw and it does have a default appearance. It exists (to a minimal degree) within the Parts and States model, and it is pretty straight forward from here to add the missing bits.

Next time.

 

-jesse

Posted in z Silverlight Archives | 1 Comment