Andrej Tozon's blog

In the Attic

NAVIGATION - SEARCH

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.

Creating Silverlight Behaviors (with Blend 3 Interactivity dll)

Behaviors are not new in WPF / Silverlight world; it’s a common way of extending visual element’s behavior through the power of attached properties and everybody probably used one of these at least once in their projects. Now, there’s new Behaviors in town…

I first learned about the behaviors in this excellent, short but sweet MIX09 presentation by Pete Blois. In short: the new “breed” of behaviors will be supported in Blend 3, allowing designers to easily extend visual elements by drag’n’dropping a variety of behaviors onto them. If you’re not familiar with what behaviors are – imagine you have a Drag behavior... By setting it to any element in your window (in Blend / Xaml, no .NET code!), that element instantly gets draggable around that window. Yeah, that’s a powerful concept. And it doesn’t stop there. Watch the video, it’s worth the time…  And for detailed info, Christian is running a series on behaviors and other interactivity mechanism in his blog.

What I don’t get is why they are called Blend (3) behaviors, because they are not limited to Blend in any way. They would be as easily called Silverlight/WPF behaviors, or simply – Behaviors. Yes, that means you can use (and create) them even if you don’t have Blend 3 installed. Even Silverlight 3 is not required, it works with version 2 perfectly. You only need a special dll, which gets installed with Blend 3 – Microsoft.Expression.Interactivity.dll. I currently have no idea what the deployment story with this assembly is at the time of the writing (with Blend being in Beta and all), but you’ll find it in the c:\Program Files\Microsoft Expression\Blend 3 Preview\Libraries\[Silverlight] | [WPF]\ folder. This assembly (it comes in Silverlight and WPF flavor) provides the base interactivity classes and is required if you’re going to use behaviors in your application.

It should be quite obvious as to where behaviors fit in the designer / developer workflow: designers will be able to decorate visual elements with various behaviors and see them in action, while developer’s job will be to come up with new, not-yet-existing behaviors that designer had in mind when designing the UX.

There are a few examples of behaviors up and ready on Expression Gallery, but let’s take a look how we can develop our own behaviors using Visual Studio 2008.

The first behavior we’ll look into will be called the TransparencyBehavior. What it will do is make every element semi-transparent when the mouse is not directly over it. I’ll use one of my previous samples (a semaphore) as a building ground for this one. The lights on the semaphore will be semitransparent until mouse enters the light’s space for it to become fully visible. Let’s begin

First, you’ll need the Expression interactivity dll (see above). Once you find it, add it as a reference to your project. Then, create a class, deriving from Behavior<T>. The generic type T is the type of element you want to extend with this behavior. I’m using the Ellipse type for the purpose of this example to show a more concrete implementation. I could also use <FrameworkElement> because this type of behavior is so common it could be attached to element.

Update: updated the previous paragraph to make sense and align with the sample code.

public class TransparencyBehavior : Behavior<Ellipse> {}

We’ll need a couple of properties to control the behavior’s parameter: The InactiveTransparency property will hold an opacity value for element’s inactive state (when the mouse is not over) and the Duration will hold the time for the element to transition from active to inactive state (and vice versa).

To hook into the element we’re extending, we have to override the OnAttached method. It will be called when the element is being initialized so we have the chance to attach additional event handlers to element’s events. The extended element can be reached through the AssociatedObject property. In this method, I’m hooking up into MouseEnter and MouseLeave events to detect when mouse is over, initialize and construct a storyboard that will be triggered for state transition:

protected override void OnAttached()
{
    base.OnAttached();

    storyboard = new Storyboard();
    animation = new DoubleAnimation();
    animation.Duration = Duration;
    Storyboard.SetTarget(animation, AssociatedObject);
    Storyboard.SetTargetProperty(animation, new PropertyPath(Border.OpacityProperty));
    storyboard.Children.Add(animation);

    AssociatedObject.Opacity = InactiveTransparency;
    AssociatedObject.MouseEnter += OnMouseEnter;
    AssociatedObject.MouseLeave += OnMouseLeave;
}
Similarly, there's the OnDetaching method that we can use to clean up (remove event handlers etc.) The rest of the class are just the handlers:
 
