Hi, I'm trying to create a class deriving from the GridViewDataColumn. For example, I'd like to make the FontWeight Bold based on the bounded value. I was able to do it successfully for the view control by overriding CreateCellElement. So I thought overriding CreateCellEditElement would be the other method of the edit control but it is not working as expected. Attached is an illustration of the issue. Also, I have a sample project but I can't attach it here.
Thanks
Jan
8 Answers, 1 is accepted


Thank you for the provided sample project.
The reason for the described behavior is that the CreateCellEditElement method is invoked too early. What I can suggest, is overriding the PrepareCellForEdit method of the GridViewDataColumn instead of the CreateCellEditElement method. It is called later than the CreateCellEditElement method and the TextBox editor will have its Text set by this point.
However, please note that you will also need to introduce some logic in order to restore the bolded/italicized text back to its string representation, when the cell gets out of edit mode. If the tags are removed when the cell enters edit mode, after the cells exits edit mode, the bound property string will not contain the tags. That said, I cannot say for certain whether this approach would work in all scenarios.
Hope this helps.
Regards,
Vladimir Stoyanov
Progress Telerik

Hello Vladimir,
Thanks for the immediate response. You're correct on overriding PrepareCellForEdit and restoring the tags to the string value. Below is the initial working prototype of my custom class if anybody is interested in trying or looking at it. Will most likely update it later after some refactoring.
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Input;
using
Telerik.Windows.Controls;
using
Telerik.Windows.Controls.GridView;
namespace
Demo1
{
internal
sealed
class
HtmlGridViewDataColumn : GridViewDataColumn
{
public
HtmlGridViewDataColumn()
{
Initialized += HtmlGridViewDataColumn_Initialized;
}
public
override
FrameworkElement CreateCellElement(GridViewCell cell,
object
dataItem) =>
UpdateTextElement(
base
.CreateCellElement(cell, dataItem));
public
override
object
GetNewValueFromEditor(
object
editor)
{
var t = (TextBox)editor;
if
(t.FontWeight == FontWeights.Bold)
ApplyTag(TagName.Bold);
if
(t.FontStyle == FontStyles.Italic)
ApplyTag(TagName.Italic);
return
base
.GetNewValueFromEditor(editor);
void
ApplyTag(
string
name)
{
var html =
new
HtmlStringBuilder(t.Text);
if
(!html.ContainsTag(name))
t.Text =
new
HtmlStringBuilder(t.Text)
.ToggleTag(name)
.ToString();
}
}
protected
override
object
PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
UpdateTextElement(editingElement);
return
base
.PrepareCellForEdit(editingElement, editingEventArgs);
}
private
static
FrameworkElement UpdateTextElement(FrameworkElement element)
{
switch
(element)
{
case
TextBlock t:
Update(
new
TextBlockAdapter(t));
break
;
case
TextBox t:
Update(
new
TextBoxAdapter(t));
break
;
}
return
element;
void
Update(ITextElementdapter adapter)
{
adapter.FontWeight =
TryRemoveTag(TagName.Bold)
? FontWeights.Bold
: FontWeights.Normal;
adapter.FontStyle =
TryRemoveTag(TagName.Italic)
? FontStyles.Italic
: FontStyles.Normal;
bool
TryRemoveTag(
string
name)
{
var html =
new
HtmlStringBuilder(adapter.Text);
var c = html.ContainsTag(name);
html.RemoveTag(name);
adapter.Text = html.ToString();
return
c;
}
}
}
private
void
HtmlGridViewDataColumn_Initialized(
object
sender, System.EventArgs e)
{
DataControl.BeginningEdit += DataControl_BeginningEdit;
DataControl.CellEditEnded += DataControl_CellEditEnded;
}
private
void
DataControl_BeginningEdit(
object
sender, GridViewBeginningEditRoutedEventArgs e)
{
if
(e.Cell.Column ==
this
)
e.Cell.KeyDown += Cell_KeyDown;
}
private
void
DataControl_CellEditEnded(
object
sender, GridViewCellEditEndedEventArgs e)
{
if
(e.Cell.Column ==
this
)
e.Cell.KeyDown -= Cell_KeyDown;
}
private
void
Cell_KeyDown(
object
sender, KeyEventArgs e)
{
if
(Keyboard.Modifiers == ModifierKeys.Control)
{
if
(e.Key == Key.B)
{
var textBox = GetTextBox();
textBox.FontWeight =
textBox.FontWeight == FontWeights.Bold
? FontWeights.Normal
: FontWeights.Bold;
}
else
if
(e.Key == Key.I)
{
var textBox = GetTextBox();
textBox.FontStyle =
textBox.FontStyle == FontStyles.Italic
? FontStyles.Normal
: FontStyles.Italic;
}
else
if
(e.Key == Key.Tab)
{
var textBox = GetTextBox();
var caretIndex = textBox.CaretIndex;
var tab =
"\t"
;
textBox.AcceptsTab =
true
;
textBox.Text = textBox.Text.Insert(caretIndex, tab);
textBox.CaretIndex = caretIndex + tab.Length;
e.Handled =
true
;
}
TextBox GetTextBox()
{
var cell = (GridViewCell)sender;
return
(TextBox)cell.GetEditingElement();
}
}
}
private
static
class
TagName
{
public
static
string
Bold =>
"b"
;
public
static
string
Italic =>
"i"
;
}
private
class
HtmlStringBuilder
{
private
string
htmlString;
public
HtmlStringBuilder(
string
htmlString)
{
this
.htmlString = htmlString;
}
public
HtmlStringBuilder ToggleTag(
string
name)
{
var startTag = StartTag(name);
var endTag = EndTag(name);
if
(ContainsTag(name))
RemoveTag(name);
else
htmlString = $
"{startTag}{htmlString}{endTag}"
;
return
this
;
}
public
HtmlStringBuilder RemoveTag(
string
name)
{
htmlString = htmlString
?.Replace(StartTag(name),
""
)
.Replace(EndTag(name),
""
);
return
this
;
}
public
bool
ContainsTag(
string
name) => htmlString.Contains(StartTag(name));
private
static
string
StartTag(
string
name) => $
"<{name}>"
;
private
static
string
EndTag(
string
name) => StartTag($
"/{name}"
);
public
override
string
ToString() => htmlString;
}
private
interface
ITextElementdapter
{
FontWeight FontWeight {
get
;
set
; }
FontStyle FontStyle {
get
;
set
; }
string
Text {
get
;
set
; }
}
private
class
TextBoxAdapter : ITextElementdapter
{
private
readonly
TextBox textBox;
public
TextBoxAdapter(TextBox textBox)
{
this
.textBox = textBox;
}
public
FontWeight FontWeight {
get
=> textBox.FontWeight;
set
=> textBox.FontWeight = value; }
public
FontStyle FontStyle {
get
=> textBox.FontStyle;
set
=> textBox.FontStyle = value; }
public
string
Text {
get
=> textBox.Text;
set
=> textBox.Text = value; }
}
private
class
TextBlockAdapter : ITextElementdapter
{
private
readonly
TextBlock textBlock;
public
TextBlockAdapter(TextBlock textBlock)
{
this
.textBlock = textBlock;
}
public
FontWeight FontWeight {
get
=> textBlock.FontWeight;
set
=> textBlock.FontWeight = value; }
public
FontStyle FontStyle {
get
=> textBlock.FontStyle;
set
=> textBlock.FontStyle = value; }
public
string
Text {
get
=> textBlock.Text;
set
=> textBlock.Text = value; }
}
}
}

