I have a data class, Category, which has a property Children. This hierarchy, from a root Category node is bound to a TreeView. Category.Name is what displays for the Category. TreeView currently works fine.
What I need to figure out how to do is raise an edit event when the user makes a modification of the display value. I do not want to actually have anything edit the underlying bound value, until I do it myself.
I have tried with and without an EditItemTemplate. In the RadTreeView.Edited event, Source is set to the Category instance itself. OriginalSource is set to the RadTreeViewItem. NewText and OldText are both null (deprecated.) OldValue and NewValue are set to teh Category instance itself. And the Category.Name property has been edited. OldValue.Name and NewValue.Name are both the new value. Of course. Since it's set to the instance itself, there's no way to distuinguish between the before and after states.
What the heck can I do here? I thought I'd just use an EditItemTemplate, with a custom TextBox, in order to capture the new value, and set the Binding.Mode of the HierarchalDataTemplate to OneWay, to prevent it from being changed. However, now I have no way in the Edited event to retrieve the new value. I can't find the TextBox of the EditItemTemplate anywhere underneath RadTreeViewItem, and the OldValue and NewValue are of course still set to the Category itself (which is no longer altered, at least.)
Help?
What I need to figure out how to do is raise an edit event when the user makes a modification of the display value. I do not want to actually have anything edit the underlying bound value, until I do it myself.
I have tried with and without an EditItemTemplate. In the RadTreeView.Edited event, Source is set to the Category instance itself. OriginalSource is set to the RadTreeViewItem. NewText and OldText are both null (deprecated.) OldValue and NewValue are set to teh Category instance itself. And the Category.Name property has been edited. OldValue.Name and NewValue.Name are both the new value. Of course. Since it's set to the instance itself, there's no way to distuinguish between the before and after states.
What the heck can I do here? I thought I'd just use an EditItemTemplate, with a custom TextBox, in order to capture the new value, and set the Binding.Mode of the HierarchalDataTemplate to OneWay, to prevent it from being changed. However, now I have no way in the Edited event to retrieve the new value. I can't find the TextBox of the EditItemTemplate anywhere underneath RadTreeViewItem, and the OldValue and NewValue are of course still set to the Category itself (which is no longer altered, at least.)
Help?
3 Answers, 1 is accepted
0
Hi Jerome,
I'm pasting a part of a support communication which is related to your question. Take a look at it and let me know if anything is unclear and you need further assistance. I'd be glad to assist you.
====================== Customer ======================
The RadTreeView PreviewEdited event is being fired after the underlying data has already been updated. I am trying to catch this event to validate the data before allowing the commit. When the event is caught in my code the underlying bound data has already been changed. As a test I hooked into the EditStarted and that fired appropriately, as did the Edited event. I set up the source code and debugged into the EditableHeaderItemsControl.cs -> CommitEdit(). If I change the code to move the following two lines after the preview event then things fire when expected:
Move them down past the this.OnPreviewEdited() block beginning on line 558.
The full change is below. This still isn't 100% right as there are two different sets for the 'newEditValue'. Any pointers as to what I should change the function to to fix this error? Can you confirm on your side that the event is indeed firing too late.
Follow-up. Looks like there are two problems:
1) The early call, before the PreviewEvent is fired, to this.UpdateTextBoxesInBeforeEditEnd();
This updates the underlying data before the event fires.
2) An error (?) in the call to this.GetEditValue():
Should this code in GetEditValue() be checking for != on the GetBindingExpression() check?
If I move just the UpdateTextBoxesInBeforeEndEdit() down, and change to != null it looks like things are working as expected. I need to do more testing to confirm.
====================== Telerik ======================
Hi,
Actually the points you mentioned are implemented intentionatelly in that manner.
1. The UpdateTextBoxesInBeforeEditEnd() is called before the preview event is fired because there is no other easy way that we can report the new value to the user.
Imagine you have a bound tree view with several TextBox-es in the header edit template. You could define oldValues and newValues (plural) properties of type List or Dictionary and provide all the new and old values of the TextBox-es.
Another option is to return a reference to the business item that is bound to that particular RadTreeViewItem and let the user operate with it (natural because he knows how to work with its own business item). This is how the tree view is implemented. That is why we need to update the business data so that the user would be able to retrieve the newly entered value.
Perhaps the "Preview" part of the name is a bit confusing. You should think of the PreviewEdited event as event that is fired before the action is completed and can be used to rollback the operation.
2. The GetEditValue() method returns the first TextBox text when there is no binding attached to it. Otherwise it returns the header, which typically contains the bound business object from RadTreeView.ItemsSource property.
If you need to rollback the edit action you can do the following:
You can handle the PreviewEdited event and call CancelEdit on the RadTreeViewItem that is currently under edit.
Hope this helps. Please let us know if you need more info on the topic or have any further questions.
====================== Customer ======================
Hello,
I've reverted my change to the Telerik source to re-test based on your email. Things still don't work as expected:
1) My treeview is bound to hierarchical data
2) When the PreviewEdited event is fired the OldText and NewText values are null
3) When the PreviewEdited event is fired the OldValue and NewValue values are the same -- I would expect these to be different!!! This is the root of my problem. The old value is gone so I can't perform the logic/action I need based on comparing the old and new values.
4) Why does the underlying data model value (bound hierarchical data) get changed BEFORE I get a chance to validate the edit and set e.Handled appropriately?
I am not using the ItemEditTemplate or ItemEditTemplateSelector at this time.
Based on the WPF TreeView documentation, which states:
"The PreviewEdited event is fired just before the new Header text of the item is applied. If the treeview is data bound you can update your DataSource with the new value. The Edited event is fired once the new Header text for the item is applied. Via the RadTreeViewItemEditedEventArgs of the PreviewEdited and Edited events you can get access to the new text of the Header property, as well as to the old one."
I wouldn't expect the data bound value to be changed until the PreviewEdit event exited with e.Handled = false.
Thanks.
====================== Telerik ======================
Hi,
Thank you for elaborating more on your scenario. I've spoken with Kiril and he is aware of the answer I've provided you with. At first glance it seemed that the suggestion you gave us would work. However, digging deeper we discovered that there are valid reasons why RadTreeView works the way it does.
Regarding your points:
"2) When the PreviewEdited event is fired the OldText and NewText values are null"
The NewText and OldText properties are not null only in cases when you edit a RadTreeView that is not bound to a data source:
In the above sample, if you change Item 0 to 123...
...in the PreviewEdited event you will receive Item0 for OldText and 123 for NewText. The OldText and NewText properties are always null when your RadTreeView is bound to a data source. This is the reason why we've marked them with obsolete and will remove them in any of the following major releases. For data-bound scenarios, we strongly advocate you use the NewValue and OldValue properties.
"3) When the PreviewEdited event is fired the OldValue and NewValue values are the same -- I would expect these to be different!!! This is the root of my problem. The old value is gone so I can't perform the logic/action I need based on comparing the old and new values."
Apparently I did not manage to explain very clearly why OldValue and NewValue are the same in the PreviewEdited event. Imagine the following basic scenario. You have a data-bound RadTreeView:
This RadTreeView is bound to a collection of business items that look as the one bellow:
You select Item 0, press F2 on the keyboard and Item 0 goes into edit mode:
You type 123 and press Enter on the keyboard. The PreviewEdited event is fired. We can't know what you have typed unless we update the business object. That is why we must update the business object (which happens in UpdateTextBoxesInBeforeEditEnd). This way you can receive the updated business object as NewValue in the PreviewEdited event. Since we have updated the business object, OldValue is lost. The only way for us to send you correct versions of OldValue and NewValue is only if we create a deep copy of the business object before it has been updated in UpdateTextBoxesInBeforeEditEnd. As you understand, this will significantly impact the performance of RadTreeView since deep copying will be done using reflection.
One possible workaround, which in our opinion will serve you good, is to preserve the property you want in a variable in the PreviewEditStarted event. Then, in the PreviewEdited event you can decide whether you want to rollback to that property value or not.
"4) Why does the underlying data model value (bound hierarchical data) get changed BEFORE I get a chance to validate the edit and set e.Handled appropriately?"
I believe the above explanation answers this question.
Let us know how this sounds to you. We'd be glad to continue the discussion further.
All the best,
Kiril Stanoev
the Telerik team
I'm pasting a part of a support communication which is related to your question. Take a look at it and let me know if anything is unclear and you need further assistance. I'd be glad to assist you.
====================== Customer ======================
The RadTreeView PreviewEdited event is being fired after the underlying data has already been updated. I am trying to catch this event to validate the data before allowing the commit. When the event is caught in my code the underlying bound data has already been changed. As a test I hooked into the EditStarted and that fired appropriately, as did the Edited event. I set up the source code and debugged into the EditableHeaderItemsControl.cs -> CommitEdit(). If I change the code to move the following two lines after the preview event then things fire when expected:
550:
this
.UpdateTextBoxesInBeforeEditEnd();
551: var newEditValue =
this
.GetEditValue();
Move them down past the this.OnPreviewEdited() block beginning on line 558.
The full change is below. This still isn't 100% right as there are two different sets for the 'newEditValue'. Any pointers as to what I should change the function to to fix this error? Can you confirm on your side that the event is indeed firing too late.
public
virtual
bool
CommitEdit()
{
if
(!
this
.IsInEditMode && !
this
.isInEditModeReentrancyCheck)
{
return
true
;
}
this
.UpdateTextBoxesInBeforeEditEnd();
var newEditValue =
this
.GetEditValue();
if
(
this
.HasInvalidEditElements())
{
return
false
;
}
var previewCommit =
new
RadTreeViewItemEditedEventArgs(newEditValue,
this
.oldEditValue, PreviewEditedEvent,
this
);
if
(
this
.OnPreviewEdited(previewCommit))
{
return
false
;
}
this
.UpdateTextBoxesInBeforeEditEnd();
var newEditValue =
this
.GetEditValue();
newEditValue = previewCommit.NewValue;
this
.IsInEditMode =
false
;
#if !WPF
this
.ChangeVisualState();
#endif
this
.SetEditValue(newEditValue);
var commit =
new
RadTreeViewItemEditedEventArgs(newEditValue,
this
.oldEditValue, EditedEvent,
this
);
this
.OnEdited(commit);
return
true
;
}
Follow-up. Looks like there are two problems:
1) The early call, before the PreviewEvent is fired, to this.UpdateTextBoxesInBeforeEditEnd();
This updates the underlying data before the event fires.
2) An error (?) in the call to this.GetEditValue():
var newEditValue =
this
.GetEditValue();
Should this code in GetEditValue() be checking for != on the GetBindingExpression() check?
if
(focusableTextBox !=
null
&& focusableTextBox.GetBindingExpression(TextBox.TextProperty) ==
null
)
{
If I move just the UpdateTextBoxesInBeforeEndEdit() down, and change to != null it looks like things are working as expected. I need to do more testing to confirm.
====================== Telerik ======================
Hi,
Actually the points you mentioned are implemented intentionatelly in that manner.
1. The UpdateTextBoxesInBeforeEditEnd() is called before the preview event is fired because there is no other easy way that we can report the new value to the user.
Imagine you have a bound tree view with several TextBox-es in the header edit template. You could define oldValues and newValues (plural) properties of type List or Dictionary and provide all the new and old values of the TextBox-es.
Another option is to return a reference to the business item that is bound to that particular RadTreeViewItem and let the user operate with it (natural because he knows how to work with its own business item). This is how the tree view is implemented. That is why we need to update the business data so that the user would be able to retrieve the newly entered value.
Perhaps the "Preview" part of the name is a bit confusing. You should think of the PreviewEdited event as event that is fired before the action is completed and can be used to rollback the operation.
2. The GetEditValue() method returns the first TextBox text when there is no binding attached to it. Otherwise it returns the header, which typically contains the bound business object from RadTreeView.ItemsSource property.
If you need to rollback the edit action you can do the following:
private
void
RadTreeView_PreviewEdited(
object
sender, Telerik.Windows.Controls.RadTreeViewItemEditedEventArgs e)
{
e.Handled = ........
if
(e.Handled)
{
treeView1.SelectedContainer.CancelEdit();
}
}
You can handle the PreviewEdited event and call CancelEdit on the RadTreeViewItem that is currently under edit.
Hope this helps. Please let us know if you need more info on the topic or have any further questions.
====================== Customer ======================
Hello,
I've reverted my change to the Telerik source to re-test based on your email. Things still don't work as expected:
1) My treeview is bound to hierarchical data
2) When the PreviewEdited event is fired the OldText and NewText values are null
3) When the PreviewEdited event is fired the OldValue and NewValue values are the same -- I would expect these to be different!!! This is the root of my problem. The old value is gone so I can't perform the logic/action I need based on comparing the old and new values.
4) Why does the underlying data model value (bound hierarchical data) get changed BEFORE I get a chance to validate the edit and set e.Handled appropriately?
I am not using the ItemEditTemplate or ItemEditTemplateSelector at this time.
Based on the WPF TreeView documentation, which states:
"The PreviewEdited event is fired just before the new Header text of the item is applied. If the treeview is data bound you can update your DataSource with the new value. The Edited event is fired once the new Header text for the item is applied. Via the RadTreeViewItemEditedEventArgs of the PreviewEdited and Edited events you can get access to the new text of the Header property, as well as to the old one."
I wouldn't expect the data bound value to be changed until the PreviewEdit event exited with e.Handled = false.
Thanks.
====================== Telerik ======================
Hi,
Thank you for elaborating more on your scenario. I've spoken with Kiril and he is aware of the answer I've provided you with. At first glance it seemed that the suggestion you gave us would work. However, digging deeper we discovered that there are valid reasons why RadTreeView works the way it does.
Regarding your points:
"2) When the PreviewEdited event is fired the OldText and NewText values are null"
The NewText and OldText properties are not null only in cases when you edit a RadTreeView that is not bound to a data source:
<
telerik:RadTreeView
PreviewEdited
=
"RadTreeView_PreviewEdited"
IsEditable
=
"True"
>
<
telerik:RadTreeViewItem
Header
=
"Item 0"
>
<
telerik:RadTreeViewItem
Header
=
"Item 0.1"
/>
</
telerik:RadTreeViewItem
>
</
telerik:RadTreeView
>
In the above sample, if you change Item 0 to 123...

