Andrej Tozon's blog

In the Attic

NAVIGATION - SEARCH

Variable-sized grid items in Windows 8 apps

This is a screenshot of the Windows 8 Store app:

image

And this is what you get when you start a new Windows 8 “Windows Store Grid App”:

image_3

Wouldn’t it be great if you would be able to control the size of each particular item in your grid? To break out from dullness of having all items the same size, like, for example, to emphasize featured items; or for just any other reason?

Turns out you can do that (quite easily). I came about to this blog post describing one way to do it: “Windows 8 Beauty Tip: Using a VariableSizedWrapGrid in a GridView makes Grids Prettier” by Jerry Nixon. Reading it, I thought there had to be some easier way to do it than subclassing a GridView just for this.

And it turned out there was. Or at least it would be, if Windows 8 XAML supported Style Bindings like Silverlight 5. Not having that, the solution required that extra step that would allow binding to properties from within declared styles.

So what’s the idea?

Looking at GridView’s GroupStyle ItemsPanelTemplate after creating a new, default, Windows Store Grid App, it’s already set to VariableSizedWrapGrid – exactly what I needed. Now you just have to set its ItemWidth and ItemHeight properties to 250, and at the same time, remove those explicit sizes from the Standard250x250ItemTemplate (found in StandardStyles.Xaml). This puts GridView in control of sizing the GridViewItems.

The next step is adapting the model with extra two properties that will hold the horizontal and vertical spanning values. For this, you have to extend the provided SampleDataItem class with two new properties:

private int _horizontalSize = 1;
public int HorizontalSize
{
    get { return this._horizontalSize; }
    set { this.SetProperty(ref this._horizontalSize, value); }
}

private int _verticalSize = 1;
public int VerticalSize
{
    get { return this._verticalSize; }
    set { this.SetProperty(ref this._verticalSize, value); }
}

The default value for both is 1, which means, each item should occupy one unit of the grid only. HorizontalSize of 2 should span 2 columns, and VerticalSize of 2 should span 2 rows. Having both properties set to 2 would of course make the item occupy four units of the grid (2x2).

For this example, I only made the first item bigger.

If Windows 8 apps supported Binding from styles, I would easily finish this off by creating a new Style like:

<Style x:Key="VariableGridViewItemStyle" TargetType="GridViewItem">
  <Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="{Binding HorizontalSize}" />
  <Setter Property="VariableSizedWrapGrid.RowSpan" Value="{Binding VerticalSize}" />
</Style>

… and set this style as GridView’s ItemContainerStyle.

Unfortunately, such binding is not yet supported, so I had to find some other way to do it.

Delay’s code to the rescue! It’s a complete (and short) solution that does exactly what I needed and it just works. I had to adapt it to suit WinRT and its new reflection model a bit, but at its core it stayed the same.

Here’s the adapted style that can bind to my properties through modified Delay’s helper:

<Style x:Key="VariableGridViewItemStyle" TargetType="GridViewItem">
    <Setter Property="local:SetterValueBindingHelper.PropertyBinding">
        <Setter.Value>
            <local:SetterValueBindingHelper>
                <local:SetterValueBindingHelper
                    Type="VariableSizedWrapGrid"
                    Property="ColumnSpan"
                    Binding="{Binding HorizontalSize}" />
                <local:SetterValueBindingHelper
                    Type="VariableSizedWrapGrid"
                    Property="RowSpan"
                    Binding="{Binding VerticalSize}" />
            </local:SetterValueBindingHelper>
        </Setter.Value>
    </Setter>
</Style>

After applying this change, the result can effectively be observed in Visual Studio 2012 (thanks XAML Editor Smile), and here’s how the app looks when running:

image_4

I like this approach because it’s XAML-only - it doesn’t force me to use a derived control and I can specify the sizing properties I want to bind to through my style resource. Which makes the solution a bit more flexible. The downside is of course this requirement of having a Style binding helper class, but I really really hope the Windows 8 gets this built in ASAP.

To close, here’s the modified version of Delay’s SetterValueBindingHelper (not really tested yet!):

