Showing posts with label Property. Show all posts
Showing posts with label Property. Show all posts

Monday, March 28, 2011

How to Databind to ListBox’s SelectedItems property (Silverlight)

 

It’s been a while since the last time I published some useful code sample. I apologize!

Today I found what I qualify as a bug in Silverlight 4.0. Those who know me are aware that I’m working on a big Silverlight project for about a year now. As you also know I’m a big fan of patterns and for Silverlight, the obvious one is MVVM.

Every pattern has its own standard and often its own framework. With MVVM the omnipresent framework feature is Databinding. In XAML, Databinding is really powerful. Of course Silverlight doesn’t have all the power WPF has but almost. I’m already used to deal with the limitation of Silverlight binding and the lack of Markup extension for example. But today I it a wall with something I didn’t expect, data binding to the SelectedItems property of a ListBox.

If you look closely at ListBox’s properties you will find that almost all of them are DependencyProperty except for SelectedItems (don’t miss the “s”). SelectedItem (without an “s”) is a ok but not SelectedItems. Why? I suppose its was forgotten.

How to fix that? I found many complex implementation of solution to solve this issue but I didn’t found one I was satisfied with. When trying to debug this “bug” I realized that if I inspect the content of the SelectedItems property in the debug view then the binding was working. So I came up with a solution around that strange side effect.

First let’s try to reproduce the problem. The first thing you need to do is to create a new Silverlight application. Accept all the default, it doesn’t matter. Next put this code in the MainPage.xaml

<UserControl x:Class="ListBoxSelecteItemsBug.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:fix="clr-namespace:ListBoxSelecteItemsBug"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="400">

    <Grid x:Name="LayoutRoot"
          Background="White">
        <StackPanel Orientation="Vertical">
            <ListBox x:Name="myListBox"
                     Height="200"
               SelectionMode="Extended"
                     ItemsSource="{Binding MyItems}" />

            <Button Content="Click me"
                    Command="{Binding DoItCommand}"
                    CommandParameter="{Binding SelectedItems, ElementName=myListBox}" />
      <Button Content="Make it work"
              Click="ButtonBase_OnClick" />
        </StackPanel>
    </Grid>
</UserControl>

Notice the “CommandParameter” binding at line 22 binding to “SelectedItems” on “myListBox”. Then Put the following code in the MainPage.xaml.cs (code behind).

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;

namespace ListBoxSelecteItemsBug
{
  public partial class MainPage : UserControl
  {
    private readonly ICommand _doItCommand = new MyCommand();
    private readonly ObservableCollection<string> _myItems = new ObservableCollection<string>();

    public MainPage()
    {
      _myItems.Add("test1");
      _myItems.Add("test2");
      _myItems.Add("test3");
      _myItems.Add("test4");
      _myItems.Add("test5");
      _myItems.Add("test6");
      _myItems.Add("test7");

      InitializeComponent();

      DataContext = this;
    }

    public ICommand DoItCommand
    {
      get { return _doItCommand; }
    }

    public ObservableCollection<string> MyItems
    {
      get { return _myItems; }
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
      MessageBox.Show(string.Format("{0} actual selected items", myListBox.SelectedItems.Count));
    }
  }

  public class MyCommand : ICommand
  {
    #region ICommand Members

    public bool CanExecute(object parameter)
    {
      return true;
    }

    public void Execute(object parameter)
    {
      var list = (ICollection<object>) parameter;
      MessageBox.Show(string.Format("{0} items selected", list.Count()));
    }

    public event EventHandler CanExecuteChanged;

    #endregion
  }
}

For simplicity purpose I put everything in the same file but wouldn’t do that normally.

The concept here is simple. If you run this application you will see a ListBox with 7 items in it. You can select one or more of these items and click on “Click me” button. If you do that you should see a popup with “0 items selected” displayed. This proves that the “SelectedItems” is not working. Now if you click on “Make it work” button you should see the actual number of item you have selected previously. Now click again on “Click me” and the number should also be right.

What happened in this sequence of event is simple. The first time you click on “Click me” button you see the initial binding value of number of “SelectedItems” which is 0. The next time you click on it will display the right number because accessing “SelectedItems” outside of data binding seems to refresh its value.

How can we trigger that refresh automatically? With an attached property.

Now add a new file to the Silverlight project call it ListBoxFix and paste it this content:

using System.Windows;