...in the PreviewEdited event you will receive Item0 for OldText and 123 for NewText. The OldText and NewText properties are always null when your RadTreeView is bound to a data source. This is the reason why we've marked them with obsolete and will remove them in any of the following major releases. For data-bound scenarios, we strongly advocate you use the NewValue and OldValue properties.
"3) When the PreviewEdited event is fired the OldValue and NewValue values are the same -- I would expect these to be different!!! This is the root of my problem. The old value is gone so I can't perform the logic/action I need based on comparing the old and new values."
Apparently I did not manage to explain very clearly why OldValue and NewValue are the same in the PreviewEdited event. Imagine the following basic scenario. You have a data-bound RadTreeView:

This RadTreeView is bound to a collection of business items that look as the one bellow:
public
class
DataItem
{
public
string
Name
{
get
;
set
;
}
}
You select Item 0, press F2 on the keyboard and Item 0 goes into edit mode:

You type 123 and press Enter on the keyboard. The PreviewEdited event is fired. We can't know what you have typed unless we update the business object. That is why we must update the business object (which happens in UpdateTextBoxesInBeforeEditEnd). This way you can receive the updated business object as NewValue in the PreviewEdited event. Since we have updated the business object, OldValue is lost. The only way for us to send you correct versions of OldValue and NewValue is only if we create a deep copy of the business object before it has been updated in UpdateTextBoxesInBeforeEditEnd. As you understand, this will significantly impact the performance of RadTreeView since deep copying will be done using reflection.
One possible workaround, which in our opinion will serve you good, is to preserve the property you want in a variable in the PreviewEditStarted event. Then, in the PreviewEdited event you can decide whether you want to rollback to that property value or not.
<
Grid
x:Name
=
"LayoutRoot"
Background
=
"White"
>
<
telerik:RadTreeView
x:Name
=
"treeView1"
PreviewEditStarted
=
"RadTreeView_PreviewEditStarted"
PreviewEdited
=
"RadTreeView_PreviewEdited"
IsEditable
=
"True"
>
<
telerik:RadTreeView.ItemTemplate
>
<
DataTemplate
>
<
Grid
>
<
TextBlock
Text
=
"{Binding Name}"
/>
</
Grid
>
</
DataTemplate
>
</
telerik:RadTreeView.ItemTemplate
>
</
telerik:RadTreeView
>
</
Grid
>
public
partial
class
MainPage : UserControl
{
string
dataItemName;
public
MainPage()
{
InitializeComponent();
treeView1.ItemsSource = Enumerable.Range(0, 5).Select(i =>
new
DataItem() { Name =
"Item "
+ i });
}
private
void
RadTreeView_PreviewEditStarted(
object
sender, Telerik.Windows.Controls.RadTreeViewItemEditedEventArgs e)
{
this
.dataItemName = (e.OldValue
as
DataItem).Name;
}
private
void
RadTreeView_PreviewEdited(
object
sender, Telerik.Windows.Controls.RadTreeViewItemEditedEventArgs e)
{
DataItem dataItem = e.NewValue
as
DataItem;
if
(dataItem.Name ==
"123"
/* or some other bad value */
)
{
e.Handled =
true
;
dataItem.Name =
this
.dataItemName;
}
}
}
public
class
DataItem : INotifyPropertyChanged
{
private
string
name;
public
string
Name
{
get
{
return
this
.name;
}
set
{
if
(
this
.name != value)
{
this
.name = value;
this
.OnPropertyChanged(
"Name"
);
}
}
}
public
event
PropertyChangedEventHandler PropertyChanged;
private
void
OnPropertyChanged(
string
propertyName)
{
if
(
this
.PropertyChanged !=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
}
"4) Why does the underlying data model value (bound hierarchical data) get changed BEFORE I get a chance to validate the edit and set e.Handled appropriately?"
I believe the above explanation answers this question.
Let us know how this sounds to you. We'd be glad to continue the discussion further.
All the best,
Kiril Stanoev
the Telerik team
Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!
0

Jerome
Top achievements
Rank 1
answered on 27 Jul 2011, 03:46 PM
Well that was a lot of reading, for this gem:
"One possible workaround, which in our opinion will serve you good, is to preserve the property you want in a variable in the PreviewEditStarted event. Then, in the PreviewEdited event you can decide whether you want to rollback to that property value or not."
So that's that then. Not much I can do other than maintain the state myself?
"One possible workaround, which in our opinion will serve you good, is to preserve the property you want in a variable in the PreviewEditStarted event. Then, in the PreviewEdited event you can decide whether you want to rollback to that property value or not."
So that's that then. Not much I can do other than maintain the state myself?
0
Hi Jerome,
Yep. Currently that's the one possible solution to this particular case.
Greetings,
Kiril Stanoev
the Telerik team
Yep. Currently that's the one possible solution to this particular case.
Greetings,
Kiril Stanoev
the Telerik team
Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!