One day, two posts! 🙂 A member of the Silverlight .NET forum asked how he could go about making a Silverlight combobox editable. Well, it’s actually easier than you might think, so for all of you who want this kind of behaviour: here’s how you do it! 🙂 The same technique can also be used to make an editable listbox, and usual, sourcecode is included at the end of this post.
I designed a dummy data-object for binding to the combobox. This is a small class which has a Description-propery, an ID and an InEdit-property (to and it implements the INotifyPropertyChanged-interface, so it can notify the UI when its value changes.
1: public class Dummy : INotifyPropertyChanged
2: {
3: public int ID { get; set; }
4:
5: private bool pInEdit;
6: public bool InEdit
7: {
8: get
9: {
10: return pInEdit;
11: }
12: set
13: {
14: pInEdit = value;
15: NotifyPropertyChanged("InEdit");
16: }
17: }
18:
19: private string pDescription;
20: public string Description
21: {
22: get
23: {
24: return pDescription;
25: }
26: set
27: {
28: pDescription = value;
29: NotifyPropertyChanged("Description");
30: }
31: }
32:
33: #region INotifyPropertyChanged Members
34:
35: public event PropertyChangedEventHandler PropertyChanged;
36:
37: public void NotifyPropertyChanged(string propertyName)
38: {
39: if (PropertyChanged != null)
40: {
41: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
42: }
43: }
44:
45: #endregion
46: }
Next, we need to build the datatemplate of our combobox. Essentially, we’ll have two “views”: a normal view, and an editable view. We’ll use a button to switch between these views.
1: <ComboBox.ItemTemplate>
2: <DataTemplate>
3: <Grid>
4: <Grid.ColumnDefinitions>
5: <ColumnDefinition Width="200"></ColumnDefinition>
6: <ColumnDefinition Width="Auto"></ColumnDefinition>
7: </Grid.ColumnDefinitions>
8:
9:
10: <TextBlock Text="{Binding Description, Mode=TwoWay}"
11: HorizontalAlignment="Left" VerticalAlignment="Center"
12: IsHitTestVisible="False"
13: Visibility="{Binding InEdit, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=contra}"/>
14:
15: <TextBox Text="{Binding Description, Mode=TwoWay}"
16: Visibility="{Binding InEdit, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=pro}"
17: HorizontalAlignment="Left" VerticalAlignment="Center"/>
18:
19: <Button Width="60" x:Name="btnEdit" Click="btnEditConfirm_Click" Content="Edit" Grid.Column="1"
20: Visibility="{Binding InEdit, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=contra}" />
21:
22: <Button Width="60" x:Name="btnConfirm" Click="btnEditConfirm_Click" Content="Confirm" Grid.Column="1"
23: Visibility="{Binding InEdit, Converter={StaticResource BoolToVisibilityConverter}}"/>
24:
25: </Grid>
26: </DataTemplate>
27: </ComboBox.ItemTemplate>
To make sure the correct view is shown, I use a convertor to convert the InEdit-value of my object to a Visibility-property. When InEdit is true, the textbox & confirm-button will be shown, when it’s false you’ll only see a textblock and an edit button. For reference, here’s what the convertor looks like:
1: public class BoolToVisibilityConverter: IValueConverter
2: {
3:
4: #region IValueConverter Members
5:
6: /// <summary>
7: /// Convert method from bool to visibility
8: /// </summary>
9: /// <param name="value">the boolean/visibility value value</param>
10: /// <param name="targetType"></param>
11: /// <param name="parameter">mappingmode = pro or contra. Pro will map true to visible, contra
12: /// will map true to collapsed</param>
13: /// <param name="culture"></param>
14: /// <returns></returns>
15: public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
16: {
17: bool normalDirection = true;
18:
19: if (parameter != null)
20: {
21: if (parameter.ToString().Trim().ToLower() == "contra")
22: normalDirection = false;
23: }
24:
25: if (value is bool)
26: {
27: if ((bool)value)
28: {
29: return normalDirection ? Visibility.Visible : Visibility.Collapsed;
30: }
31: else
32: {
33: return normalDirection ? Visibility.Collapsed : Visibility.Visible;
34: }
35: }
36: else
37: {
38: return Visibility.Visible;
39: }
40: }
41:
42: public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
43: {
44: bool normalDirection = true;
45:
46: if (parameter.ToString().Trim().ToLower() == "contra")
47: normalDirection = false;
48:
49: if (value is Visibility)
50: {
51: if ((Visibility)value == Visibility.Visible)
52: {
53: return normalDirection ? true : false;
54: }
55: else
56: {
57: return normalDirection ? false : true;
58: }
59: }
60: else
61: {
62: return true;
63: }
64: }
65:
66: #endregion
67: }
All we need to do now is handle the click-event of the edit & confirm button. Thanks to the rich, two-way-databinding, all we need to do in these handlers is change the edit-mode: we use the DataContext of the sender to access the underlying object, and change the edit-mode-property. We do not need to write our changes to the underlying collection nor commit them manually in any way, nor update the UI when a value is changed – Silverlights’ databinding & notifypropertychanged-interface handles this for us.
1: private void btnEditConfirm_Click(object sender, RoutedEventArgs e)
2: {
3: Dummy tmpDummy = (Dummy)(((Button)sender).DataContext);
4: tmpDummy.InEdit = !tmpDummy.InEdit;
5: }
… and that’s it, really. 🙂 As said, the same technique can also be used to make your listbox editable (or any itemscontrol for that matter).
As promised: sourcecode. Enjoy!