Wednesday, June 9, 2010

Virtual member call in constructor

If you have a derived class, in which the constructor calls a virtual or override method (or property, event), you need to be careful. Why? Let's take a look at this piece of code (The code does not have any useful meanings, it is just for the example).
abstract class Animal
{
    public virtual void Move()
    {
    }
}

class Dog : Animal
{
    private string name;

    public Dog()
    {
        name = "Dog";
        Move();
    }

    public override void Move()
    {
        Console.WriteLine("{0} is running.", name);
    }
}
This piece of code just looks fine, nothing wrong with it. If you new an instance of the class Dog, it just print out "Dog is running.", as expected.

But it has a potential issue. If you have another class like this.
class Dingo : Dog
{
    private string name;

    public Dingo()
    {
        name = "Dingo";
        Move();
    }

    public override void Move()
    {
        Console.WriteLine("{0} is tracking.", name);
    }
}
It just looks like the same as class Dog except that it derives from Dog. But if you new an instance of Dingo, "strange" thing happens. It prints out two lines, the first one is " is tracking", the second is "Dingo is tracking.", which is not expected.

The reason is that CLR initializes classes from the most derived one to the most basic one, then calls the constructors from the most basic one to the most derived one. But when it is calling the basic one's constructor, such as Dog(), inside the constructor it calls the virtual method Move(), which in this example actually is the Move method in Dingo class. And at that time the name of Dingo is not assigned yet, until it calls the Dingo's constructor.

So it is not safe to call virtual members in constructor, unless it is the most derived type, in which case you can use the key work "sealed" to prevent others inheriting from this type.

Saturday, June 5, 2010

User defined WPF ComboBox binds to a nullable boolean type

For some reason the project I am doing at the moment requires a ComboBox that displays 'Yes', 'No' which represent True and False respectively. Also in some cases it needs to have an empty option which means both True and False. A common example of this would be searching the Active or Inactive records, or all of them. It looks like this



It needs to be used many times in the project so I decide to write an user defined ComboBox. Let's call it BoolComboBox.

The BoolComboBox should have these functions:

1. It can be defined in XAML if it needs to display an empty option.
2. The code behind should be able to receive a nullable boolean type value regarding to the user selected value.
3. It can be defined a default nullable boolean type value in XAML.

Let's start the code.

First we define a class derives from ComboBox and bind it to a Dictionary. Please note that it is just for presenting and it does not include the usings and namespace. The solution can be downloaded at the end of this post.
public class BoolComboBox : ComboBox
{        
    private Dictionary BindItems = new Dictionary() 
    {
        {"Yes",true}, {"No",false}
    }; // The displayed values can be configurable
    public BoolComboBox()
    {
        InitialiseItems();
    }

    private void InitialiseItems()
    {
        this.ItemsSource = BindItems.Keys;
    }
}
The "Yes", "No" are hardcoded. We can make it configurable to let it display whatever we want. But here let's keep it simple.

Then for the first function in the list above, we need to define a boolean type dependency property, say HasEmptyOption.
public static readonly DependencyProperty HasEmptyOptionProperty =
 DependencyProperty.Register("HasEmptyOption", typeof(Boolean), typeof(BoolComboBox),
 new PropertyMetadata(false, OnHasEmptyOptionPropertyChanged));

private static void OnHasEmptyOptionPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
 BoolComboBox boolComboBox = source as BoolComboBox;

 if ((bool)e.NewValue)
  boolComboBox.BindItems.Add(string.Empty, null);
}

public bool HasEmptyOption
{
 get { return (bool)GetValue(HasEmptyOptionProperty); }
 set { SetValue(HasEmptyOptionProperty, value); }
}
In the Event method it decides to add the empty option or not.

For function two we have another dependency property, a nullable boolean type named ReturnedValue.
public static readonly DependencyProperty ReturnedValueProperty =
 DependencyProperty.Register("ReturnedValue", typeof(Boolean?), typeof(BoolComboBox),
 new PropertyMetadata(false));

public bool? ReturnedValue
{
 get { return (bool?)GetValue(ReturnedValueProperty); }
 set { SetValue(ReturnedValueProperty, value); }
}

The last nullable boolean type dependency property is the DefaultValue.

public static readonly DependencyProperty DefaultValueProperty =
 DependencyProperty.Register("DefaultValue", typeof(Boolean?), typeof(BoolComboBox),
 new PropertyMetadata(null, OnDefaultValuePropertyChanged));

private static void OnDefaultValuePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
 BoolComboBox boolComboBox = source as BoolComboBox;
 if (e.NewValue != null)
 {
  foreach (var p in boolComboBox.BindItems)
  {
   if (p.Value == (bool)e.NewValue)
   {
    boolComboBox.Text = p.Key;
    break;
   }
  }
 }
}

public bool? DefaultValue
{
 get { return (bool?)GetValue(DefaultValueProperty); }
 set { SetValue(DefaultValueProperty, value); }
}
At last we need to add an Event method so the ReturnedValue property is updated when user selected an option.
void BoolComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
 this.ReturnedValue = this.BindItems[this.SelectedValue.ToString()];
}
And in the InitialseItems method add this
this.SelectionChanged += new SelectionChangedEventHandler(BoolComboBox_SelectionChanged);
We have finished the code for BoolComboBox. When we need to use it we just need to add a piece of XAML like this:

The word "view" in front of "BoolComboBox" is the namespace name of this type.

Click here to download the code files.