public class SetterValueBindingHelper
{
    /// <summary>
    /// Optional type parameter used to specify the type of an attached
    /// DependencyProperty as an assembly-qualified name, full name, or
    /// short name.
    /// </summary>
    [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
        Justification = "Unambiguous in XAML.")]
    public string Type { get; set; }

    /// <summary>
    /// Property name for the normal/attached DependencyProperty on which
    /// to set the Binding.
    /// </summary>
    public string Property { get; set; }

    /// <summary>
    /// Binding to set on the specified property.
    /// </summary>
    public Binding Binding { get; set; }

    /// <summary>
    /// Collection of SetterValueBindingHelper instances to apply to the
    /// target element.
    /// </summary>
    /// <remarks>
    /// Used when multiple Bindings need to be applied to the same element.
    /// </remarks>
    public Collection<SetterValueBindingHelper> Values
    {
        get
        {
            // Defer creating collection until needed
            if (null == _values)
            {
                _values = new Collection<SetterValueBindingHelper>();
            }
            return _values;
        }
    }

    private Collection<SetterValueBindingHelper> _values;

    /// <summary>
    /// Gets the value of the PropertyBinding attached DependencyProperty.
    /// </summary>
    /// <param name="element">Element for which to get the property.</param>
    /// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "SetBinding is only available on FrameworkElement.")]
    public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
    {
        if (null == element)
        {
            throw new ArgumentNullException("element");
        }
        return (SetterValueBindingHelper) element.GetValue(PropertyBindingProperty);
    }

    /// <summary>
    /// Sets the value of the PropertyBinding attached DependencyProperty.
    /// </summary>
    /// <param name="element">Element on which to set the property.</param>
    /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
        Justification = "SetBinding is only available on FrameworkElement.")]
    public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
    {
        if (null == element)
        {
            throw new ArgumentNullException("element");
        }
        element.SetValue(PropertyBindingProperty, value);
    }

    /// <summary>
    /// PropertyBinding attached DependencyProperty.
    /// </summary>
    public static readonly DependencyProperty PropertyBindingProperty =
        DependencyProperty.RegisterAttached(
            "PropertyBinding",
            typeof (SetterValueBindingHelper),
            typeof (SetterValueBindingHelper),
            new PropertyMetadata(null, OnPropertyBindingPropertyChanged));

    /// <summary>
    /// Change handler for the PropertyBinding attached DependencyProperty.
    /// </summary>
    /// <param name="d">Object on which the property was changed.</param>
    /// <param name="e">Property change arguments.</param>
    private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Get/validate parameters
        var element = (FrameworkElement) d;
        var item = (SetterValueBindingHelper) (e.NewValue);

        if ((null == item.Values) || (0 == item.Values.Count))
        {
            // No children; apply the relevant binding
            ApplyBinding(element, item);
        }
        else
        {
            // Apply the bindings of each child
            foreach (var child in item.Values)
            {
                if ((null != item.Property) || (null != item.Binding))
                {
                    throw new ArgumentException(
                        "A SetterValueBindingHelper with Values may not have its Property or Binding set.");
                }
                if (0 != child.Values.Count)
                {
                    throw new ArgumentException(
                        "Values of a SetterValueBindingHelper may not have Values themselves.");
                }
                ApplyBinding(element, child);
            }
        }
    }

    /// <summary>
    /// Applies the Binding represented by the SetterValueBindingHelper.
    /// </summary>
    /// <param name="element">Element to apply the Binding to.</param>
    /// <param name="item">SetterValueBindingHelper representing the Binding.</param>
    private static void ApplyBinding(FrameworkElement element, SetterValueBindingHelper item)
    {
        if ((null == item.Property) || (null == item.Binding))
        {
            throw new ArgumentException(
                "SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
        }

        // Get the type on which to set the Binding
        Type type = null;
        TypeInfo typeInfo = null;
        if (null == item.Type)
        {
            // No type specified; setting for the specified element
            type = element.GetType();
            typeInfo = type.GetTypeInfo();
        }
        else
        {
            // Try to get the type from the type system
            type = System.Type.GetType(item.Type);
            if (null == type)
            {
                // Search for the type in the list of assemblies
                foreach (var assembly in AssembliesToSearch)
                {
                    // Match on short or full name
                    typeInfo = assembly.DefinedTypes
                        .Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
                        .FirstOrDefault();
                    if (null != typeInfo)
                    {
                        // Found; done searching
                        break;
                    }
                }
                if (null == typeInfo)
                {
                    // Unable to find the requested type anywhere
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                                                              "Unable to access type \"{0}\". Try using an assembly qualified type name.",
                                                              item.Type));
                }
            }
            else
            {
                typeInfo = type.GetTypeInfo();
            }
        }
        
        // Get the DependencyProperty for which to set the Binding
        DependencyProperty property = null;
        var field = typeInfo.GetDeclaredProperty(item.Property + "Property"); // type.GetRuntimeField(item.Property + "Property");
        if (null != field)
        {
            property = field.GetValue(null) as DependencyProperty;
        }
        if (null == property)
        {
            // Unable to find the requsted property
            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                                                      "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
                                                      item.Property, type.Name));
        }

        // Set the specified Binding on the specified property
        element.SetBinding(property, item.Binding);
    }

    /// <summary>
    /// Returns a stream of assemblies to search for the provided type name.
    /// </summary>
    private static IEnumerable<Assembly> AssembliesToSearch
    {
        get
        {
            // Start with the System.Windows assembly (home of all core controls)
            yield return typeof(Control).GetTypeInfo().Assembly;
        }
    }
}

Display “My Pictures” in Silverlight application at design time

Following up on The ultimate hack for Silverlight in Blend post from Josh Smith, I tried to make Blend display pictures from the My Pictures folder right in my Silverlight application. Needless to say, it worked as advertised :)

Blend

The ViewModel is set through d:DataContext:

public class MainPageViewModel
{
    public MainPageViewModel()
    {
        SetLocalPictures();
    }

    private void SetLocalPictures()
    {
        string folder = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
        IEnumerable<string> pictures = Directory.EnumerateFiles(folder);
        Pictures = (
            from p in pictures 
            select new Uri(string.Format("file://{0}", p.Replace('\\', '/')), UriKind.Absolute)
            ).ToArray();
    }

    public Uri[] Pictures { get; set; }
}

The thing is that this code wouldn’t work with Silverlight application running in a non-trust mode – it would throw a security exception. However, setting this ViewModel as the run-time DataContext and running with elevated permissions, the pictures would get displayed as well.

And of course this works in Visual Studio 2010 designer too:

VS2010

A nice alternative for Blend’s sample data…



Halloween Live Gallery

With Halloween around the corner, it’s time for some scary photos… In this post I’ll explain how I built my demo application I showed at my Windows 7 Launch talk.

Expression Blend 3 shipped with a few interesting samples, available for immediate use. One of them is called Wall3D (Silverlight 3) and is a great example of one of the new Silverlight 3D features, called perspective transform.

image