void OnMouseLeave(object sender, MouseEventArgs e)
{
    animation.To = InactiveTransparency;
    storyboard.Begin();
}

void OnMouseEnter(object sender, MouseEventArgs e)
{
    animation.To = 1;
    storyboard.Begin();
}
 
To finish, here’s a piece of modified (from the previous semaphore example) piece of Xaml using the behavior:
 
<Ellipse Fill="{Binding Brush}" Width="50" Height="50" Stroke="Black">
    <i:Interaction.Behaviors>
        <local:TransparencyBehavior Duration="00:00:00.2" InactiveTransparency="0.5" />
    </i:Interaction.Behaviors>
</Ellipse>
That’s all for this simple behavior. In future posts, I’ll come up with new behaviors and put them in the context of some other samples I’ve used in the previous posts.
[Hint: in the below sample, hover over green lights]

The way to create new behaviors is very similar to using “direct” approach with attached properties. The Behavior base class provides a very convenient infrastructure to build on, and porting the “old way” attached behaviors to this new model shouldn’t be that difficult. Behaviors are not limited to use in Blend 3 and work with Silverlight 2 too. With that, it would be great to see Microsoft releasing the interactivity dll as a standalone release or a part of some other SDK, not tying it to either Blend 3 or any particular Silverlight version.

The source code for this sample is available. Please note that the project is a Silverlight 3 project and doesn’t include the Expression Interactivity dll.

Update: the above code was updated to match Silverlight 3 / Blend 3 RTM bits. Thanks To Michael Washington for taking the time to update the behavior.

Shout it

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.

Declarative Role-based “security” for Silverlight Xaml elements

Tim Heuer published a great example on how to implement user authentication and role validation with ASP.NET Application Services in his Application Corner a while ago. It really works as expected, even with custom membership / role providers. And you get to do declarative role based security checks on your server side classes too.

When building MVVM pattern applications, you tend to have as less code as possible in your views’ code-behind classes. Call me a MVVM purist [ :), see Tim’s video interview for pixel8 ], but (close to) zero lines of custom code is what I like to have in my code-behinds. Not at all costs, but usually it doesn’t take much to code additional helper class, an attached property or a controller of some sort.

What I wanted to do, was replace the following piece of code with something more declarative, which could be used in Xaml:

private void EnableAppFeatures(ObservableCollection<string> roles)
{
    EmployeeButton.IsEnabled = (roles.Contains("Admin") || roles.Contains("HR"));
    OrderButton.IsEnabled = (roles.Contains("Admin") || roles.Contains("Sales"));
    CustomersButton.IsEnabled = (roles.Contains("Admin") || roles.Contains("Sales"));
    ReportsButton.IsEnabled = (roles.Contains("Admin") || roles.Contains("Sales"));
}

[the roles string collection comes in as a result of the Roles service call]

Solution #1 - Good

If you’re using MVVM, adding some additional properties to your ViewModels (like IsInHRRole, IsInSalesRole, etc) should be easy, right? You would just need to bind al the buttons’ IsEnabled properties to their ViewModel counterparts and you’re done.

<Button ... x:Name="ReportsButton" ... IsEnabled="{Binding IsInSalesRole}">

True, but you’ll probably end up “polluting” all your ViewModels with such properties since usually the majority of the Views have some elements, depending on a role the user is in. Additionally, you’d have to create additional properties for combined roles, e.g. IsInSalesOrAdminRole,

Solution #2 - Better

I was thinking somewhere in the lines of attaching a new attached property to each button, which would enable the button when the user would be in one of the specified roles:

<Button ... x:Name="ReportsButton" ... sec:SecurityAction.DemandRole="Sales,Admin">

SecurityAction.DemandRole in this case is an attached property, specifying all the roles, which would make the button enabled. The logic is simple – if the user is in one of the specified roles, the button (or any other control with this attribute appended) would have its IsEnabled property set to true. 

To make this possible, I first had to come up with a class to hold all the current roles, something like a Principal… This is a short class, supporting roles only that will do a job for this demonstration:
 
public class SilverlightPrincipal
{
    public static ObservableCollection<string> Roles { get; private set; }

    static SilverlightPrincipal()
    {
        Roles = new ObservableCollection<string>();
    }

    public static void SetRoles(IList<string> roles)
    {
        Roles.Clear();
        for (int i = 0; i < roles.Count; i++)
        {
            Roles.Add(roles[i]);
        }
    }
    public static bool IsInRole(string role)
    {
        return Roles.Contains(role);
    }

    public static bool IsInOneOfRoles(string[] roles)
    {
        for (int i = 0; i < roles.Length; i++)
        {
            if (Roles.Contains(roles[i]))
            {
                return true;
            }
        }
        return false;
    }
}

The roles collection will contain all roles the user is currently in. IsInRole() and IsInOneOfRoles() will return true, if user is in one of the passed in roles. The SetRoles() method is here for simplicity – it servers just as a roles setter, called from wherever the Roles service calls returns.

Perhaps the class above probably doesn’t even deserve to be called a Principal, but it will serve well for the purpose of this post. For more ideas to implement a more complete Silverlight Principal, check this blog post.

To plug it in Tim’s code, I used the aforementioned EnableAppFeatures method, which now looks like:

private void EnableAppFeatures(ObservableCollection<string> roles)
{
    SilverlightPrincipal.SetRoles(roles);
}

The next thing to do was create the SecurityAction.DemandRole attached property that could be used to set the required roles to a specific control. You’ll find this attached property in the sample code attached to this post. I’m not including the code here because it will make this post much longer and I’m not really fond of using it anyway, because of the reasons for solution #3 below. However, feel free to try the property and please leave comment if you like (or dislike) it.

Solution #3 - Best

After creating #2, I thought – if user can never access the functionality behind the buttons, there is no need to show the buttons to her in the first place, right? Why not showing her the dashboard, created specifically for her role? Sounds familiar? ASP.NET? LoginView?

Here’s my simple LoginView control for Silverlight. You can define as many LoginView templates for as many roles you have:

<controls:LoginView>
    <controls:LoginView.Templates>
        <controls:LoginViewDataTemplate Roles="HR>
            <!-- Layout for HR role -->
        </controls:LoginViewDataTemplate>
        <controls:LoginViewDataTemplate Roles="Sales>
        <!-- Layout for Sales role -->
        </controls:LoginViewDataTemplate>
        <controls:LoginViewDataTemplate Roles="Admin>
            <!-- Layout for Admin role -->
        </controls:LoginViewDataTemplate>
     </controls:LoginView.Templates>
</controls:LoginView>
 
First, here’s the LoginViewTemplate class:
 
public class LoginViewDataTemplate : DataTemplate
{
    public string Roles { get; set; }
}
Yeah, that’s pretty much it. The Roles property will hold the list of roles, which are allowed to see the template.
 
The LoginView control itself is simple too:
 
public class LoginView : UserControl
{
    public Collection<LoginViewDataTemplate> Templates { get; set; }

    public LoginView()
    {
        Templates = new Collection<LoginViewDataTemplate>();

        SilverlightPrincipal.RegisterForRoleChangeNotification(this, LoadTemplate);
        Loaded += (s,e) => { LoadTemplate(); };
    }

    private void LoadTemplate()
    {
        for (int i = 0; i < Templates.Count; i++)
        {
            string[] roleNames = Templates[i].Roles.Split(new char[] { ',', ' ' }, 
                StringSplitOptions.RemoveEmptyEntries); bool isInRole = SilverlightPrincipal.IsInOneOfRoles(roleNames); if (isInRole) { Content = Templates[i].LoadContent() as UIElement; return; } } Content = null; } }

[Update 06-13: updated the above code; should work now]

There’s not much to comment here… The Templates collection holds the collection of, errr., LoginViewDataTemplates, and the LoadTemplate method is there to Load the first template, having one of the specified roles match the current role that user is in. This could be easily extended to support the default template, if needed. The default template would be loaded when no other template would match the current user’s role.

RoleChanged notification /execution

The right template is loaded with the control and whenever the Roles might change, the control would reload the template. I’ve implemented this behavior by adding a registration method for role change notification. Any visual element (or whatever object for that matter) can register itself by passing in the delegate to whatever method should be called whenever role(s) would change. The registration method was conveniently added to existing SilverlightPrincipal class:

public static void RegisterForRoleChangeNotification(object o, Action action)
{
    bool existsInCollection = references.Keys.FirstOrDefault(reference => reference.Target == o) != null;
    if (!existsInCollection)
    {
        references.Add(new WeakReference(o), action);
    }
}

Whenever the roles would change, register objects’ delegate would be called:

private static void OnRolesChanged()
{
    CleanUp();
    foreach (var reference in references)
    {
        reference.Value();
    }
}

The CleanUp method takes care for dead/disposed objects:

private static void CleanUp()
{
    List<WeakReference> list = references.Keys.ToList<WeakReference>();
    for (int i = list.Count - 1; i >= 0; i--)
    {
        WeakReference r = list[i];
        if (!r.IsAlive)
        {
            references.Remove(r);
        }
    }
}

Classes, included in the package: SilverlightPrincipal, SecurityAction, LoginView:

photoSuru install experience

Realizing that i don’t have any decent photo slideshow player installed on my machine I thought I’d install photoSuru and see what it can do for me.

photoSuru

While it’s a beautiful subscription-based WPF photo viewer, built on a Syndicated Client Experiences (SCE) Starter Kit , it was something else that caught my eye:

install

What kind of installer is this? Looks like the application itself… did I already install it and it’s now updating itself?

Yes, photoSuru is a ClickOnce application. Deployment-wise, it uses a hybrid MSI/ClickOnce installer, providing a consistent look, matching the appearance of the application itself. If you’ve deployed any ClickOnce application before, you know that you had no power to change that dull install dialog in any way.

.NET 3.5 SP1 changed this. With SP1 installed, you now do have the power to customize and brand your application’s progress dialog, including optional end-user license agreement page, localization, etc., you just have to do it all by putting together a bunch of Xml files, describing what you want it to look like.  Yup, there is no visual support for this yet (as in Visual Studio 2008 SP1). The future looks bright, though. Client Profile Configuration Designer is currently a part of WPF Futures, a taste of what is about to come. You can download and play with it - it’s certainly going to be of some help to you, at least to get you started and set up the basic dialogs and progress flow, but you might still have to get into the Xml files to fine-tune some details.

I’ve tried the CPC Designer with one of my WPF apps some time ago and I did manage to put together a quite decent looking installer, it just took me more time as it would if I went with the default ClickOnce option. But that goes without saying, doesn’t it?

Making the Silverlight TreeView bindable two-way

One of the most common scenarios in LOB applications is a list control, displaying some sort of items, and clicking on an item provides the user with some details about selected item. This is called a Master-detail scenario. Take Microsoft Outlook, as a typical three level example [Folder-Mail-Content]. I’m going to implement this scenario with Silverlight Toolkit’s TreeView using the MVVM pattern.

I’ll use the same PageViewModel, used in the first post of my TreeView Editing series and begin working on the user interface, first using a ListBox, not the TreeView. The PageViewModel is, again, set as the DataContext of the main page.

ListBox

Selecting a help topic from the list will get its description shown in a TextBlock below the ListBox. How this works is that when an item is selected, the ViewModel is notified. The ViewModel then gets the selected item’s details and notifies the TextBlock when the details are available. Sounds complicated? It’s not, really.

Let’s do this the easy way – I’m going to use the HelpTopic class as a list item and as a detail. That means both the ListBox and the TextBlock will be bound to the new SelectedTopic property on the ViewModel:

private HelpTopic selectedTopic;
public HelpTopic SelectedTopic
{
    get { return selectedTopic; }
    set
    {
        if (selectedTopic == value)
        {
            return;
        }
        selectedTopic = value;
        OnPropertyChanged("SelectedTopic");
    }
}

with ListBox and TextBlock bound as displayed in this parts of Xaml:

<ListBox ItemsSource="{Binding HelpTopics}" DisplayMemberPath="Name"
SelectedItem="{Binding SelectedTopic, Mode=TwoWay}" />

<TextBlock Text="{Binding SelectedTopic.Name}" />

Now let’s add a Tree and bind it the same way as the ListBox. Here’s the complete Xaml:

<StackPanel>
    <StackPanel Orientation="Horizontal">
        <ListBox ItemsSource="{Binding HelpTopics}" DisplayMemberPath="Name" 
                 SelectedItem="{Binding SelectedTopic, Mode=TwoWay}" Width="300"
                 HorizontalAlignment="Stretch" /> <slt:TreeView VerticalAlignment="Stretch" ItemsSource="{Binding HelpTopics}"
                      SelectedItem="{Binding SelectedTopic, Mode=TwoWay}" Width="300"> <slt:TreeView.ItemTemplate> <slt:HierarchicalDataTemplate ItemsSource="{Binding SubTopics}"> <TextBlock Text="{Binding Name}" VerticalAlignment="Center" /> </slt:HierarchicalDataTemplate> </slt:TreeView.ItemTemplate> </slt:TreeView> </StackPanel> <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch"> <StackPanel Orientation="Horizontal"> <TextBlock Text="You selected: " Margin="4" /> <TextBlock Text="{Binding SelectedTopic.Name}" VerticalAlignment="Center" Margin="4" /> </StackPanel> </Border> </StackPanel>

We have two controllers now (ListBox and TreeView). But let’s observe how they like being controlled.

image

image

TreeView differs from the ListBox in having a private SelectedItem property setter, which makes two-way binding impossible. Almost impossible anyway, there is a way around it.

Let’s extend the TreeView by creating a new attached property which will provide us with “the-missing-way binding”, needed to update the TreeView from the ViewModel:

public class SelectionService
{
    public static readonly DependencyProperty SelectedItemProperty = 
           DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(SelectionService),
           new PropertyMetadata(null, OnSelectedItemChanged)); public static void SetSelectedItem(DependencyObject o, object propertyValue) { o.SetValue(SelectedItemProperty, propertyValue); } public static object GetSelectedItem(DependencyObject o) { return o.GetValue(SelectedItemProperty); } private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TreeView treeView = d as TreeView; if (treeView == null) { return; } TreeViewItem item = treeView.ItemContainerGenerator.ContainerFromItem(e.NewValue) as TreeViewItem; if (item == null) { return; } item.IsSelected = true; } }

There’s really just two lines of code that actually do anything. In the OnSelectedItemChangedMethod, I’m getting the container TreeViewItem of the selected HelpTopic and set its IsSelected property to true.

To attach this property to the TreeView, add the following to the above-defined TreeView:

<slt:TreeView ... local:SelectionService.SelectedItem="{Binding SelectedTopic}">

There’s however two minor issues to this approach… TreeView’s native SelectedItem property is still two-way bound so when ViewModel tries to call its private setter, an exception is still thrown, which may affect performance. What we would need here is a OneWayToSource type binding, which exists in WPF, but unfortunately not in Silverlight.

The other issue is that the above code only works for the root level. If you want to select any node in the hierarchy, you would traverse the tree unit you find the one that should be selected. But… the TreeView creates TreeViewItems only when needed (when their parent node is expanded). In order to fix this, each item has to be expanded before inspecting their children and then collapsed, if that was its original state. Additionally, this approach can be even more time consuming. Let’s look at the quick and dirty implementation:

private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    TreeView treeView = d as TreeView;
    if (treeView == null)
    {
        return;
    }

    TreeViewItem item = treeView.ItemContainerGenerator.ContainerFromItem(e.NewValue) as TreeViewItem;
    if (item != null)
    {
        item.IsSelected = true;
        return;
    }

    for (int i = 0; i < treeView.Items.Count; i++)
    {
        SelectItem(e.NewValue, treeView.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem);
    }
}

private static void SelectItem(object o, TreeViewItem parent)
{
    if (parent == null)
    {
        return;
    }

    bool isExpanded = parent.IsExpanded;
    if (!isExpanded)
    {
        parent.IsExpanded = true;
        parent.UpdateLayout();
    }
    TreeViewItem item = parent.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem;
    if (item != null)
    {
        item.IsSelected = true;
        return;
    }

    for (int i = 0; i < parent.Items.Count; i++)
    {
        SelectItem(o, parent.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem);
    }

    if (parent.IsExpanded != isExpanded)
    {
        parent.IsExpanded = isExpanded;
    }
}

OK, now we have a two-way bindable TreeView, playing nice with the ViewModel, but with some performance hit for that “other-way binding”. I’m sure the Silverlight Toolkit guys would make this much more performant, so if you would like to see TreeView’s SelectedItem property to be made public, you can vote here.

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:

Silverlight TreeView: MVVM and editing [3 – Delete]

After the first two introductory posts, we're ready to move into the TreeView control itself. We’ll begin by implementing the easiest of the three update commands (insert/update/delete) – to delete an item in a TreeView.

This one actually looks very simple, all we need is to redefine TreeView’s ItemTemplate by adding a delete button we’ve created in the previous post:

<slt:TreeView.ItemTemplate>
    <slt:HierarchicalDataTemplate ItemsSource="{Binding SubTopics}">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
            <Button ToolTipService.ToolTip="Delete Topic" Margin="2,0,0,0" 
                    input:CommandService.Command="DeleteTopic" 
                    input:CommandService.CommandParameter="{Binding}">
                <Image Source="/EditableTreeView;component/Resources/delete.png" Stretch="None" />
            </Button>
        </StackPanel>
    </slt:HierarchicalDataTemplate>
</slt:TreeView.ItemTemplate>

As we’ve seen in the previous post, associating the delete Button with the DeleteTopic command will invoke the same command when the button is pressed. What’s new is that we’re now passing the current item as a CommandParameter. By ‘current item’ I don’t mean the currently selected item, but the item, which button was clicked.

In order to get this working, we have to change the ViewModel as well:

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

    bool isRemoved = topic.RemoveFromStructure(HelpTopics, item => { return item.SubTopics; });
    if (!isRemoved)
    {
        throw new InvalidOperationException(string.Format("Topic '{0}' couldn't be removed.", topic.Name));
    }
}

OnDeleteTopicExecuted now deletes the element, passed as the invoked command’s parameter. To delete an element from a hierarchical collection structure, we somehow need to find its parent. Because the HelpTopic class doesn’t know about its parent, I used a simple hierarchical query to help me out. The generic RemoveFromStructure() extension method finds an item in the passed structure and deletes the item, if found.

TreeView

Now we’re able to delete individual items in the tree, but all those distractive buttons scattered around the screen introduced a lot of noise to the view. We should be somehow able to hide them.

There’s a couple of ways to do this. For now, we’ll just modify the TreeViewItem’s style a bit and set it as TreeView’s ItemContainerStyle. TreeViewItem’s style also contains the TreeViewItem’s template, defining the overall look of the items in the TreeView – how the items looks selected, when mouse-over etc. Don’t confuse it with the ItemTemplate – this one basically defines TreeViewItem’s content. To not complicate this further, here’s our TreeViewItem style modification [only modified parts shown here, the whole style is defined in project’s App.Xaml]:

<StackPanel Orientation="Horizontal"> <ContentPresenter x:Name="content" Cursor="{TemplateBinding Cursor}" Content="{TemplateBinding Content}"
  ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Left"
        Margin="{TemplateBinding Padding}" /> <Button x:Name="commands" ToolTipService.ToolTip="Delete Topic" Margin="4,0,0,0" Opacity="0" input:CommandService.Command="DeleteTopic" input:CommandService.CommandParameter="{Binding}"> <Image Source="/EditableTreeView;component/Resources/delete.png" Stretch="None" /> </Button> </StackPanel>

 
and this is the added animation for the pressed state, showing the button when mouse is over the item:
 
<DoubleAnimation Storyboard.TargetName="commands" Storyboard.TargetProperty="Opacity" Duration="0" To="1" />

The current TreeView overall styling and chosen mouse-over behavior might not be the best suiting for any type of application. We’ll worry about making it look pretty after we get it working right.

The TreeView definition now looks like:

<slt:TreeView VerticalAlignment="Stretch" Grid.Row="1" ItemsSource="{Binding HelpTopics}"
    ItemContainerStyle="{StaticResource TreeVireItemContainerStyle}">
    <slt:TreeView.ItemTemplate>
        <slt:HierarchicalDataTemplate ItemsSource="{Binding SubTopics}">
            <TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
        </slt:HierarchicalDataTemplate>
    </slt:TreeView.ItemTemplate>
</slt:TreeView>

The delete button is now visible only when mouse hovers over each TreeView item. Pressing it deletes the same node, which is what we wanted to finish this post with anyway. And we still didn’t write any serious code… We’ll get more into the code in the next part of the series.

Next: item editing

The source code for this sample is available: