Hello,
How can I remember and restore IsExpanded state in bound self-referencing hierarchy grid when source collection (BindingList<T>) is updated?
Basically I see 2 solutions, remember the rows state before update and restore it after, or make IsExpanded property in bound object and update it accordingly.
The problem with first approach is that I don't know, which events I should handle to remember and restore IsExpanded state for all updates.
For the second approach I was able to handle RowFormatting event and set initial expanded state for the row. But then I was not able to find a correct event to update IsExpanded value in corresponding object when row is expanded or collapsed by user.
Could someone help?
5 Answers, 1 is accepted
Thank you for writing.
I have prepared a sample code snippet demonstrating that when updating a property in the DataBoundItem doesn't collapse the expanded rows:
BindingList<Student> collectionOfStudents =
new
BindingList<Student>();
BindingList<School> collectionOfSchools =
new
BindingList<School>();
public
Form1()
{
InitializeComponent();
collectionOfStudents.Add(
new
Student(1,
"Peter"
,
"D-"
,2));
collectionOfStudents.Add(
new
Student(2,
"Antony"
,
"B+"
,1));
collectionOfStudents.Add(
new
Student(3,
"David"
,
"A-"
,1));
collectionOfStudents.Add(
new
Student(4,
"John"
,
"D-"
,2));
collectionOfSchools.Add(
new
School(
"Schrool 1"
,1));
collectionOfSchools.Add(
new
School(
"Schrool 2"
,2));
radGridView1.DataSource = collectionOfSchools;
radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
GridViewTemplate template =
new
GridViewTemplate();
template.DataSource = collectionOfStudents;
template.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
radGridView1.MasterTemplate.Templates.Add(template);
GridViewRelation relation =
new
GridViewRelation(radGridView1.MasterTemplate);
relation.ChildTemplate = template;
relation.RelationName =
"SchoolsStudents"
;
relation.ParentColumnNames.Add(
"Id"
);
relation.ChildColumnNames.Add(
"SchoolId"
);
radGridView1.Relations.Add(relation);
}
public
class
School: System.ComponentModel.INotifyPropertyChanged
{
string
_name;
int
_id;
public
School(
string
name,
int
id)
{
this
._name = name;
this
._id = id;
}
public
string
Name
{
get
{
return
this
._name;
}
set
{
this
._name = value;
OnPropertyChanged(
"Name"
);
}
}
public
int
Id
{
get
{
return
this
._id;
}
set
{
this
._id = value;
OnPropertyChanged(
"Id"
);
}
}
public
event
PropertyChangedEventHandler PropertyChanged;
protected
virtual
void
OnPropertyChanged(
string
propertyName)
{
if
(PropertyChanged !=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
}
public
class
Student : System.ComponentModel.INotifyPropertyChanged
{
int
m_id;
string
m_name;
string
m_grade;
int
school_id;
public
event
PropertyChangedEventHandler PropertyChanged;
public
Student(
int
m_id,
string
m_name,
string
m_grade,
int
s_id)
{
this
.m_id = m_id;
this
.m_name = m_name;
this
.m_grade = m_grade;
this
.school_id = s_id;
}
public
int
Id
{
get
{
return
m_id;
}
set
{
if
(
this
.m_id != value)
{
this
.m_id = value;
OnPropertyChanged(
"Id"
);
}
}
}
public
string
Name
{
get
{
return
m_name;
}
set
{
if
(
this
.m_name != value)
{
this
.m_name = value;
OnPropertyChanged(
"Name"
);
}
}
}
public
string
Grade
{
get
{
return
m_grade;
}
set
{
if
(
this
.m_grade != value)
{
this
.m_grade = value;
OnPropertyChanged(
"Grade"
);
}
}
}
public
int
SchoolId
{
get
{
return
this
.school_id;
}
set
{
this
.school_id = value;
OnPropertyChanged(
"SchoolId"
);
}
}
protected
virtual
void
OnPropertyChanged(
string
propertyName)
{
if
(PropertyChanged !=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
}
private
void
radButton1_Click(
object
sender, EventArgs e)
{
collectionOfStudents[1].Name =
"Name"
+ DateTime.Now.ToLongTimeString();
}
The following help article demonstrates how to reflect custom object changes in RadGridView: http://docs.telerik.com/devtools/winforms/gridview/populating-with-data/reflecting-custom-object-changes-in-rgv
However, if you reset the DataSource property of RadGridView, before setting the keep in an appropriate data structure which the expanded rows and restore them after that.
I hope this information helps. Should you have further questions I would be glad to help.
Regards,
Dess
Telerik by Progress
Hello Dess,
That I need to achieve is something like a binding of IsExpanded row's property to a similar business property of the bound row object. I.e. when user expand or collepse a row from the grid, corresponding IsExpanded property is changed in business object. And vice versa, if business property IsExpanded is changed row should reflect this change. In this case I don't need to bother of collection updates, because each row will have the state of its underlying item.
So far I have a handler of RowFormatting where I set row's IsExpanded property to corresponding value. But which event is fired when user expand or collapse a row in the grid? I was not able to find it, so I don't know how to update business object's property when UI is updated by user. Also it is not clear how make UI to update in case business onject's IsExpanded property is changed in code.
I am not strong in WinForms, I worked with Telerik Silverlight controls before, where this "problem" is not a problem at all. So I just don't know if there a way in WinForms to really bind row's IsExpanded to business object IsExpanded.
Thank you for writing back.
You can use the RadGridView.ChildViewExpanded event to handle when the user expands or collapses a row and update the DataBoundItem. As to the question about updating the GridViewDataRowInfo. property when the DataBoundItem is updated, you can subscribe to the BindingList<T>.ListChanged event and manage the expanded state accordingly:
collectionOfSchools.ListChanged+=collectionOfSchools_ListChanged;
private
void
collectionOfSchools_ListChanged(
object
sender, ListChangedEventArgs e)
{
if
(e.ListChangedType == ListChangedType.ItemChanged)
{
this
.radGridView1.Rows[e.NewIndex].IsExpanded = collectionOfSchools[e.NewIndex].IsActive;
}
}
I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Telerik by Progress
Thanks a lot, Dess, this is really help.
But is this really that simple to find a matching row by index from BindingList? Even if row is out of view because of scroll position or because some of its parent is collapsed (I have self-referencing hierarchy grid)?
Thank you for writing back.
The provided code snippet just illustrates a sample approach and it may not cover all possible cases. When using self-referencing hierarchy, you should find the corresponding record in the BindingList considering a unique field in GridViewDataRowInfo, e.g. "Id". Thus, by the NewIndex, you can get the grid row and extract the necessary unique information by which you can traverse the DataSource collection and update the relevant field accordingly.
I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Telerik by Progress