This sample (perhaps a starter kit is a better description for it) features a 3D photo gallery, which sort of puts the viewer in the center of an oval “room”, full of photos, with an option to rotate the view to the left or right (flick / mouse click and pan), zoom in or out (mouse wheel), etc.. The kind of what people used to do in Flash; now it’s possible in Silverlight too. The sample serves also as a demonstration of yet another new Blend 3 feature, called Sample data – the photos on the above image were generated through this feature. Let’s explore the sample and see how we can push it further, shall we?

After opening Blend 3, switch to the Samples page and choose Wall3D (Silverlight 3) and the default project will be generated for you. image

You can test the application by hitting F5. You navigate through the gallery by flicking the view left or right and zoom in or out with your middle mouse button.

Examining the code that’s been created in the project will reveal the following:

  • the photos come from the sample data source (XML + photos, which are also included in the project – look into the SampleData folder)
  • “The wall” is driven by the CircularPanel3D control (a custom panel)

Knowing this, we’re ready to create our own 3D gallery. We’ll only need a single thing from this sample project, and that’s the CircularPanel3D.cs file, found in the root of the project. Copy and save that in some other folder for later reference, then start a new Silverlight 3 Application project in Blend.

I used an ItemsControl instead of ListBox to arrange and display the photos. Of course both will accept CircularPanel3D as their ItemsPanel, I just didn’t need item selection in this case. Here’s how it all looked in Blend designer:

Now what’s wrong with this picture? It’s hard to design something when you’ve got no visual feedback to help you with the layout and arrangements. I have already modified the ItemTemplate, but without the actual photos, it’s hard to do proper design work.  And that’s where the sample data comes in. To create a sample data source, open the Data panel and click on the second icon from the right image (Add sample data source). Choose Define New Sample Data, name the data source and click OK. You can shape the sample data source however you like – flat, hierarchical, with entities having simple or complex type properties, etc. For this gallery, I created the following source:

image

Bind this data source to the ItemsControl as its items source – voila!

Cool, much better. Modifying the ItemTemplate has just become a lot easier. Let’s add another element for displaying a bit more details about the photo (like the title and location where it was taken).

Photo view Details view

So we’ve got two views for the ItemTemplate, each view being represented with a separate element – one’s with the photo and the second one with details. We have to provide a way for user to switch between the two; how would we do this? Write code? Call the developer? Not necessarily. If we’re in luck, the appropriate piece of code may have already been written, waiting to be taken it used in Blend for declaratively specifying whatever we wanted to do to those elements and how. That piece of code is called a behavior. There are several places on the web to look for behaviors (as well as more info on what they are and how to produce them), but my first bets would be the Expression Gallery and Expression Blend Samples library on Codeplex; the letter containing some very handy behaviors.

I added a couple of behaviors to add interaction to gallery elements - FlipAction would switch the photo and details part through flip motion, triggered by DoubleClick trigger; and ScaleBehavior would enlarge the photo on mouse over. A couple of clicks for very cool-looking effect.

Let’s take a look at the data source... I decided to use Flickr to retrieve the photos, because it’s API is easy to use and can (amongst other things) return geo-location of the place where photo was taken (when available). This geo-location is then passed to the Bing Maps geocoding web serviceas an input parameter to retrieve the real-world address; these addresses can be seen on the bottom of the flipped photo.

image

Playing with geo-location was fun so I decided to take it a step further. I put together a special ShowMapAction, which is currently triggered by a mouse wheel over the photo (or details). The action simply shows the location on the interactive Bing Map (I used Bing Maps Silverlight Map Control for this).

image

Just a word of caution: Geo-encoded photos are cool, but be careful what you’re publishing on the net. The information may not always be used for good purposes.

The final result can be seen here (live). Right in time for keeping track with what’s going on on Halloween.

Finally I have to say I’m spending a lot more time in Blend since version 3 got out. It’s evolving into a great tool and behaviors are going to take a significant part in designer’s/integrator’s role in the future.

http://tozon.info/gallery/



Windows 7 Launch talk: Building cool applications with MS Expression tools

Windows 7 launch day was fun. I gave a talk on Expression tools (and related) – here’s the PowerPoint slide deck for those who asked for it:

I used Expression Blend 3 to build a photo viewer application, which I’ll blog about later; key points here were designer-developer workflow + using sample data and behaviors.

I blogged about the Microsoft ICE last year and I used that exact sample I’ve used in that blog post, but with one difference: did you know that by installing ICE on your desktop will, you can stitch panoramic (or large composite) photos right from your Windows Live Photo Gallery? It’s as easy as selecting the photos and click on the Extras menu item:

Create Image Composite...

Yet another option for stitching a bunch of photos together will give you the Deep Zoom Composer(free and not listed as an Expression tool, but sure looks and feels like it), which has similar features and much more compositing power (different zoom levels, layers, etc.)

And for closing I briefly showcased Expression Encoder 3 and IIS Smooth Streaming.

Yup, fun.



Dynamic Data Forms for Silverlight, revisited

A couple of weeks ago, I wrote a post about handling dynamic forms in Silverlight. With this continuation post, I’m going to make a few changes to the original project:

1. Implement a new, custom, field type and provide a template for it (I’ll write about it later), and
2. Make a few changes to the FormFieldTemplateSelector to make it more generic. So instead of writing templates like this:

A new TemplateSelectorDataTemplate…

<local:FormFieldTemplateSelector.StringTemplate>
    <DataTemplate>
        <TextBox Text="{Binding Value, Mode=TwoWay}" Width="100"/>
    </DataTemplate>
