
A grid
without editors can not be called a grid. Convenient data editing feature is
one of the most important advantages of Dapfor's product over other grids.
In developing
the editing system programmers considered that the grid should support standard
.Net framework editors, enable easy use of new editors and controls and use any
third-party editors and graphical controls.
Standard
Microsoft DataGridView control for data editing supports different column
types: DataGridViewTextBoxColumn, DataGridViewComboBoxColumn, etc. Many
third-party grid developers use similar approach. However, we think that editor
type shouldn't depend on column type as this approach is too bulky and limits
available editors, while the programmer often has to write a lot of code to
implement a new editor. Besides, we think that a header column shouldn't
contain information of data types. Below we shall provide some examples of
this.
NetGrid editing
system is based on well-known proven data editing principle used in
PropertyGrid. This approach is based on UITypeEditor class and supports use of
almost any controls in a dropdown combo box.
Let's say a few
words about UITypeEditor. Many .Net classes have their own editors. They can be
declared very easily:
class MyEditor :
UITypeEditor{...}
[Editor(typeof(MyEditor),
typeof(UITypeEditor))]class MyClass{...}
To get an editor
object the following code can be used.
//Custom
editorUITypeEditor myEditor =
(UITypeEditor)TypeDescriptor.GetEditor(typeof(MyClass), typeof(UITypeEditor));
//Standard
editor to edit Color objectsUITypeEditor colorEditor =
(UITypeEditor)TypeDescriptor.GetEditor(typeof(Color), typeof(UITypeEditor));
By the way, we
see editors of this kind when we edit properties in VisualStudio. This is
how property editinglooks in IDE.
UITypeEditor has
several styles determined by UITypeEditorEditStyle enum. Data editing in
different styles looks as follows.
Data editing
starts on left-click over PropertyGrid cell. The grid reads current value of
data object property and calls UITypeEditor.EditValue() method. Further
implementation of editor completely depends on developer. With modal call the
programmer just has to call Form.ShowModal() from of any user form. As the call
is modal, the developer has to return a new value after closing the form. This
is how font picker editor works.
class MyEditor :
UITypeEditor{public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context){return
UITypeEditorEditStyle.Modal;}
public override
object EditValue(ITypeDescriptorContext context, IServiceProvider provider,
object value){Font font = value as Font;FontDialog dlg = new FontDialog(); =
font;if(dlg.ShowDialog() == ){value = ;}return value;}}
Usage of user
controls in a dropdown combo box is not much more complicated. Implementation
of IWindowsFormsEditorService interface ensures modal display of user control
in a dropdown window. IWindowsFormsEditorService.DropDownControl() method is
equivalent to Form.ShowModal() and to close control the programmer only has to
call IWindowsFormsEditorService.CloseDropDown() method.
class MyEditor :
UITypeEditor{public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context){return
UITypeEditorEditStyle.DropDown;}
public override
object EditValue(ITypeDescriptorContext context, IServiceProvider provider,
object value){//Get the editor serviceIWindowsFormsEditorService service =
provider.GetService(typeof(IWindowsFormsEditorService)) as
IWindowsFormsEditorService;
if(service !=
null){//Create a controlusing(Button someControl = new Button()){//Add a reaction
on user clicksomeControl.Click += delegate{//User has clicked on the button
inside of the dropdown box. Close the control.service.CloseDropDown();
//Set a desired
value//value = _new_value_;};
//Start editing
with specified control.//This call opens a modal dropdown
boxservice.DropDownControl(someControl);}}
return value;}}
.Net Grid uses
similar approach and implements IWindowsFormsEditorService interface in the
same way. This enables it to use standard controls and third-party controls for
data editing.
Data editing in
cells
As it has been
previously said, UITypeEditor may have only 3 styles: (None, Modal, DropDown).
If None style is applied, PropertyGrid uses TextBox. We think that it is a
significant limitation as the programmer may desire to place his own control
inside a cell and not in a dropbox (e.g. a slider). Dapfor NetGrid expands its
features with UITypeEditorEx class. This class has an enhanced set of methods
that can be used to create an arbitrary control directly over data cell and to
use it for editing.
//This editor
creates a clickable button inside the cellclass MyEditor :
UITypeEditorEx{public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context){return ;}
public override
StopEditReason EditCell(IGridEditorService service, Cell cell, StartEditReason
reason){using(Button button = new Button()){//Add a reaction on user
clickbutton.Click += delegate{//Close control with some
reasonservice.CloseCellControl(StopEditReason.UserStop);
//Set a new
value if neededcell.Value = _newvalue_;};
//Start editing
in cellreturn service.CellEditControl(button, cell.VirtualBounds, reason);}}}
Let's note that
user control should be created only for the time of data editing. It is
especially important when the grid operates a lot of rows. Controls cannot be
created for each row as Windows limits maximum number of controls created by
the application. For this purpose UITypeEditorEx provides a feature of drawing
control directly inside a cell. This way user feels comfortable when editing
data as if he was working with actual controls. However, in reality the grid
creates controls only when the user clicks a cell.
public class
TrackBarEditor : UITypeEditorEx{private readonly int _minValue;private readonly
int _maxValue;
//Constructorpublic
TrackBarEditor(int minValue, int maxValue){_minValue = minValue;_maxValue =
maxValue;}
//Indicates that
the editor draws the entire cellpublic override bool
GetPaintCellSupported(){return true;}
//Painting
routinepublic override void PaintCell(PaintCellEventArgs e){//Do basic painting
without =
string.Empty;e.PaintAll();e.Handled = true;
Rectangle r =
e.Cell.VisibleBounds;r.Inflate(-4, 0);int value = (int) e.Cell.Value;
int range =
_maxValue - _minValue;if(range > 0){Rectangle channel = new Rectangle(r.X,
r.Y + r.Height / 2 - 3, r.Width, 4);int offset = (value*(r.Width -
4)/range);int x = r.X + offset;if(e.Grid._impl.IsRightToLeft){x = r.Right -
offset - 4;}
Rectangle button
= new Rectangle(x, r.Y + 3, 5, r.Height - 6);
//Draw the track
channelControlPaint.DrawBorder3D(e.Graphics, channel,
Border3DStyle.SunkenInner);
//Draw the
thumbif (Application.RenderWithVisualStyles){VisualStyleRenderer renderer = new
VisualStyleRenderer();renderer.DrawBackground(e.Graphics,
button);}else{ControlPaint.DrawButton(e.Graphics, button,
ButtonState.Normal);}}}
public override
StopEditReason EditCell(IGridEditorService service, Cell cell, StartEditReason
reason){StopEditReason stopReason = StopEditReason.Undefined;//Edit the cell if
the user has clicked on the cell with the left buttonif ( != null &&
Equals(StartEditReason.LButtonClick, reason)){cell.EnsureVisible();//Create a
real controlusing (TrackBar control = new TrackBar()){control.RightToLeft =
._impl.IsRightToLeft ? : ;
//Initialize the
controlcontrol.TabStop = true;control.BackColor =
cell.Appearance.BackColor;control.TickStyle = ;control.Minimum =
_minValue;control.Maximum = _maxValue;int value =
Convert.ToInt32(cell.Value);value = (control.Minimum, value);value =
(control.Maximum, value);control.Value = value;
//Handle the
MouseUp eventscontrol.MouseUp += delegate{//Stop edit in
placeservice.CloseCellControl(StopEditReason.UserStop);};
//Start Edit in
place.stopReason = service.CellEditControl(control, cell.VirtualBounds,
reason);
//Set a new
value to Cellcell.Value = Convert.ChangeType(control.Value,
cell.DataField.FieldType);}}return stopReason;}}
Specific case -
data editing without graphical controls.
In some cases it
is not necessary to create controls. This may happen when a data field contains
bool value or a star rating assigned by user. Such values don't require control
creation but only painting current state and reacting on user click on a cell.
public class
RatingEditor : UITypeEditorEx{public override bool
GetPaintCellSupported(){//The cell should be repainted in the editorreturn
true;}
public override
void PaintCell(PaintCellEventArgs e){//Do basic painting without = string.Empty;e.PaintAll();e.Handled = true;
//Draw the stars
over the cellRectangle bounds = e.Cell.VirtualBounds;bounds.Width =
Resources.star_grey.Width;for (int i = 0; i = curRating ? Resources.star_grey :
Resources.star_yellow;e.Graphics.DrawImage(image, bounds);bounds.X +=
bounds.Width;}}}
public override
StopEditReason EditCell(IGridEditorService service, Cell cell, StartEditReason
reason){//Edit the cell if the user has clicked on the cell with the left
buttonif ( != null && Equals(StartEditReason.LButtonClick,
reason)){Point pt = .PointToClient(Cursor.Position);Rectangle bounds = cell.VirtualBounds;
int imageWidth =
Resources.star_grey.Width;if (bounds.Contains(pt) && imageWidth >
0){//Set a new rating to the data objectcell.Value = ((pt.X -
bounds.X)/imageWidth) + 1;cell.Invalidate();}}
return
StopEditReason.UserStop;}}
Enabling editing
The following
conditions should be met for data editing by user:
Property of the
edited object should have a setter.
Column.Editable
property should be set to true
Editor setup
There are
several methods that can be used for editor setup.
As it has been
previously said, editors can be set only for object types
[Editor(typeof(MyEditor),
typeof(UITypeEditor))]class MyClass{...}
Dapfor has
expanded use of these attributes for class properties. For example, the
following code can be used for data editing with a rating editor:
class
SomeProduct{private int _rating;
[Editor(typeof(RatingEditor),
typeof(UITypeEditor))]public int Rating{get { return _rating; }set { _rating =
value; }}}
If grid rows
contain objects of different types, the grid may use different editors
depending on class properties.
Editors can be
set in columns:
Header header =
grid.Headers[0];header[""rating""].Editor = new
RatingEditor();
Editors can also
be set directly when the user clicks a cell. This feature can be used to disable
editing of some cells:
grid.CellBeginEdit
+= delegate(object sender, GridCellBeginEditEventArgs e){if (e.Cell.Column !=
null && ==
""rating""){e.Editor = new RatingEditor();}};
Validation
Users may enter
wrong data when editing. A programmer can use validation features to transmit
only correct values to data objects. With editors based on UITypeEditorEx this
is quite simple as the programmer has full control over data editing and
therefore controls the validation process and transmission of new value to a
data object.
public override
StopEditReason EditCell(IGridEditorService service, Cell cell, StartEditReason
reason){using(SomeControl control = new SomeControl()){button.Click +=
delegate{//validate user's input. If data is correct, close the editorif(control.IsValid){service.CloseCellControl(StopEditReason.UserStop);}};
//Start editing
in cellreturn service.CellEditControl(control, cell.VirtualBounds, reason);}}
A similar
approach can be used for editors based on UITypeEditor. Besides, the grid
provides an interface that controls grid behavior in data editing and
validation. The following example shows how to display a tooltip error message
upon incorrect data entry.
grid.ValidateCell
+= delegate(object sender, ValidateCellEventArgs e){e.ErrorText = ""Invalid
data"";e.Action = ValidateCellAction.CancelValue;};
Navigation
The grid
provides editor navigation feature to make data editing easier for users. It is
possible to set the reason for stopping cell editing, such as Esc, Enter, Tab
keys, Shift+Tab shortcut, etc. Depending on the reason the grid decides whether
editing should be continued or ended. Grid reactions on completion of data
sorting in a cell are shown in the table below.
Reason
Reaction
StopEditReason.SelectValue
Navigation to
the next editable cell
StopEditReason.Enter
Navigation to
the next editable cell
Navigation to
the next editable cell
StopEditReason.ShiftTab
Navigation to
the previous editable cell
StopEditReason.LButtonOutsideClick
Editing a new
cell, over which the cursor is
StopEditReason.Escape
End of editing
the current cell
StopEditReason.Exception
End of editing
the current cell
The grid
provides the following 2 callbacks to determine next or previous cell for
editing.Therefore, the programmer may specify any desired cell for this
purpose.
grid.PrevEditableCell
+= delegate(object sender, GridEditableCellEventArgs e){e.NewEditableCell =
_a_new_editable_cell;};
grid.NextEditableCell
+= delegate(object sender, GridEditableCellEventArgs e){e.NewEditableCell =
_a_new_editable_cell;};
We hope that
reading this tutorial will make data editing in NetGrid easy and clear for you.
We also hope that effortsof Dapfor's programmers will result in creation of
beautiful and compact applications.

0 Response to "Use Editors in .Net Grid"
Post a Comment