namespace ListBoxSelecteItemsBug
{
  public static class ListBoxFix
  {
    public static bool GetSelectedItemsBinding(System.Windows.Controls.ListBox element)
    {
      return (bool)element.GetValue(SelectedItemsBindingProperty);
    }
      
    public static void SetSelectedItemsBinding(System.Windows.Controls.ListBox element, bool value)
    {
      element.SetValue(SelectedItemsBindingProperty, value);
      if (value)
      {
        element.SelectionChanged += (sender, args) =>
        {
          // Dummy code to refresh SelectedItems value
          var x = element.SelectedItems;
        };
      }
    }

    public static readonly DependencyProperty SelectedItemsBindingProperty =
        DependencyProperty.RegisterAttached("FixSlecetedItemsBinding",
        typeof(bool), typeof(FrameworkElement), new PropertyMetadata(false));
  }
}

The key here is the line 20. The only thing it does is accessing the “SelectedItems”. The last thing to do is to use that AttachedProperty in our ListBox:

            <ListBox x:Name="myListBox"
                     Height="200"
               fix:ListBox.SelectedItemsBinding="True"
               SelectionMode="Extended"
                     ItemsSource="{Binding MyItems}" />

Doing that, triggers the binding to be refreshed and everything should work.

Thursday, July 23, 2009

Type-safe INotifyPropertyChanged and derived classes

Some of you who read my previous blog post notice that this technique doesn't allow to raise “PropertyChanged” event from a derived class. This is because you can only call method of “PropertyChangedEventHandler” from the class where it is defined. Anywhere else you can only assign (+=) and unassign (-=) event handler.

One way to work around this is to add a method in your base model class that will forward the call to “PropertyChangedEventHandler”.