</local:FormFieldTemplateSelector.StringTemplate>
<local:FormFieldTemplateSelector.DateTimeTemplate>
    <DataTemplate>
        <basics:DatePicker SelectedDate="{Binding Value, Mode=TwoWay}" Width="100" />
    </DataTemplate>
</local:FormFieldTemplateSelector.DateTimeTemplate>

You could simply write:

<local:TemplateSelectorDataTemplate DataType="System.String">
    <TextBox Text="{Binding Value, Mode=TwoWay}" Width="100"/>
</local:TemplateSelectorDataTemplate>
<local:TemplateSelectorDataTemplate DataType="System.DateTime">
    <basics:DatePicker SelectedDate="{Binding Value, Mode=TwoWay}" Width="100" />
</local:TemplateSelectorDataTemplate>

You can see the DataType property on the TemplateSelectorDataTemplate used instead of using concrete templates like FormFieldTemplateSelecot.StringTemplate. This way, the FormFieldTemplateSelector’s DataType property can be matched with TemplateSelectorDataTemplate’s DataType property [long names confusion, I know] and you don’t have to factor a new template for each new field type you come with, making it feel a bit more like the DataTemplate implementation in WPF. Strings are used here instead of types. One reason for this is that Silverlight doesn’t support x:Type markup, and the second is the fact you can use any string to identify your field type: for example, you can use “#Signature#” string as a custom field type to insert a signature control into your form.

A written signature form field…

To demonstrate a support for custom field types, I created a special control, which will allow the user to sign herself on a special panel and the signature will be submitted to the server together with the other field values.

The control, which will allow us to write on it with the mouse, is of course InkPresenter. I’ve encapsulated it in another control, called SignaturePanel, which adds required mouse event handler which enables the user to write on the InkPresenter with a mouse. Here’s this control, contained within a new TemplateSelectorDataTemplate:

<local:TemplateSelectorDataTemplate DataType="#Signature#"> <local:SignaturePanel Width="100" Height="50"
Strokes="{Binding Value, Mode=TwoWay, Converter={StaticResource strokesConverter}}" /> </local:TemplateSelectorDataTemplate>

The template looks nothing special, except that strokesConverter, specified with the binding. What that does is serialize the Strokes collection from the InkPresenter when being written to the underlying ViewModel, where we need it when sending all submitted data over to the server side. This means that signature actually gets sent over the wire in a form of strokes collection, which you can store in a database. Alternatively, you could convert that strokes into the bitmap on the server and store the signature as an image [And I’ve yet to check how Silverlight 3 WritableBitmap can help doing this on the client]. Anyway, I’ve found StrokeCollection (de)serialization code in Julia Lerman’s Create Web Apps You Can Draw On with Silverlight 2 MSDN article and is included in the code sample attached to this post.

Here’s how the current form looks like:

image

Source code is now available. Please note that this is a Silverlight 3 project. It doesn’t contain any SL3 code, it’s just the project files were converted to SL3 when opened in Visual Studio.

All comments welcome. Enjoy.

Dynamic Data Forms for Silverlight with a Data Template Selector Control

The basic idea behind this post is to show a simple way for items/list controls to display each item data with a different template. The ideal candidate for such exercise are data forms, where user can enter different kinds of information - text, numbers, check marks, etc. Imagine a scenario, where each data form field would be presented as a separate ListBox item… no matter of what data type that field is.

The data model for this solution is going to be very simple and generic. We’ll need a generic class, which will represent a single field in a form. Each field will have an id, a caption, a value (entered by the user) and a value type (what’s the type of the value – text, integer, date, etc.):

public class AutoFormField
{
    public Guid Id { get; private set; }
    public string Caption { get; set; }
    public string Value { get; set; }
    public string Type { get; set; }

    public AutoFormField()
    {
        Id = Guid.NewGuid();
    }
}

When the user requests a form, the associated web service will return a collection of AutoFormFields, representing all fields in the form that user should fill out. I’m not using any database for this example, so I’ve hardcoded the fields, returning to the client, in the service itself:

public class AutoFormService : IAutoFormService
{
    public Collection<AutoFormField> GetForm()
    {
        Collection<AutoFormField> form = new Collection<AutoFormField>();
        form.Add(new AutoFormField()
        {
            Caption = "Name: ",
            Type = typeof(string).FullName
        });
        form.Add(new AutoFormField()
        {
            Caption = "Last name: ",
            Type = typeof(string).FullName
        });
        form.Add(new AutoFormField()
        {
            Caption = "Birth date: ",
            Type = typeof(DateTime).FullName
        });
        form.Add(new AutoFormField()
        {
            Caption = "Is employed: ",
            Type = typeof(bool).FullName,
            Value = false.ToString()
        });
        return form;
    }
}
 
The above code shows that the form consists of two text fields, a date and a yes/no field. We would naturally want to display TextBoxes for text entry, DatePicker for a date, and a CheckBox for a yes/no field entry. The Type property contains an identifier, which will tell the client what kind of entry field to display; field data type names are used here for convenience.
Let’s move to the client and implement this.
 
I’ll use the ItemsControl to display the items, because I don’t need the notion of a selected item (yet). Let’s first get the basics out of the way; this is how the ItemsControl looks like, displaying all the form’s captions:
 
<ItemsControl ItemsSource="{Binding Fields, Source={StaticResource viewModel}}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Caption}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
Its ItemsSource property is bound to a ViewModel’s Fields property, which gets populated when a call to the GetForm() web service method returns. The ItemTemplate is set up so that TextBlock displays the fields’ captions.

What we need to do next is display the actual entry fields the user will fill in. I already mentioned the Type property; this will be the key in deciding what kind of control to display.

If we would be coding this in WPF, we would use a DataTemplateSelector to help us out with selecting the right template to load for a specific type. Unfortunately, this feature is not implemented in Silverlight, so we’re on our own here. As it turns out, there isn’t much code required to come up with a solution. This is something that I call…

… a data template selector control…

For our example, we need templates for: a TextBox, a DatePicker and a CheckBox. The following user control will accept all these templates as properties and contain logic to choose between them based on a certain property, which will be bound to AutoFormField’s Type property:

public class FormFieldTemplateSelector : UserControl { public DataTemplate StringTemplate { get; set; } public DataTemplate DateTimeTemplate { get; set; } public DataTemplate BooleanTemplate { get; set; } public DataTemplate SignatureTemplate { get; set; } public static readonly DependencyProperty FieldTypeProperty = DependencyProperty.Register("FieldType", typeof(string), typeof(FormFieldTemplateSelector), new PropertyMetadata(string.Empty));

 

public string FieldType { get { return (string)GetValue(FieldTypeProperty); } set { SetValue(FieldTypeProperty, value); } } public FormFieldTemplateSelector() { Loaded += new RoutedEventHandler(OnLoaded); } private void OnLoaded(object sender, RoutedEventArgs e) { string fieldType = FieldType; if (fieldType == typeof(string).FullName) { Content = StringTemplate.LoadContent() as UIElement; } else if (fieldType == typeof(DateTime).FullName) { Content = DateTimeTemplate.LoadContent() as UIElement; } else if (fieldType == typeof(bool).FullName) { Content = BooleanTemplate.LoadContent() as UIElement; } else { Content = null; } } }

The way to set it up in the item template is simple too. Here’s the complete ItemTemplate:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition MinWidth="100" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding Caption}" VerticalAlignment="Center" />
    <local:FormFieldTemplateSelector Grid.Column="1" FieldType="{Binding Type}" Margin="0,2,0,2">
        <local:FormFieldTemplateSelector.StringTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Value, Mode=TwoWay}" Width="100"/>
            </DataTemplate>
        </local:FormFieldTemplateSelector.StringTemplate>
        <local:FormFieldTemplateSelector.DateTimeTemplate>
            <DataTemplate>
                <basics:DatePicker SelectedDate="{Binding Value, Mode=TwoWay}" Width="100" />
            </DataTemplate>
        </local:FormFieldTemplateSelector.DateTimeTemplate>
        <local:FormFieldTemplateSelector.BooleanTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding Value, Mode=TwoWay}" />
            </DataTemplate>
        </local:FormFieldTemplateSelector.BooleanTemplate>
    </local:FormFieldTemplateSelector>
</Grid>

Of course, this is just one way to put it together. You're free to style each field completely to your liking. This is what we have so far:

To wrap this up, you just have to add a button to submit the form back to the server. I’m not discussing this here since it’s pretty straightforward.

I will, however, continue to build up on this example in the next couple of posts. Some topics I’ll dig into is styling, positioning and more (interesting) templates. I’ll also post the full code in the final post of this series.

Disabling items in a Silverlight ListBox

This is a quick tip on making the ListBox items behave as disabled.

You know the semaphore, right? STOP on red, GO on green? OK. I’m drilling this with my two-year old when driving her to kindergarten every morning so lately it’s probably stuck in my mind a bit more than it should be.

OK. This is a ListBox:

image

Yes, it is:

<ListBox ItemsSource="{Binding Lights}" BorderThickness="0">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Ellipse Fill="{Binding Brush}" Width="50" Height="50" Stroke="Black" Cursor="Hand" />
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem" >
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <ContentPresenter x:Name="contentPresenter" Margin="4" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

The data context of this ListBox is a ViewModel with the Lights property. Lights is a collection of five Light objects.

One property that the Light object exposes is the Brush property; it will simply point to a solid color brush of a red, yellow or green color, respectively. The Ellipse’s Fill property is bound to the Brush property as can observed in the above code.

Semaphores usually change colors in an order. The ones in this sample will blink randomly, changing their colors in 2 second intervals:

public Light()
{
    this.SetColor();
    DispatcherTimer timer = new DispatcherTimer()
    {
        Interval = TimeSpan.FromSeconds(2.0)
    };
    
    timer.Tick += (s, e) => { this.SetColor(); };
    timer.Start();
}

private void SetColor()
{
    Color[] colorArray = new Color[] { Colors.Red, Colors.Yellow, Colors.Green };
    Color color = colorArray[randomizer.Next(0, 3)];
    this.Brush = new SolidColorBrush(color);
    this.IsEnabled = color == Colors.Green;
}

Now, we want to allow the user click only on green light, OK? The last line in the above SetColor method sets the IsEnabled property on the Light object only when the current color is set to Green.

With the IsEnabled property up and about, it gets really easy. To prevent the user from clicking on the ListBox item, simply set bind its IsHitTestVisible property to IsEnabled property on the Light class (bold underlined part added):

<ControlTemplate TargetType="ListBoxItem">
    <ContentPresenter x:Name="contentPresenter" Margin="4" 
        IsHitTestVisible="{Binding IsEnabled}" Cursor="Hand" /> </ControlTemplate>

[Cursor property is there for instant visual feedback on when the item is enabled]

That’s it, really. You can test it by adding i.e. a Click event handler to the contentPresenter and display a message box or something (included in the sample below). You would also want to build a better templates for the enabled/disabled items. If built around the bound IsEnabled property, it should work fine.

Silverlight TreeView: MVVM and editing [5 – Add]

In the previous post, we enabled editing for our TreeView. The last operation we need to implement is adding a new item to the hierarchy. With our tree item editor in place, there’s not so much left to do…

