I am trying to develop a application using GridView .In my application:
1. When page loads i am grouping based on a column name Notes.
2. Getting data in form of datatable.(Using Silverlight DataTable code provided by telerik).
3. Getting Datatable from 2 webservice.After fetching data from 1st webervice binding data to grid.
4. After that invoking 2nd webservice.After getting data from 2nd service adding datarow's from 2nd datatable to gridview's itemsource.
In 2nd datatable 4k-5k records are there(All disinct for group by column) .I am copying data in for loop.It is not coming to the end of the for loop and applicaion is hanging.Many times IE also crashed.
I tried with Defer refresh also but after calling defeerrefresh even i call refresh also it is not updating with new rows.
Kindly suggest.
Thanks,
Rajesh
14 Answers, 1 is accepted
Can you clarify a bit on "am copying data in for loop.It is not coming to the end of the for loop and applicaion is hanging."?
Kind regards,Vlad
the Telerik team
I am copying data from 2nd data data table to the 1st data table which is item source of gridview.I am looping through each dararow from 2nd datatable and creating a new row in first datatable (coying columnvalue from 2nd datatable's row to newly created row).If before the loop i am calling delayrefresh method in that case control is exiting from loop and UI is coming up but groups corresponding to newly added datarow's are not showing on UI.(Calling refresh method after loop exits).But if i am not calling delay refresh method UI is not reponsive at all.If i put breakpoint i can seee that even it is not exiting from the for loop and control never comes out of for loop.
Let me know if more details are required.
Thanks,
Rajesh
.
Can you send us small (runnable) example project via support ticket where we can check exactly your scenario and reproduce the problem?
Greetings,Vlad
the Telerik team
Dear Vlad,
I have pasted code snippet below.This is a prototype and in my case i have many more columns.(Here there are only three).
You can see in timer_Tick method i have a for loop.In this loop it takes long time.For loop took around 8 minutes in last run which i did locally.I am going to log a support ticket as well but i think using this snippet you will get some idea what i am trying to do.
Thanks,
Rajesh
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
RadGridView1.ItemsSource = GetDataTable1();
RadGridView1.GroupDescriptors.Add(new GroupDescriptor() { Member = "Name" });
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = new TimeSpan(0, 0, 25);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
(sender as DispatcherTimer).Stop();
(sender as DispatcherTimer).Tick -= timer_Tick;
DataTable dtable = RadGridView1.ItemsSource as DataTable;
//change 5000 to 6000 or 7000
//Put breakpoint before start of this for loop and after end of this for loop
for (int i = 21; i < 5000; i++)
{
DataRow dr = dtable.NewRow();
dr["ID"] = i;
dr["Name"] = "TestName" + i.ToString();
dr["Date"] = DateTime.Today;
dtable.Rows.Add(dr);
}
}
private DataTable GetDataTable1()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn() { ColumnName = "ID", DataType = typeof(int) });
table.Columns.Add(new DataColumn() { ColumnName = "Name", DataType = typeof(string) });
table.Columns.Add(new DataColumn() { ColumnName = "Date", DataType = typeof(DateTime) });
for (int i = 0; i < 20; i++)
{
DataRow dr = table.NewRow();
dr["ID"]= i;
dr["Name"] = "TestName" + i.ToString();
dr["Date"] = DateTime.Today;
table.Rows.Add(dr);
}
return table;
}
}
<
Grid
x:Name
=
"LayoutRoot"
Background
=
"White"
Margin
=
"10"
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"Auto"
/>
<
RowDefinition
/>
</
Grid.RowDefinitions
>
<
telerik:RadGridView
x:Name
=
"RadGridView1"
AutoExpandGroups
=
"True"
Grid.Row
=
"1"
/>
</
Grid
>
I was trying to upload the source code but somehow was not able to locate the URI from where i can open support ticket.
If the snippet which i have provided is not enough to reproduce the scenario in that case Kindly let me know procedure of opening a new support ticket.
Thanks,
Rajesh
Was you able to reproduce the issue which i have mentioned in previous mails.
Can you please give me some suggestion of fixing this.
Thanks,
Rajesh
I've checked the code and I've found what is causing this issue. RadGridView will try to respond to every CollectionChanged event raised from the DataTable in order to refresh the UI - here is an example how to avoid this:
void timer_Tick(object sender, EventArgs e)
{
(sender as DispatcherTimer).Stop();
(sender as DispatcherTimer).Tick -= timer_Tick;
DataTable dtable = RadGridView1.ItemsSource as DataTable;
RadGridView1.ItemsSource = null;
//change 5000 to 6000 or 7000
//Put breakpoint before start of this for loop and after end of this for loop
for (int i = 21; i < 5000; i++)
{
DataRow dr = dtable.NewRow();
dr["ID"] = i;
dr["Name"] = "TestName" + i.ToString();
dr["Date"] = DateTime.Today;
dtable.Rows.Add(dr);
}
RadGridView1.ItemsSource = dtable;
RadGridView1.GroupDescriptors.Add(new GroupDescriptor() { Member = "Name" });
}
Vlad
the Telerik team
Thanks for your response.I am also using same kind of code which you have suggested with few changes.
Approach 1 (suggested by you)# You are setting itemsource to null the basic problem is that you will have to apply group by again because after setting null as itemsource group by columns will not be available.
Approach 2(My approach) # In my case I am creating a clone of datatable and adding a dummy item and then applying that dummy item as itemsource.So in my case i don't have to add all group by columns again to maintain group by state .
Other problem with both these approaches is that it will not maintain the state if you have groups in expanded state because datasource is changing
For e.g take a scenario in which
1.whenever I am dragging and dropping new columns based on which i want to apply group by i am adding new items to datatable(similar code as timer_Tick).
2.User has dragged and dropped 2 group by columns and few of items are in expanded state.
3.User is dragging 3rd column to perform group by based on that column.You can see that state will not be maintained because the underlying datasource we have changed.
The other problem is when i try to add 2-3 new rows after having 2000 distinct groups it takes really long time.
Can you suggest me some approach where i don't have to change the datasource without compromising on performance.I have seen many posts from you and due to that i am hopeful you can suggest me some approach using which i can overcome this issue.
Thanks,
Rajesh
The main problem comes from the fact that RadGridView is grouped. When you add an item in the source collection the grid will re-group in order to position the item in the proper group - this operation is expensive. When you do this 5000 times in a loop actually you will force RadGridView to re-group 5000 times and because of this you have such performance problem.
The other possible approach will be to use RadObservableCollection<> in the DataTable instead ObservableCollection<>. Here is an example how to change the code:
private
void
CreateInternalView()
{
this
.internalView = (IList)Activator.CreateInstance(
typeof
(RadObservableCollection<>).MakeGenericType(
this
.ElementType));
((INotifyCollectionChanged)internalView).CollectionChanged += (s, e) => {
this
.OnCollectionChanged(e); };
}
public
void
SuspendNotifications()
{
((ISuspendNotifications)
this
.InternalView).SuspendNotifications();
}
public
void
ResumeNotifications()
{
((ISuspendNotifications)
this
.InternalView).ResumeNotifications();
}
Here is also how to use these methods:
void
timer_Tick(
object
sender, EventArgs e)
{
(sender
as
DispatcherTimer).Stop();
(sender
as
DispatcherTimer).Tick -= timer_Tick;
DataTable dtable = RadGridView1.ItemsSource
as
DataTable;
dtable.SuspendNotifications();
for
(
int
i = 21; i < 5000; i++)
{
DataRow dr = dtable.NewRow();
dr[
"ID"
] = i;
dr[
"Name"
] =
"TestName"
+ i.ToString();
dr[
"Date"
] = DateTime.Today;
dtable.Rows.Add(dr);
}
dtable.ResumeNotifications();
}
Vlad
the Telerik team
Thanks Vlad.I will look into this and will check whether there is any issue which i get due to this approach.I just want to ask you regarding one approach which i am currently using.Let me know if this is also ok without changing datatable.
void
timer_Tick(
object
sender, EventArgs e)
{
(sender
as
DispatcherTimer).Stop();
(sender
as
DispatcherTimer).Tick -= timer_Tick;
DataTable dtable = RadGridView1.ItemsSource
as
DataTable;
//Defer the refresh cycle
{
for
(
int
i = 21; i < 5000; i++)
{
DataRow dr = dtable.NewRow();
dr[
"ID"
] = i;
dr[
"Name"
] =
"TestName"
+ i.ToString();
dr[
"Date"
] = DateTime.Today;
dtable.Rows.Add(dr);
}
//A dummy row to refresh and update all rows.after deferred update
DataRow dr = dtable.NewRow();
dtable.Rows.Add(dr);
dtable.Rows.Remove(dr);
}
Please let me know if this approach also i can use without any issue.
Here i am deferring refresh and adding all rows in deferred cycle.After that i am adding and removing a dummy row to bring grid control to normal state.
Dear Vlad,
ISuspendNotification interface is in which namespace or assembly.I am using 2010 Q2 release and i am not able to get that interface.
Thanks,
Rajesh
This interface is available in our Q3 2010 release and above. Generally you do not need to add and remove the dummy row - to achieve the same you can call Rebind(). For example:
(sender
as
DispatcherTimer).Stop();
(sender
as
DispatcherTimer).Tick -= timer_Tick;
DataTable dtable = RadGridView1.ItemsSource
as
DataTable;
using
(RadGridView1.DeferRefresh())
{
for
(
int
i = 21; i < 5000; i++)
{
DataRow dr = dtable.NewRow();
dr[
"ID"
] = i;
dr[
"Name"
] =
"TestName"
+ i.ToString();
dr[
"Date"
] = DateTime.Today;
dtable.Rows.Add(dr);
}
}
RadGridView1.Rebind();
Vlad
the Telerik team
Actually i have tried with Rebind method but somehow it is now working in my case.Problem is that if i have multiple level grouping applied in that case Rebind method call collapses groups of lower level.
Rajesh
The problem suprise me, but solution was simple.
RadObservableCollection<
string
> stringy;
....
stringy.SuspendNotifications();
stringy.Clear();
stringy.AddRange(wsCall.Result.Body.@return); //@return is ObservableCollection<>
stringy.ResumeNotifications();
It will be nice if you provide "using()" member.
RadObservableCollection<
string
> stringy;
....
using (stringy.DeferNotifications()) {
stringy.Clear();
stringy.AddRange(wsCall.Result.Body.@return); //@return is ObservableCollection<>
}
And another point .. when ending "using()" or call "ResumeNotifications()" one all-in-one notification should be fired.
We typically dont want stop all notifications, rather we want one bulk notification.