Silverlight, ASP .NET, C#, AJAX, and all things Web 2.0

maandag 18 mei 2009

Designing an editable Silverlight combobox (or listbox)

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!