The only change we need to make with the View is add an Add button, right next to the Edit button. We’ll wire it to the Add command, which already exists.

The next stop is the ViewModel. We need to change the existing OnAddTopicExecuted handler to add a new child to the HelpTopic, passed into the method through CommandParameter:

private void OnAddTopicExecuted(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = new HelpTopic();
    HelpTopic parent = e.Parameter as HelpTopic;
    if (parent == null)
    {
        HelpTopics.Add(topic);
    }
    else
    {
        parent.SubTopics.Add(topic);
    }
    topic.BeginEdit();
}

Note: when CommandParameter is not set to a valid HelpTopic (when it’s null), we’re adding a child to the root.

There is something wrong with the code above. I’m assuming that the SubTopics collection was already initialized when I created a new HelpTopic class instance. I this case, I assumed wrong and the above code would throw a nasty error.

I need to assure that SubTopics property is never null as this puts the HelpTopic in an invalid state. I’ll do that by inserting the collection initialization code in its constructor:

public HelpTopic()
{
    SubTopics = new ObservableCollection<HelpTopic>();
}

Better. But we’re not done yet. There is no way for user to cancel the newly added item. Sure, she can press the Cancel button when editing, but the node will not be removed from the tree.

This is the time to provide the HelpTopic class with an unique identificator field. When working with the database, the type of this field would align with whatever your database table primary key type is. For simplicity, I’ll use Guids here, just because their generation results in more unique results than a number randomization would.

The logic behind is this: when a new HelpTopic is being added, its Id is set to null. If the item is confirmed, the new Id is set, otherwise the item is removed from the hierarchy. Here’s how modified OnEndEditTopic and OnCancelEditTopic now look like:

private void OnEndEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    if (!topic.Id.HasValue)
    {
        topic.Id = Guid.NewGuid();
    }
    topic.EndEdit();
}

private void OnCancelEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    if (!topic.Id.HasValue)
    {
        topic.RemoveFromStructure(HelpTopics, item => { return item.SubTopics; });
    }
    topic.CancelEdit();
}

Wait! Before we end this, let me just put another button in a TreeView to enable user to add a new HelpTopic to the root of the tree. This is the modified template with a new button in place (added lines in bold italic):

<ControlTemplate TargetType="slt:TreeView">
    <Grid x:Name="Root" >
        <Grid.Resources>
            <SolidColorBrush x:Key="BorderBrush" Color="#FF000000" />
        </Grid.Resources>
        <Border x:Name="Border" BorderBrush="{StaticResource BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"> <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" Margin="1"> <ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Auto"
                    VerticalScrollBarVisibility="Auto" Background="{x:Null}" BorderBrush="Transparent"
                    BorderThickness="0" IsTabStop="False" TabNavigation="Once"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Button HorizontalAlignment="Left" ToolTipService.ToolTip="Add Topic" input:CommandService.Command="AddTopic"> <Image Source="/EditableTreeView;component/Resources/add.png" Stretch="None" /> </Button> <ItemsPresenter x:Name="TreeItems" Grid.Row="1" Margin="5" /> </Grid> </ScrollViewer> </Border> </Border> </Grid> </ControlTemplate>

Editable TreeView

OK, we’ve got our TreeView to allow basic inline editing now, but not in very realistic scenario and it lacks usability. I’m going to end this series with this post and spin off to more general topics posts, covering more MVVM, LOB scenarios and Silverlight in general. A taste of things to come: improving, extending and expanding on the current state of this sample, going over the wire, testing and user experience.

The source code for this sample is available:

Silverlight TreeView: MVVM and editing [4 – Edit]

In the fourth post in this series we’ll get to actually edit an item in the TreeView.

First, we have to make our HelpTopic class editable. Well, yes, it is editable now, but we’ll make it even more editable :) by

a] implementing the IEditableObject interface. IEditableObject is defined as:

public interface IEditableObject
{
    void BeginEdit();
    void CancelEdit();
    void EndEdit();
}

The method names in the interface are so straightforward that I shouldn’t need to explain them here. However, one method which we benefit most from when our business object implements this interface, is CancelEdit. Why? Well, let’s say you have your TextBox twoway-bound to some object’s property. When you change the value in this TextBox and tab out, the changes are committed to your object and you have no options to undo this change. If you wanted to provide the OK/Cancel button pair for user to either confirm or undo her changes, you have a slight problem.

The IEditableObject interface is here to help. The CancelEdit method is responsible for reverting your object to the state it was before BeginEdit was called.

One of the Silverlight controls that supports this interface is DataGrid, so making your objects implement this interface will make them work consistently with DataGrid scenarios too.

Let’s see how our HelpTopic would implement the interface:

public class HelpTopic : IEditableObject
{
    private string nameCopy;

    public string Name { get; set; }
    public ObservableCollection<HelpTopic> SubTopics { get; set; }

    public HelpTopic()
    {
    }

    public void BeginEdit()
    {
        nameCopy = this.Name;
    }

    public void CancelEdit()
    {
        Name = nameCopy;
    }

    public void EndEdit()
    {
    }
}

I’ve introduced a new private variable to store the value of the name property in case the CancelEdit is called. When dealing with complex objects, you would want to store a deep clone of your object instead of storing each and every (sub)property in a separate variable.

b] creating a new property, which would indicate that the object is in edit mode. We would set this property within these three new methods. It is important that changes to this property are propagated to the UI, so we also need:

c] implementing the INotifyPropertyChanged interface. This interface is not new, our PageViewModel already implements it. When implementing this interface, we can also change the Name property implementation to support notifications.

