I have created a custom data row and a custom data table class by extending both MyDataRow and MyDataTable classes. MyDataRow and MyDataTable are defined in my data set file (.xsd). I.e., I'm using a real database. MyDataTable has a few fields (Id, Status, DateCreated, etc...).
In XAML, I have set RadGridView control's ItemsSource property to a DataView object (bound to ViewModel's property). In short, new MyDataTable().DefaultView. Naturally it's been filled with data from the database before getting the DefaultView.
The reason why I created the custom classes was that I want to add additional property (IsReadOnly) to be used together with IsReadOnlyBinding and I don't want to change the database or the data set file structure.
This is how I have created the custom classes (the second answer): https://stackoverflow.com/questions/3101412/how-to-extend-datarow-and-datatable-in-c-sharp-with-additional-properties-and-me
When I set grid's IsReadOnlyBinding="{Binding IsReadOnly}", an exception is thrown when try to add a new row or edit an existing one. The exception says: Property with specified name: IsReadOnly cannot be found on component: System.Data.DataRowView
However, if I set IsReadOnlyBinding="{Binding Status}", it works. Status field already exist in my database table.
Is the grid using internally the data set schema (.xsd) or what might cause this? Any ideas how to solve this?
I am trying to accomplish functionality where rows cannot be edited until I click a modify button (which sets IsReadOnly to false for the selected row) but I still want to add new rows using the grid's new row button.
I wish I could attach actual code but there's a lot of it and it's not easy to copy-paste it here. I could try to put together a test project if my description is not clear enough.
7 Answers, 1 is accepted
Here are the extended classes just in case:
public class ExtendedTable : MyDataSet.MyDataTable
{
public ExtendedTable() : base()
{
}
public ExtendedTable(DataTable table) : base(table)
{
}
public ExtendedTable(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
protected override Type GetRowType()
{
return typeof(ExtendedRow);
}
protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
{
return new ExtendedRow(builder);
}
}
[Serializable]
public class ExtendedRow : MyDataSet.MyRow, INotifyPropertyChanged
{
private bool isReadOnly = false;
public event PropertyChangedEventHandler PropertyChanged = null;
public ExtendedRow() : base(null)
{
}
public ExtendedRow(DataRowBuilder builder) : base(builder)
{
}
public bool IsReadOnly
{
get
{
return this.isReadOnly;
}
set
{
if (this.isReadOnly != value)
{
this.isReadOnly = value;
this.NotifyPropertyChanged("IsReadOnly");
}
}
}
private void NotifyPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The exception occurs because the DefaultView of DataTable works with DataRowView items instead of the DataRows. Since the DataRowView doesn't have a IsReadOnly property, the reported exception is thrown. Actually, in the latest version of UI for WPF the exception is not thrown but silently caught. However, the binding still won't work.
I can suggest you two approaches to achieve your requirement.
- Instead of using the DataTable class, you can create viewmodel wrappers and bind the RadGridView control to a collection of the wrappers. For example:
public
class
TableRowWrapper
{
public
string
Name {
get
;
set
; }
public
DateTime DateOfBirth {
get
;
set
; }
public
bool
IsReadOnly {
get
;
set
; }
// etc.
}
- Alternatively, instead of using the IsReadOnlyBinding you can subscribe to the BeginingEdit event of RadGridView and if the ExtendedRow IsReadOnly property is set to True, cancel the editing. Here is an example in code:
private
void
RadGridView_BeginningEdit(
object
sender, GridViewBeginningEditRoutedEventArgs e)
{
var dataRowView = e.Row.Item
as
DataRowView;
if
(dataRowView !=
null
)
{
var extendedRow = (ExtendedRow)dataRowView.Row;
e.Cancel = extendedRow.IsReadOnly;
}
}
Regards,
Martin Ivanov
Progress Telerik
I went with the first approach, row wrapper, and added an additional property IsReadOnly.
However, I'm still unable to make the column show a modifiable text box with a click of a button. My button's click event handler invokes the BeginEdit method (which seem to return false all the time). After clicking the button, if I click the grid view row, it then shows the modifiable text box.
If I remove ReadOnlyBinding property setting from XAML, the text box is shown when I click the button.
You can test this with your demo project: Read Only Binding in GridView's examples in your SDK Samples Browser demo application. Just add new button to the current UI layout.
Here's the XAML code for the bottom grid view:
<
telerik:RadGridView
x:Name
=
"grid"
Grid.Row
=
"4"
ItemsSource
=
"{Binding Clubs}"
AutoGenerateColumns
=
"False"
ColumnWidth
=
"*"
IsReadOnlyBinding
=
"{Binding IsNameReadOnly}"
SelectedItem
=
"{Binding SelectedItem}"
>
<
telerik:RadGridView.Columns
>
<
telerik:GridViewDataColumn
DataMemberBinding
=
"{Binding Name}"
/>
<!--<
telerik:GridViewDataColumn
DataMemberBinding
=
"{Binding Established}"
Header
=
"Est."
DataFormatString
=
"{}{0:yyyy}"
/>
<
telerik:GridViewDataColumn
DataMemberBinding
=
"{Binding StadiumCapacity}"
Header
=
"Stadium"
/>-->
</
telerik:RadGridView.Columns
>
</
telerik:RadGridView
>
The last two columns are intentionally disabled because I have only one column in my application.
And here's the button click event handler:
private
void
Button_Click(
object
sender, RoutedEventArgs e)
{
((
this
.DataContext
as
MyViewModel).SelectedItem
as
Club).IsNameReadOnly =
false
;
Boolean b = grid.BeginEdit();
}
IsNameReadOnly would be set back to true when the new row is validated (i.e., it's again read-only until the next button click), but it's not important at the moment.
As a side not, I'm using MVVM so I prefer not to use events. On the other hand, I have already created a new class that inherits from your grid view class, to allow me to use row validation events, so I guess events are okay...
In MainWindows constructor:
this
.grid.RowEditEnded += Grid_RowEditEnded;
Here the event handler code:
private
void
Grid_RowEditEnded(
object
sender, Telerik.Windows.Controls.GridViewRowEditEndedEventArgs e)
{
Club c = (
this
.DataContext
as
MyViewModel).SelectedItem
as
Club;
c.IsNameReadOnly =
true
;
}
Looks like I got it working like this (not sure if this is a recommended way to handle this, though):
private
void
Button_Click(
object
sender, RoutedEventArgs e)
{
Club c = (
this
.DataContext
as
MyViewModel).SelectedItem
as
Club;
c.IsNameReadOnly =
false
;
System.Windows.Data.Binding binding = grid.IsReadOnlyBinding;
grid.IsReadOnlyBinding =
null
;
Boolean b = grid.BeginEdit();
grid.IsReadOnlyBinding = binding;
}
Well, at least this whole thing for you information, if this is a bug in the grid control.
Let me know your thoughts.
I am glad to hear you managed to achieve your requirement.
As for the reported behavior, I am not sure why this happens, but I will guess that the edit mode is set up to start on a single click which triggers the begin edit method when you click on the button in the row.
Regards,
Martin Ivanov
Progress Telerik