using System.Windows.Controls;
using System.Windows.Data;
using Telerik.Windows.Controls.GridView;
using Xunit;
namespace Demo1
{
public class HtmlGridViewDataColumnTests
{
[WpfFact]
public void Demo()
{
var column = new HtmlGridViewDataColumn
{
DataMemberBinding = new Binding("Name")
};
var textBlock = (TextBlock)column.CreateCellElement(
new GridViewCell(),
new MyItem());
Assert.Equal("a", textBlock.Text);
}
}
public class MyItem
{
public string Name { get; set; } = "a";
}
}
Thank you for the update.
I am glad to hear that you were able to achieve what you were going for. Thank you for the sharing your solution with the community.
As for unit testing, I would suggest testing the custom column with the RadGridView control, since the GridViewDataColumn, which it inherits is designed to be used in that way.
Can you check out the testing infrastructure of the RadGridView by downloading its source code from your telerik account? The test project is inside Controls\GridView\GridView.Tests directory. You can also open the GridView_WPF.sln in the Controls\GridView directory, which includes the test project. It includes many tests for different functionality, which may be helpful for your scenario.
Please, check the test project out and let me know, if you find it helpful.
Regards,
Vladimir Stoyanov
Progress Telerik

Hello Vladimir,
Thanks for the suggestion. Unfortunately, I can't figure out how to sort the grid programatically and examine its sorted items for testing. There's a `GridViewDataControl.PerformSorting` method that's used in GridView.Tests but it's internal. Do you know any other way I can sort the grid in unit tests?
```
using System.Windows.Data;
using System.Windows.Interactivity
using Telerik.Windows.Controls;
using Telerik.Windows.Data;
using Xunit;
[WpfFact] // from Xunit.StaFact
public void Test()
{
var grid = new RadGridView();
grid.AutoGenerateColumns = false;
grid.Columns.Add(new GridViewDataColumn { DataMemberBinding = new Binding() });
grid.ItemsSource = new[] { 1, 3, 2 };
Interaction.GetBehaviors(grid).Add(new CustomSortBehavior());
/// TODO: perform sort
Assert.Equal(new[] { 1, 2, 3}, grid.Items);
}
public class CustomSortBehavior : Behavior<RadGridView>
{
/// custom sort implementation
}
```
Thank you for the provided code snippet.
Can you check out the Programmatic Sorting article in our documentation and let me know if you find it helpful?
I am looking forward to your reply.
Regards,
Vladimir Stoyanov
Progress Telerik