The full HelpTopic class now looks like:

public class HelpTopic : IEditableObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool isEditing;
    private string name;
    private string nameCopy;

    public ObservableCollection<HelpTopic> SubTopics { get; set; }

    public string Name
    {
        get { return name; }
        set
        {
            if (name == value)
            {
                return;
            }

            name = value;
            OnPropertyChanged("Name");
        }
    }

    public bool IsEditing
    {
        get { return isEditing; }
        private set
        {
            if (isEditing == value)
            {
                return;
            }

            isEditing = value;
            OnPropertyChanged("IsEditing");
        }
    }

    public HelpTopic()
    {
    }

    public void BeginEdit()
    {
        nameCopy = this.Name;
        IsEditing = true;
    }

    public void CancelEdit()
    {
        Name = nameCopy;
        IsEditing = false;
    }

    public void EndEdit()
    {
        IsEditing = false;
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

[Edit: changed IsEditing setter above to private; the property should be set from within the class only]

Moving upwards to the ViewModel, we need another three commands for invoking the new methods – BeginEditTopic, EndEditTopic and CancelEditTopic commands. Their ViewModel implementation is, again, pretty simple and straightforward:

private void OnBeginEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    topic.BeginEdit();
}

private void OnEndEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    topic.EndEdit();
}

private void OnCancelEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    topic.CancelEdit();
}

All three methods just call their corresponding method on the HelpTopic item, passed into the method as a command parameter.

Now for the UI part… I guess I *could* do this with Xaml only, but I decided on building a new content control to pose as an View/Edit item switcher. The main reason for doing this is that I didn’t want end up with every item in the tree carry its own editor within its template. Instead, this new control will be responsible for loading the editor when one is needed.

In short, things are set up like this: I have this new control called EditableItemControl, pushed to somewhere near the top of the TreeViewItem’s visual tree with its contents set to what original item would look like. EditableItemControl has a property called EditorTemplate, which should be set to what the tree item should look like in edit mode. This is the EditorTemplate I used:

<Grid >
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <TextBox Text="{Binding Name, Mode=TwoWay}" 
        input:CommandService.Command="EndEditTopic"
        input:CommandService.CommandParameter="{Binding}" 
    />
    <Button Name="saveButton" ToolTipService.ToolTip="OK" Grid.Column="1"
        input:CommandService.Command="EndEditTopic"
        input:CommandService.CommandParameter="{Binding}">
        <Image Source="/EditableTreeView;component/Resources/ok.png" Stretch="None" />
    </Button>
    <Button Name="closeButton" ToolTipService.ToolTip="Cancel" Grid.Column="2"
        input:CommandService.Command="CancelEditTopic"
        input:CommandService.CommandParameter="{Binding}">
        <Image Source="/EditableTreeView;component/Resources/cancel.png" Stretch="None" />
    </Button>
</Grid>

When editing is initialized, control’s original content is preserved and replaced with the above editor template, just to be switched back when editing is over. There are two ways to commit the changes – one is to press the saveButton, and the other is to press the Return key focused on the TextBox. Note that the TextBox is also wired to the EndEditTopic command, and TextBox control being one of the actionable elements in the SLExtensions framework, this simply works.

The key to make the switch between view and edit states is EditableItem’s IsEditing dependency property – the control goes to edit state when this property is set to true, and returns to its normal state when its value is set back to false. Ok, but how is this property set? Remember that our HelpTopic class also implements a property with the same name? They fit perfectly together:

<local:EditableTreeViewItem Grid.ColumnSpan="3" 
       IsEditing="{Binding IsEditing}" 
       HorizontalContentAlignment="Stretch">
    ...
</local:EditableTreeViewItem>

To tie all the ends together – when the Edit button is pressed, BeginEdit command is executed. ViewModel handles that command by calling the BeginEdit method on the HelpTopic item, for which the command was executed. IsEditing property gets set for this item and at the same time picked up by the EditableItemControl, which does the switch and display the editor for it. It’s like playing The Incredible Machine!

OK, we made the TreeView control editable. However, there are a few visual issues – one of them is that a mouse-over in a subitem shows the edit/delete commands for its ancestors all the way up to the root. I don’t know whether this is a default behavior for the TreeView or a bug of some sort, but until I find out, I’m leaving it as it is. I did use a TreeViewItem’s MouseOver state which wasn’t in the original template, so it may be that this state is not (properly) supported. Anyway, I’ll definitely get back to this, but first we have some adding to do.

About to edit an item

Editing an item

Next in the series: adding an item

The source code for this sample is available:

Silverlight TreeView: MVVM and editing [4 – Edit]

In the fourth post in this series we’ll get to actually edit an item in the TreeView.

First, we have to make our HelpTopic class editable. Well, yes, it is editable now, but we’ll make it even more editable :) by

a] implementing the IEditableObject interface. IEditableObject is defined as:

public interface IEditableObject
{
    void BeginEdit();
    void CancelEdit();
    void EndEdit();
}

The method names in the interface are so straightforward that I shouldn’t need to explain them here. However, one method which we benefit most from when our business object implements this interface, is CancelEdit. Why? Well, let’s say you have your TextBox twoway-bound to some object’s property. When you change the value in this TextBox and tab out, the changes are committed to your object and you have no options to undo this change. If you wanted to provide the OK/Cancel button pair for user to either confirm or undo her changes, you have a slight problem.

The IEditableObject interface is here to help. The CancelEdit method is responsible for reverting your object to the state it was before BeginEdit was called.