Here is a modified copy of the class from my previous post:

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type safe raise from base method
            RaisePropertyChanged(() => Data);
        }
    }

    protected void RaisePropertyChanged(Expression<Func<object>> expression)
    {
        PropertyChanged.Raise(expression);   
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

Now you can define a derived class and use the same method to raise a PropertyChanged event.

public class DerivedModel : Model
{
    private string _moreData;

    public string MoreData
    {
        get { return _moreData; }
        set
        {
            if (_moreData == value)
                return;

            _moreData = value;
            RaisePropertyChanged(() => MoreData);
        }
    }
}

Now with very little effort you can build a type-safe and bindable data model.

It is also useful to be able to raise many property at once. For example if have calculated properties that depends on others like in this sample class:

public class User : INotifyPropertyChanged
{
    private string _lastName;

    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (_lastName == value)
                return;

            _lastName = value;
            RaisePropertyChanged(()=>LastName, ()=>FullName);
        }
    }

    private string _firstName;

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName == value)
                return;

            _firstName = value;
            RaisePropertyChanged(() => FirstName, () => FullName);
        }
    }

    public string FullName
    {
        get { return String.Format("{0} {1}", _firstName, _lastName); }
    }

    public void RaisePropertyChanged(params Expression<Func<object>>[] expression)
    {
        PropertyChanged.Raise(expression);
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

Because a change to either “FirstName” or “LastName” should trigger a change to “FullName” you have to raise both changes from both properties. Of course you can call “RaisePropertyChanged” many times but with a simple overload you can do this. All you have to do is add this to your extensions class.

public static void Raise(this PropertyChangedEventHandler handler, params Expression<Func<object>>[] proppertyExpressions)
{
    foreach (var expression in proppertyExpressions)
        Raise(handler, expression);
}

Aside from beeing type safe, this method will give you intellisense support while you type your “RaiePropertyChanged” calls. You still have to type the right property though.

Wednesday, July 22, 2009

How to use INotifyPropertyChanged, the type-safe way (no magic string)

Implementation of the INotifyPropertyChanged interface is quite simple. There is only one event to implement. Take for example the following simple model class:

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type un-safe PropertyChanged raise 
            PropertyChanged(this, new PropertyChangedEventArgs("Data"));
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

This is a pretty standard way to implement a bindable property. The problem here is the “Data” string to specify which property changed. If someone change the name of the property without changing the content of the string, the code will compile fine but won’t work. In a big application with may properties it can be hard to detect and find the problem.

The best solution is to rely on the compiler to warn us. But because the property name is a string it can’t. So let’s change that line with a type-safe one.

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type safe PropertyChanged raise
            PropertyChanged.Raise(() => Data);
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

What is the trick? Raise is an extension method that takes a lambda expression to specify the name of the property in a type safe way. The Raise method resolve this expression to extract the name of the property and pass it to the PropertyChanged event.

public static class PropertyChangedExtensions
{
    public static void Raise(this PropertyChangedEventHandler handler, Expression<Func<object>> propertyExpression)
    {
        if (handler != null)
        {
            // Retreive lambda body
            var body = propertyExpression.Body as MemberExpression;
            if (body == null)
                throw new ArgumentException("'propertyExpression' should be a member expression");

            // Extract the right part (after "=>")
            var vmExpression = body.Expression as ConstantExpression;
            if (vmExpression == null)
                throw new ArgumentException("'propertyExpression' body should be a constant expression");

            // Create a reference to the calling object to pass it as the sender
            LambdaExpression vmlambda = Expression.Lambda(vmExpression);
            Delegate vmFunc = vmlambda.Compile();
            object vm = vmFunc.DynamicInvoke();

            // Extract the name of the property to raise a change on
            string propertyName = body.Member.Name;
            var e = new PropertyChangedEventArgs(propertyName);
            handler(vm, e);
        }
    }
}

All you have to do is to put this extension method in your code and the jib is done. Of course at the end a string will be used to raise the PropertyChanged event but because you don’t have to type it, you don’t have to maintain it.

Tuesday, July 21, 2009

How to use INotifyPropertyChanged, the type-safe way (no magic string)

Implementation of the INotifyPropertyChanged interface is quite simple. There is only one event to implement. Take for example the following simple model class:

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type un-safe PropertyChanged raise 
            PropertyChanged(this, new PropertyChangedEventArgs("Data"));
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

This is a pretty standard way to implement a bindable property. The problem here is the “Data” string to specify which property changed. If someone change the name of the property without changing the content of the string, the code will compile fine but won’t work. In a big application with may properties it can be hard to detect and find the problem.

The best solution is to rely on the compiler to warn us. But because the property name is a string it can’t. So let’s change that line with a type-safe one.

public class Model : INotifyPropertyChanged
{
    private string _data;

    public string Data
    {
        get { return _data; }
        set
        {
            if (_data == value)
                return;

            _data = value;

            // Type safe PropertyChanged raise
            PropertyChanged.Raise(() => Data);
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged = null;

    #endregion
}

What is the trick? Raise is an extension method that takes a lambda expression to specify the name of the property in a type safe way. The Raise method resolve this expression to extract the name of the property and pass it to the PropertyChanged event.

public static class PropertyChangedExtensions
{
    public static void Raise(this PropertyChangedEventHandler handler, Expression<Func<object>> propertyExpression)
    {
        if (handler != null)
        {
            // Retreive lambda body
            var body = propertyExpression.Body as MemberExpression;
            if (body == null)
                throw new ArgumentException("'propertyExpression' should be a member expression");

            // Extract the right part (after "=>")
            var vmExpression = body.Expression as ConstantExpression;
            if (vmExpression == null)
                throw new ArgumentException("'propertyExpression' body should be a constant expression");

            // Create a reference to the calling object to pass it as the sender
            LambdaExpression vmlambda = Expression.Lambda(vmExpression);
            Delegate vmFunc = vmlambda.Compile();
            object vm = vmFunc.DynamicInvoke();

            // Extract the name of the property to raise a change on
            string propertyName = body.Member.Name;
            var e = new PropertyChangedEventArgs(propertyName);
            handler(vm, e);
        }
    }
}

All you have to do is to put this extension method in your code and the jib is done. Of course at the end a string will be used to raise the PropertyChanged event but because you don’t have to type it, you don’t have to maintain it.

Wednesday, October 24, 2007

Editing a password in a PropertyGrid control

Sometimes, when something is too simple, you can spend hours to find out the solution. That happened to me yesterday as I was looking for a way to edit a password in a PropertyGrid control. As you should know, the PropertyGrid control can be customized by adding attributes on object's property you want to edit. One of this attribute is "Editor". With this attribute you can define a designer class that inherits from UITypeEditor to implement your own way to edit the value of a field in the property grid. I made some research on internet to find if somebody had build a special editor for password. No luck. Every time I was looking for page with "UITypeEditor and Password" I found many pages talking about UITypeEditor only. These pages were found because they all contain a "Forgot your password" link in it. Then I tied to search on Code Search. Here I found some interesting code that lead me to Enterprise Library, but it turns out to be a dead-end. Finally I used Reflector to search for "Password". This is where I found that simple attribute called "PasswordPropertyTextAttribute". So the solution was only to add this attribute to my password property and that's it!