How to: Create custom editor with RadGridView for Silverlight and WPF

Thursday, November 12, 2009 by XAML Team | Comments 10

If you need a custom editor to edit a specific data, you can use one of the following approaches:

 

  1. Use CellEditTemplate property of the GridViewColumn.
  2. Create a custom column by inheriting from GridViewBoundColumnBase.

 

As almost anything in our life both approaches have some advantages and disadvantages. Although it is quite easy to implement the first option, there are some significant disadvantages like not so usable with many RadGridView instances, and more important this bypasses the RadGridView validation and editing engine. For more information about this approach you can take a look at this online example.

 

In this blog post I’ll try to explain how to implement and take maximum advantages from the second option (needs more efforts to implement).

 

I’ll demonstrate the second approach by creating a column with embedded color picker control as an editor. I will start with creating a class that inherits from GridViewBoundColumnBase (this is the base class used to create a column with editing capabilities). There are several methods you have to override in order to get this column to work:

 

1. CreateCellElement – override this method if you want to customize how cells that belongs to this column will look like. This method is called when GridViewCell is prepared and returned element will be used as a ContentPresenter. If you do not override this method a TextBlock control will be used as a default presenter.

 

   1: public override FrameworkElement CreateCellElement(GridViewCell cell, object dataItem)
   2:         {
   3:             Border cellElement = new Border();
   4:             var valueBinding = new Binding(this.DataMemberBinding.Path.Path)
   5:                 {
   6:                     Mode = BindingMode.OneTime,
   7:                     Converter = new ColorToBrushConverter()
   8:                 };
   9:             cellElement.SetBinding(Border.BackgroundProperty, valueBinding);
  10:             cellElement.Width = 45;
  11:             cellElement.Height = 20;
  12:             cellElement.CornerRadius = new CornerRadius(5);
  13: return cellElement;
  14:         }

There is nothing unusual here just a border with bound background to the color from data item.

 

2. CreateCellEditElement – override this method to create custom editor element (according to type of the property or some other business logic). This method must be overridden otherwise GridViewCell will have no content when enters into edit mode.

 

   1: public override FrameworkElement CreateCellEditElement(GridViewCell cell, object dataItem)
   2:         {
   3:             var cellEditElement = new RadColorPicker();
   4: this.BindingTarget = RadColorPicker.SelectedColorProperty;
   5:  
   6:             cellEditElement.MainPalette = this.MainPalette;
   7:  
   8:             Binding valueBinding = this.CreateValueBinding();
   9:  
  10:             cellEditElement.SetBinding(RadColorPicker.SelectedColorProperty, valueBinding);
  11:  
  12: return cellEditElement as FrameworkElement;
  13:         }

In this method we create and return an instance of a RadColorPicker control. In order to work properly as an editor you have to bind this editor to the underlying data property. Lets take a look at the CreateValueBinding method:

   1: private Binding CreateValueBinding()
   2:         {
   3:             Binding valueBinding = new Binding();
   4:             valueBinding.Mode = BindingMode.TwoWay;
   5:             valueBinding.NotifyOnValidationError = true;
   6:             valueBinding.ValidatesOnExceptions = true;
   7:             valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
   8:             valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
   9: return valueBinding;
  10:         }

 

 

We set BindingMode to TwoWay, because this is an editor and we need to update data property which is bound to the parent GridViewColumn.

NotifyOnValidationError and ValidatesOnExceptions properties are related to validation engine (if any error or exception occurs while we set new value to the data object will result as validation error and editor will enter into invalid state (if editor has such state)).

Set UpdateSourceTrigger to Explicit and allow RadGridView to validate and update the value of the data item at the right moment.

Of course every TwoWay binding requires a path, so we take the path from the DataMemberBinding property.

Another interesting line of the CreateCellEditElement is: cellEditElement.MainPalette = this.MainPalette. This line shows how you can transfer properties from column to the actual editor. In order to allow such properties and transfer them to custom column instance you have to override CopyPropertiesFrom method:

   1: public override void CopyPropertiesFrom(GridViewColumn source)
   2:         {
   3: base.CopyPropertiesFrom(source);
   4:             var radColorPickerColumn = source as RadColorPickerColumn;
   5: if (radColorPickerColumn != null)
   6:             {
   7: this.MainPalette = radColorPickerColumn.MainPalette;
   8:             }
   9:         }

Then you can set this custom property via xaml:

   1: <local:RadColorPickerColumn UniqueName="FavouriteColor" DataMemberBinding="{Binding FavouriteColor}" 
   2:                                             Header="FavouriteColor" MainPalette="ReallyWebSafe">

 

Call to the base method is very critical because if you do not call base method properties like DataMemberBinding will not be copied to the RadColorPickerColumn.

 

And now we have to integrate this column into the RadGridView validation and editing engine. There are two methods which you have to override:

 

   1: public override object GetNewValueFromEditor(object editor)
   2:         {
   3:             RadColorPicker colorPicker = editor as RadColorPicker;
   4: if (colorPicker != null)
   5:             {
   6: return colorPicker.SelectedColor;
   7:             }
   8: else
   9:             {
  10: return null;
  11:             }
  12:         }
  13:  
  14: public override IList<ValidationError> UpdateSourceWithEditorValue(GridViewCell gridViewCell)
  15:         {
  16:             List<ValidationError> errors = new List<ValidationError>();
  17:             RadColorPicker editor = gridViewCell.GetEditingElement() as RadColorPicker;
  18:             BindingExpression bindingExpression = editor.ReadLocalValue(RadColorPicker.SelectedColorProperty) as BindingExpression;
  19: if (bindingExpression != null)
  20:             {
  21:                 bindingExpression.UpdateSource();
  22:                 errors.AddRange(Validation.GetErrors(editor));
  23:             }
  24: return errors;
  25:         }

 

As you can see first method gathers required information from the actual editor (used by the validation engine), after UI validation is successful then new value is submitted to the data item via second method. This second method returns errors (if any) that occurred while new value is set to the data item (Data layer validation).

 

There is another useful property which you probably already noticed - BindingTarget property. Generally the actual editor is a single control which has a ValueProperty bound to the data item. When you set BindingTarget property to this ValueProperty, there is no need to override GetNewValueFromEditor and UpdateSourceWithEditorValue methods. Base methods will take necessary actions to validate and update the value. There is only one restriction this ValueProperty must be a direct property of the CellEditElement returned as a result of the CreateCellEditElement method. In this example BindingTarget property is set to RadColorPicker.SelectedColorProperty. If you have a composite UserControl you have to set BindingTarget to UserControl1.ValueProperty.

 

You can play with this example:

 

 

 

Happy editing!

Enjoy!

10 Comments

  • Jax 22 Dec 2009
    Thanks for this - you mention that BindingTarget needs a ValueProperty for composite controls that are edit elements. Can you expand on this - does it mean I must explicitly register a ValueProperty on the composite UserControl that returns the value of the data-bound control which it contains?
    Could you expand this example that includes such a scenario? (assuming it's not as simple as I've stated above)
  • Nedyalko Nikolov 07 Jan 2010
    Sorry for the late reply.

    It is not necessary to set BindingTarget property. This property is some kind of a shortcut. The only limitation is that the value of this property must be a direct property of the editing element.
    For example as a result of the CreateCellEditElement() method you returned a user control "UserControl1" which have a textblock and a textbox control within. Of course textbox control will be used for editing purposes and it’s "Text" property will be bound to the data source. If you set BindingTarget property to the TextBox.TextProperty RadGridView will be unable to get the new value with a code like following:

    (EditingElement(UserControl1) as DependencyProperty).GetValue(BindingTarget)

    In order to work with BindingTarget you have to create a dependency property similar to UserControl1.ValueProperty. Now you will bind UserControl1.ValueProperty to the data source and set BindingTarget to UserControl1.ValueProperty.

    Note: You have to ensure that this property will have correct "new" value. Be aware that RadGridView validation process needs this value before UserControl1.LostFocus event (some controls updates bindings on lost focus). In connection with our example on textbox.TextChanged event handler you have to update UserControl1.ValueProperty accordingly.

    If you do not set BindingTarget property editing element will be searched for a property which is bound to the data item in order to get new value for editing and validation purposes. (This functionality is available only with latest assemblies and will be included officially with the 2009.Q3.SP2 release).
  • Niz 28 Jan 2010
    Hi Nedyalko,

    Thanks for the great post. It was exactly what I was looking for.

    I'm still new to Silverlight. So I was wondering how I would catch the SelectedColorChanged event of the RadColorPicker in the RadColorPickerColumn class from the XAML file.

    Thanks,

    Niz
  • Nedyalko Nikolov 02 Feb 2010
    There are two ways to do that:
    1. In the CreateCellEditElement(...) method you can hook for the SelectedColorChanged event of the RadColorPicker. You can use this approach if you want to do something with the new selected color while current GridViewCell is still in edit mode.
    2. You can hook for the RadGridView.CellEditEnded event take the new color from event args and make some custom action.

    Let me know if this does not help.
  • Sandi Markon 22 Mar 2010
    Hello,

    with last build of Telerik Silverlight Controls 2010 Q1 I cannot make VS 2010 designer to work:
    Exception throwed: "A value of type 'RadColorPickerColumn' cannot be added to a collection or dictionary of type 'GridViewColumnCollection'."

    When I run app everything works fine! Where is the problem???
  • Nedyalko Nikolov 25 Mar 2010
    I've just tested the example application with VS 2010 Version 10.0.30128.1 RC1Rel, and indeed design time support is far from OK but there is no error message. I guess problem is in VS, but I cannot say why.
  • Aileen 15 May 2010
    how can i select this row when click  the custom button cell in this row?
  • Alieen 15 May 2010
    In 2009 Q3,   I have a column that has delete button. ie. each row has a delete button.
    Now i realise that to click delete, i have to click twice to click fire the click event for that button.
    I'm suspecting, the first click is to get focus on the gridview first.

    Is there a way to click the button with only 1 click? Please reply as soon as possible.Thanks a lot.
  • jack 28 May 2010
    Will this technique work if I want to change the editor for different rows? ie) Have a datepicker on one row, the colorpicker on another, a slider, etc.?

    thanks
  • Nedyalko Nikolov 05 Jul 2010
    Sorry for the late reply.

    Hi Alieen,

    You can check this online example for some ideas how to implement this.

    Hi Jack,

    Yes this is possible since you get the underlying business object as an argument of the CreateCellEditElement method, and it is up to you what kind of editor to create.

Add comment

  1. Formatting options
       
     
     
     
     
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)