One of the Silverlight controls that supports this interface is DataGrid, so making your objects implement this interface will make them work consistently with DataGrid scenarios too.

Let’s see how our HelpTopic would implement the interface:

public class HelpTopic : IEditableObject
{
    private string nameCopy;

    public string Name { get; set; }
    public ObservableCollection<HelpTopic> SubTopics { get; set; }

    public HelpTopic()
    {
    }

    public void BeginEdit()
    {
        nameCopy = this.Name;
    }

    public void CancelEdit()
    {
        Name = nameCopy;
    }

    public void EndEdit()
    {
    }
}

I’ve introduced a new private variable to store the value of the name property in case the CancelEdit is called. When dealing with complex objects, you would want to store a deep clone of your object instead of storing each and every (sub)property in a separate variable.

b] creating a new property, which would indicate that the object is in edit mode. We would set this property within these three new methods. It is important that changes to this property are propagated to the UI, so we also need:

c] implementing the INotifyPropertyChanged interface. This interface is not new, our PageViewModel already implements it. When implementing this interface, we can also change the Name property implementation to support notifications.

The full HelpTopic class now looks like:

public class HelpTopic : IEditableObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool isEditing;
    private string name;
    private string nameCopy;

    public ObservableCollection<HelpTopic> SubTopics { get; set; }

    public string Name
    {
        get { return name; }
        set
        {
            if (name == value)
            {
                return;
            }

            name = value;
            OnPropertyChanged("Name");
        }
    }

    public bool IsEditing
    {
        get { return isEditing; }
        private set
        {
            if (isEditing == value)
            {
                return;
            }

            isEditing = value;
            OnPropertyChanged("IsEditing");
        }
    }

    public HelpTopic()
    {
    }

    public void BeginEdit()
    {
        nameCopy = this.Name;
        IsEditing = true;
    }

    public void CancelEdit()
    {
        Name = nameCopy;
        IsEditing = false;
    }

    public void EndEdit()
    {
        IsEditing = false;
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

[Edit: changed IsEditing setter above to private; the property should be set from within the class only]

Moving upwards to the ViewModel, we need another three commands for invoking the new methods – BeginEditTopic, EndEditTopic and CancelEditTopic commands. Their ViewModel implementation is, again, pretty simple and straightforward:

private void OnBeginEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    topic.BeginEdit();
}

private void OnEndEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    topic.EndEdit();
}

private void OnCancelEditTopic(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;
    topic.CancelEdit();
}

All three methods just call their corresponding method on the HelpTopic item, passed into the method as a command parameter.

Now for the UI part… I guess I *could* do this with Xaml only, but I decided on building a new content control to pose as an View/Edit item switcher. The main reason for doing this is that I didn’t want end up with every item in the tree carry its own editor within its template. Instead, this new control will be responsible for loading the editor when one is needed.

In short, things are set up like this: I have this new control called EditableItemControl, pushed to somewhere near the top of the TreeViewItem’s visual tree with its contents set to what original item would look like. EditableItemControl has a property called EditorTemplate, which should be set to what the tree item should look like in edit mode. This is the EditorTemplate I used:

<Grid >
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <TextBox Text="{Binding Name, Mode=TwoWay}" 
        input:CommandService.Command="EndEditTopic"
        input:CommandService.CommandParameter="{Binding}" 
    />
    <Button Name="saveButton" ToolTipService.ToolTip="OK" Grid.Column="1"
        input:CommandService.Command="EndEditTopic"
        input:CommandService.CommandParameter="{Binding}">
        <Image Source="/EditableTreeView;component/Resources/ok.png" Stretch="None" />
    </Button>
    <Button Name="closeButton" ToolTipService.ToolTip="Cancel" Grid.Column="2"
        input:CommandService.Command="CancelEditTopic"
        input:CommandService.CommandParameter="{Binding}">
        <Image Source="/EditableTreeView;component/Resources/cancel.png" Stretch="None" />
    </Button>
</Grid>

When editing is initialized, control’s original content is preserved and replaced with the above editor template, just to be switched back when editing is over. There are two ways to commit the changes – one is to press the saveButton, and the other is to press the Return key focused on the TextBox. Note that the TextBox is also wired to the EndEditTopic command, and TextBox control being one of the actionable elements in the SLExtensions framework, this simply works.

The key to make the switch between view and edit states is EditableItem’s IsEditing dependency property – the control goes to edit state when this property is set to true, and returns to its normal state when its value is set back to false. Ok, but how is this property set? Remember that our HelpTopic class also implements a property with the same name? They fit perfectly together:

<local:EditableTreeViewItem Grid.ColumnSpan="3" 
       IsEditing="{Binding IsEditing}" 
       HorizontalContentAlignment="Stretch">
    ...
</local:EditableTreeViewItem>

To tie all the ends together – when the Edit button is pressed, BeginEdit command is executed. ViewModel handles that command by calling the BeginEdit method on the HelpTopic item, for which the command was executed. IsEditing property gets set for this item and at the same time picked up by the EditableItemControl, which does the switch and display the editor for it. It’s like playing The Incredible Machine!

OK, we made the TreeView control editable. However, there are a few visual issues – one of them is that a mouse-over in a subitem shows the edit/delete commands for its ancestors all the way up to the root. I don’t know whether this is a default behavior for the TreeView or a bug of some sort, but until I find out, I’m leaving it as it is. I did use a TreeViewItem’s MouseOver state which wasn’t in the original template, so it may be that this state is not (properly) supported. Anyway, I’ll definitely get back to this, but first we have some adding to do.

About to edit an item

Editing an item

Next in the series: adding an item

The source code for this sample is available: