I migrated my application to v2011 vol 2 and it seems the auto refresh doesn't work anymore, whereas the exact same code in vol 1 did (and still does).
This is the code I'm using:
public partial class ViewControllerAutoRefresh : ViewController
{
public ViewControllerAutoRefresh()
{
InitializeComponent();
RegisterActions(components);
}
protected override void OnActivated()
{
base.OnActivated();
if (View is ListView && View.IsRoot)
Frame.TemplateChanged += Frame_TemplateChanged;
}
void Frame_TemplateChanged(object sender, EventArgs e)
{
if (Frame.Template == null)
return;
var page = (Page)Frame.Template;
page.Init += page_Init;
}
void page_Init(object sender, EventArgs e)
{
if (View is ListView)
{
try
{
const string timerID = "RefreshTimer";
var page = sender as Page;
if (page.FindControl(timerID) != null)
return;
var timer = new ASPxTimer
{
ID = timerID,
Interval = 10000,
Enabled = true
};
timer.Tick += timer_Tick;
if (page.Form != null)
page.Form.Controls.Add(timer);
}
catch
{
}
}
}
void timer_Tick(object sender, EventArgs e)
{
if (View is ListView)
((ListView)View).CollectionSource.ObjectSpace.Refresh();
}
}
We have closed this ticket because another page addresses its subject:
How to automatically refresh data in a View after a certain period of time on the WebHow to implement auto refresh functionality on the Web
Answers approved by DevExpress Support
Hi Ad,
To accomplish this task, I suggest that you try the following controller:
C#public class MyController : ViewController<ListView> {
const int Interval = 10000;
private void grid_Load(object sender, EventArgs e) {
ASPxGridView grid = (ASPxGridView)sender;
grid.Load -= grid_Load;
grid.ClientSideEvents.Init = string.Format("function(s,e){{ {0} }}", GetRefreshOnTimerFunctionBody(grid.ClientID, Interval));
}
private string GetRefreshOnTimerFunctionBody(string gridId, int interval) {
return string.Format("if(!window.{0}_timerIsSet) {{window.{0}_timerIsSet=true; window.setTimeout(function(){{ window.{0}_timerIsSet = false; if({0}) {{ {0}.PerformCallback('MyCallback');}} }}, {1});}}", gridId, Interval);
}
private void grid_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e) {
if(e.Parameters == "MyCallback") {
View.ObjectSpace.Refresh();
ASPxGridView grid = (ASPxGridView)sender;
grid.JSProperties["cpEndCallbackHandlerRefreshOnTimer"] = GetRefreshOnTimerFunctionBody(grid.ClientID, Interval);
grid.JSProperties[ASPxGridListEditor.EndCallbackHandlers] = "cpEndCallbackHandlerRefreshOnTimer;" + grid.JSProperties[ASPxGridListEditor.EndCallbackHandlers];
}
}
protected override void OnViewControlsCreated() {
base.OnViewControlsCreated();
ASPxGridListEditor editor = View.Editor as ASPxGridListEditor;
if(editor != null) {
ASPxGridView grid = editor.Grid;
grid.CustomCallback += grid_CustomCallback;
grid.Load += grid_Load;
}
}
}
Hope this will help you.
Thanks,
Konstantin M
UPDATE:
This solution may cause the popup window to be unexpectedly closed under certain circumstances. Refer to the Auto-Refresh ListView without closing current popup thread to learn more on how to avoid this.
In some cases it's possible that the user is dealing with an opened popup window. The above code causes this popup window to be closed. How can I check if there is any open popup window, ignore the refresh or refresh without closing the open popup windows (if any)?
@Sina: We discussed how to solve this problem in the Auto-Refresh ListView without closing current popup ticket. I hope this helps. Otherwise, please create a separate ticket in the Support Center and attach your current implementation so we can assist you further.
Hi Ad de Rijke,
Thank you for contacting us.
I have reproduced the problem. XAF uses callback mechanism since version 11.2 of the suite, so adding the control directly to a page during a callback will not work as expected. I suggest that you add an extra XafUpdatePanel to a template (Default.aspx) manually and add the timer to this panel:
<cc3:XafUpdatePanel ID="XafUpdatePanel1" runat="server"/>
protected override void OnActivated() { base.OnActivated(); if(View is ListView && View.IsRoot) { Page page = (Page)Frame.Template; try { const string timerID = "RefreshTimer"; if(page.FindControl(timerID) != null) { return; } ASPxTimer timer = new ASPxTimer(); timer.ID = timerID; timer.Interval = 10000; timer.Enabled = true; timer.Tick += timer_Tick; ((IMyCustomPanelHolder)page).MyXafUpdatePanel.Controls.Add(timer); } catch { } } } void timer_Tick(object sender, EventArgs e) { if(View is ListView) ((ListView)View).CollectionSource.ObjectSpace.Refresh(); }
Let me know if I can help you further.
See Also:
eXpressApp Framework 11.2 ASP.NET Application Migration Guidelines
Thanks,
Konstantin M
Hi Constantin M,
Thanks, this explains why it doesn't work anymore.
It's just not clear to me what to do with the ((IMyCustomPanelHolder)page).MyXafUpdatePanel.Controls.Add(timer);
Could you drop me al line?
Thanks. Ad
Hi Ad,
IMyCustomPanelHolder is an interface that looks like this:
public interface MyCustomPanelHolder { XafUpdatePanel MyXafUpdatePanel { get; } }
I have introduced it just to easily access the update panel in your controller. This interface (or any other interface you want) should be implemented by the template (e.g., Default.aspx).
Hope this will help you.
Thanks,
Konstantin M
Hi Constantin,
Thanks. Conceptually, I get the point. But how to implement this, I have absolutely no clue.
Isn't there a working example available?
Thanks, Ad.
Hi Ad,
Please review the attached project. Let me know if this helps.
Thanks,
Konstantin M
Hi Konstantin,
Thanks… Yes, this solution performs a refresh. However, it is still not clear to me how to cope with this issue.
In the sample attached, the PagePreRender event is trapped, resulting in rendering the complete page (the timer_Tick event on the other hand is never fired).
Basically, what I'm trying to achieve is the same behavior as when clicking the Refresh button (updating the GridView).
The question is, how can I force the GridView (or more precisely, the panel containing teh GridView) to refresh on a timely basis???
Thanks, Ad.
Hi Ad,
Thank you for the feedback. I should apologize for the inconvenience because my previous project was not fully functional.
Please modify my previous controller as follows:
public class MyViewController : ViewController<ListView> { protected override void OnFrameAssigned() { base.OnFrameAssigned(); if(Frame != null) { if(Frame.Template != null) { ((Page)Frame.Template).Load += new EventHandler(MyViewController_Load); } else { Frame.TemplateChanged += new EventHandler(Frame_TemplateChanged); } } } void Frame_TemplateChanged(object sender, EventArgs e) { if(Frame.Template != null) { ((Page)Frame.Template).Load += new EventHandler(MyViewController_Load); } } protected override void OnActivated() { base.OnActivated(); timer.Enabled = true; } protected override void OnDeactivated() { timer.Enabled = false; base.OnDeactivated(); } ASPxTimer timer; void MyViewController_Load(object sender, EventArgs e) { IMyCustomUpdatePanelHolder holder = Frame.Template as IMyCustomUpdatePanelHolder; if(holder != null) { timer = new ASPxTimer(); timer.Interval = 1000; timer.ID = "timer"; timer.Enabled = false; timer.Tick += new EventHandler(timer_Tick); holder.UpdataPanel.Controls.Add(timer); } } private void timer_Tick(object sender, EventArgs e) { } public MyViewController() : base() { TargetViewNesting = Nesting.Root; } }
It seems to work fine in my tests. I look forward to hearing from you.
Thanks,
Konstantin M.
Hi Konstantin,
Based on your example I tried to make it work with the XafCallbackManager so the whole page wouldn’t reload, but just the gridview and actions (via ajax). I got this all to work using the following code.
public partial class MyViewController : ViewController<ListView>, IXafCallbackHandler { ASPxTimer _timer; XafCallbackManager _callbackManager; public MyViewController() : base() { TargetViewNesting = Nesting.Root; TargetViewType = ViewType.ListView; } protected override void OnFrameAssigned() { base.OnFrameAssigned(); if (Frame != null) { if (Frame.Template != null) ((Page)Frame.Template).Load += new EventHandler(MyViewController_Load); else Frame.TemplateChanged += new EventHandler(Frame_TemplateChanged); } } void Frame_TemplateChanged(object sender, EventArgs e) { if (Frame.Template != null) ((Page)Frame.Template).Load += new EventHandler(MyViewController_Load); } protected override void OnActivated() { base.OnActivated(); if (View is ListView && View.IsRoot) { _callbackManager = GetCallbackManager(); _callbackManager.RegisterHandler("callbackHandler", this); IMyCustomUpdatePanelHolder holder = Frame.Template as IMyCustomUpdatePanelHolder; if (holder != null) { _timer = new ASPxTimer(); _timer.Interval = 1000; _timer.ID = "timer"; _timer.Enabled = true; _timer.ClientSideEvents.Tick = "function(f, e) { " + _callbackManager.GetScript("callbackHandler", "") + "}"; holder.UpdataPanel.Controls.Add(_timer); } } } protected override void OnDeactivated() { if (_timer != null) _timer.Enabled = false; base.OnDeactivated(); } void MyViewController_Load(object sender, EventArgs e) { _callbackManager = GetCallbackManager(); _callbackManager.RegisterHandler("callbackHandler", this); } public void ProcessAction(string parameter) { if (View is ListView) { ((ListView)View).CollectionSource.Reload(); ObjectSpace.Refresh(); } } private XafCallbackManager GetCallbackManager() { XafCallbackManager manager = null; if (Frame != null && Frame.Template != null) { ICallbackManagerHolder holder = Frame.Template as ICallbackManagerHolder; if (holder != null) { manager = holder.CallbackManager; } } return manager; } }
While this works when entering the ListView page, when I navigate to a detailView page, after I visited the ListView page the refresh ASPxTimer control is still executing. Can tell me how I can disable the ASPxTimer when I leave the page, or is this approach completely off ??
Thanks