I've been searching for a couple of days now and am running into an issue no matter what I've tried. The problem is that I seem to have come across with the perfect storm and I can't get all 3 things working at the same time.
Pagination
Optional Parameters
Parameter Dialog Prompt
So this first method is what I've been using and everything works except it won't Navigate past past 2 (And I've very aware of why navigation doesn't work)
// ##################################################################################################################################################
// METHOD 1: Everything works correctly except you can't go past page 2
protected void Page_Load(object sender, EventArgs e)
{
CrystalReportViewer1.ReportSource = Session["myReportDoc"] as CrystalDecisions.CrystalReports.Engine.ReportDocument;
if (CrystalReportViewer1.ReportSource == null)
{
//Generate the Report Document
Handlers.ReportHandler myReportHandler = new Handlers.ReportHandler();
CrystalDecisions.CrystalReports.Engine.ReportDocument myReportDocument = myReportHandler.GenerateReport("AlarmStatusReport");
Session["myReportDoc"] = myReportDocument; //This is were we save it off for next time
CrystalReportViewer1.ReportSource = myReportDocument;
}
}
So knowing that the common fix is to not use Page Load but use Page_Init instead. This fixes the Navigation... until I open a report that has optional parameters. With those, every time I try to navigate to the next page, instead of it working, the Parameter box re-appears and now requires at least 1 of my Optional Parameters to be filled out. (Each "next Page" reduces the prompt by 1 Optional). But, because I'm being forced to change the Parameters, it "refreshes" the report and I'm back on Page 1.
// ##################################################################################################################################################
// METHOD 2: Works, but not for any report that has Optional Parameters. They become "Required" and keep popping up instead of navigating to the next page
protected void Page_Init(object sender, EventArgs e)
{
CrystalReportViewer1.ReportSource = Session["myReportDoc"] as CrystalDecisions.CrystalReports.Engine.ReportDocument;
if (CrystalReportViewer1.ReportSource == null)
{
//Generate the Report Document
Handlers.ReportHandler myReportHandler = new Handlers.ReportHandler();
CrystalDecisions.CrystalReports.Engine.ReportDocument myReportDocument = myReportHandler.GenerateReport("AlarmStatusReport");
Session["myReportDoc"] = myReportDocument; //This is were we save it off for next time
CrystalReportViewer1.ReportSource = myReportDocument;
}
}
Now, I got real excited, because I got a bit clever and fixed both those issues, by trapping the Navigation and keeping track of the Page myself. EVERYTHING WORKS NOW!!! until I go to the Parameter Dialog prompt and it was totally jacked up.
// ##################################################################################################################################################
// METHOD 3: Everything works correctly except the Prompt Box doesn't Format correcly due to the addition of the added Event Handers
protected void Page_Load(object sender, EventArgs e)
{
CrystalReportViewer1.ReportSource = Session["myReportDoc"] as CrystalDecisions.CrystalReports.Engine.ReportDocument;
if (CrystalReportViewer1.ReportSource == null)
{
//Generate the Report Document
Handlers.ReportHandler myReportHandler = new Handlers.ReportHandler();
CrystalDecisions.CrystalReports.Engine.ReportDocument myReportDocument = myReportHandler.GenerateReport("AlarmStatusReport");
Session["myReportDoc"] = myReportDocument; //This is were we save it off for next time
CrystalReportViewer1.ReportSource = myReportDocument;
//Init our Manual Page Counter to 1
HiddenFieldPageNumber.Value = "1";
}
CrystalReportViewer1.Navigate += CrystalReportViewer1_Navigate; //Simply Adding this event, EVEN IF IT HAS NO CODE, Breaks the style and formating of the Parameter Prompt box.
CrystalReportViewer1.PreRender += CrystalReportViewer1_PreRender;
}
private void CrystalReportViewer1_Navigate(object source, CrystalDecisions.Web.NavigateEventArgs e)
{
//This prevents this event from Incrementing the Page again when the PreRender Event
//below re-sets which page to show.
if (_SkipPageIncrement == true)
{
return;
}
//Whenever the Navigation is used, this Event fires. Here is the problem, there is nothing that actually tells
//us if the user clicked on Previous or Next (or GotoPage for that Matter). So we have to do some guessing here
if (e.CurrentPageNumber == 1 && e.NewPageNumber == 2)
{
//If they pressed "NEXT" we will always get Current = 1 and New = 2 due to the Pagination starting over on the PostBack
//So we INCREMENT our real Page Number Value.
HiddenFieldPageNumber.Value = (Convert.ToInt32(HiddenFieldPageNumber.Value) + 1).ToString();
}
else if (e.CurrentPageNumber == 1 && e.NewPageNumber == 1)
{
//If they pressed "PREV" we will always get Current = 1 and New = 1 due to the Pagination starting over on the PostBack
//So we DECREMENT our real Page Number Value.
HiddenFieldPageNumber.Value = (Convert.ToInt32(HiddenFieldPageNumber.Value) - 1).ToString();
}
}
private void CrystalReportViewer1_PreRender(object sender, EventArgs e)
{
//The Viewer has a method that allows us to set the page number. This PreRender Event is the only
//Event I could find that works. It comes AFTER the Navigate, but before the reports is rendered.
_SkipPageIncrement = true; //The ShowNthPage re-triggers the Navigation, so this prevents it from running again.
CrystalReportViewer1.ShowNthPage(Convert.ToInt32(HiddenFieldPageNumber.Value));
}
As commented above, the moment I add the OnNavigation Event, even if I comment out all the actual code inside, my Prompt box goes from looking like this...
To this (my page as a dark background and you can see that now shows, plus the "OK" button is all jacked up.
I just don't get why trapping the Navigation Event breaks the Prompt box even when the event is not firing (on that first load).
Side note: I'm using VS 2019 with CR 13.0.3500.0
So thanks to the help of a teammate that is more adept on CSS as I am, I have resolved the issue "good enough". So for anyone who wants to use the LOAD event, (Or has to like me), but then loses the ability to use the navigation and wants to use my method, the band-aid for the Crystal Reports Parameter prompt is to simply override their Styling in you Site.css with this...
/*---------------------- Custom CSS for Report Prompt Buttons ----------------------*/
.pePromptButton {
padding-bottom:4.3px;
}
td.pePromptButton {
display: inherit;
}
img {
vertical-align:top;
}
Related
Edit (more brief):
I have a control "configuration" with a tree view on the left and a list of panels on the right to which i add my controls. These elements are placed inside a table and surrounded by an ajax panel.
Let's say i have a tree node cars with two elements car1 and car2. When i click an item i save the ID and Type of the selected tree node inside hidden fields to be able to load the right user control on Page_Init
protected void Page_Init(object sender, EventArgs e)
{
var type = this.Request.Form["ctl00$MainContent$ctl00$typeId"];
var id = this.Request.Form["ctl00$MainContent$ctl00$entityId"];
if (!string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(id))
{
this.LoadUserControl(type, id);
}
}
private void LoadUserControl(string type, string id)
{
if (Convert.ToInt32(type) != 0)
{
switch (Convert.ToInt32(type))
{
case (int)SkTopics.Product:
this.AddUserControl("../Modules/Product.ascx", this.Product, type, id);
break;
}
}
}
private void AddUserControl(string controlName, Panel panel, string type, string id)
{
// Init new user cotntrol
control = (BaseControl)this.LoadControl(controlName);
control.LoadEntityItem(Convert.ToInt32(id));
// Setting the user control ID
var replace = controlName.Replace("../Modules/", string.Empty);
string userControlId = replace.Split('.')[0] + id;
var targetControl = panel.FindControl(userControlId);
if (object.Equals(targetControl, null))
{
control.ID = userControlId;
control.DataBind();
panel.Controls.Add(control);
}
}
On LoadEntityItem the data for this car are collected from the database and the values are set as Text of textboxes. When i now switch to the second car, the new values are displayed.
Problem: I change the values inside the textboxes and press the save button to call my save method of the user control. From now on, when i switch to the car 1 again, the values for car 2 are still displayed, although the data for car1 are filled. tO see the data for car1 i have to switch to a complete different type of control and then back to the car1.
Another problem is, that i have a binary image inside the user control which Data i fill on init. When i swith to the the next car, where there i no image configured, i set the image data to null, but the image from the previous car i show. Onyl if i set a different image data the new image is displayed.
There is something else i noticed:
Inside the user control i call a javascript funtion but this one is only called on clicking the the save button. It seems that there is a different between the switch of the tree node and the save button click ?
ScriptManager.RegisterStartupScript(this, this.GetType(), "InitScrewPointsForDragging", "InitScrewPointsForDragging();", true);
I hope this was more brief than before.
It seems that the reason for the issue is some improper viewstate handling and after I disable the view state it is working correctly at my side. Here is the code that I updated to have it work at my side:
this.control.EnableViewState = false;
this.control.ID = userControlId;
Summary: When trying to hide a custom command button in ASPxGridView command column, it hides the buttons strangely, one of the buton texts appears in the filter row entry, and the button handlers stop working. The error message like "More controls with the DXCBtn_0_9_-1 were found. The FindControl requires unique identifiers of the controls" (loosely translated) appeares.
I am using DevExpress 14.2.3.0, the grid view is nested in another grid views and in ASPxRoundPanels.
Details: The command column contains the following custom buttons...
<dx:GridViewCommandColumn VisibleIndex="9">
<CustomButtons>
<dx:GridViewCommandColumnCustomButton ID="btnClose" Text="Close as done">
</dx:GridViewCommandColumnCustomButton>
<dx:GridViewCommandColumnCustomButton ID="btnReopen" Text="Reopen">
</dx:GridViewCommandColumnCustomButton>
</CustomButtons>
</dx:GridViewCommandColumn>
The buttons are displayed fine (as links) and the following code handles them nicely:
protected void gvMilestoneTasks_CustomButtonCallback(object sender, ASPxGridViewCustomButtonCallbackEventArgs e)
{
ASPxGridView grid = sender as ASPxGridView;
if (e.ButtonID == "btnClose")
{
int milestoneID = Convert.ToInt32(grid.GetRowValues(e.VisibleIndex, "ID"));
DbUtil.ExecuteNonQuery(String.Format("EXEC sp_milestone_tasks_close_open {0}, 0, N'{1}'",
milestoneID, Page.User.Identity.Name));
grid.DataBind();
} else if (e.ButtonID == "btnReopen")
{
int milestoneID = Convert.ToInt32(grid.GetRowValues(e.VisibleIndex, "ID"));
DbUtil.ExecuteNonQuery(String.Format("EXEC sp_milestone_tasks_close_open {0}, 1, N'{1}'",
milestoneID, Page.User.Identity.Name));
grid.DataBind();
}
}
(That is, dedicated SQL stored procedures with different arguments are called [notice the second argument if curious], and the grid.DataBind(); is used to refresh the content of the status column.)
I want to show only one of the buttons. When the row shows the open one, only the Close button should be displayed and active. When the row shows it was closed earlier, only the Reopen button should be visible and active.
I tried to handle visibility in the CustomButtonInitialize event handler (based on the status info -- when the closed is NULL in the database, then it is open; otherwise, the closed contains the datetime of when it was closed):
protected void gvMilestoneTasks_CustomButtonInitialize(object sender, ASPxGridViewCustomButtonEventArgs e)
{
if (e.VisibleIndex == -1)
return;
ASPxGridView grid = sender as ASPxGridView;
if (e.ButtonID == "btnClose")
{
object o = grid.GetRowValues(e.VisibleIndex, "closed");
bool flagVisible = Convert.IsDBNull(o);
e.Visible = flagVisible ? DefaultBoolean.True : DefaultBoolean.False;
}
else if (e.ButtonID == "btnReopen")
{
object o = grid.GetRowValues(e.VisibleIndex, "closed");
bool flagVisible = !Convert.IsDBNull(o);
e.Visible = flagVisible ? DefaultBoolean.True : DefaultBoolean.False;
}
}
I can observe also an error message in the browser in the sense "More controls with the DXCBtn_0_9_-1 were found. The FindControl requires unique identifiers of the controls" (loosely translated) -- this is hidden somewhere deep in the controls; I am not using the FindControl.
Where is the bug hidden?
Thanks for your help.
I think this is a DevExpress bug fixed in version 2014 2.5:
Fix
The reason is that FilterRow behaves as another row of the displayed grid. It is considered to be another visible row. This way the handler should return early also in the case when the cell type is detected as filter.
if (e.VisibleIndex == -1 || e.CellType == GridViewTableCommandCellType.Filter)
return;
Alternatively, that part can be changed to...
if (e.CommandCellType != GridViewTableCommandCellType.Data)
return;
How to open new tab window with passing ID using Query String.
I tried all the possible ways,i.e Onclientclick and others. but not.help me.
Thank you in advance
if (e.CommandName == "Items")
{
int ID = Convert.ToInt32(e.CommandArgument.ToString());
Response.Redirect("Add_Items.aspx?TestID=" + ID);
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
int id = Convert.ToInt32(Request.QueryString["TestID"]);
lblTestID.Text = id.ToString();
}
The server-side code can't instruct the browser to open a new tab, that's not how redirects work. All a redirect does is tell the browser to navigate to an address.
To open a new tab, you'd need to use client-side code. Generally this could be as simple as:
window.open(address, '_blank');
However, keep in mind that browsers make no standard guarantee to open things in tabs vs. new windows. That's entirely up to the browser's settings and capabilities. (I think that in some browsers it might make a difference whether this code is executed directly or in a click event.)
I am having user control registered on a aspx page.
there is a button on user control resisted on some aspx page that perform some action to store the value of viewste to the database, and below that i have cleared my viewstate value, but if i do right click and reload the page it reinsert the values to the data base, what i am missing or the viewstate do not get cleared, how should i prevent it to resave the same entries to the database.
I am partially writing my code below.
protected void btnFinish_Click(object sender, EventArgs e)
{
if (ViewState["dtQuestions"] != null)
{
foreach (DataRow dr in dtQuestions.Rows)
{
int currQueScore = GetAnswerScore(dr["AnswerType"].ToString().Trim(), dr["ClientAnswerValue"].ToString().Trim());
dr["ClientAnswerScore"] = currQueScore;
myScore += currQueScore;
SurveyClientAnswer objDetail = new SurveyClientAnswer();
objDetail.SurveyClientID = objMaster.SurveyClientID;
objDetail.QuestionID = int.Parse(dr["QuestionID"].ToString());
objDetail.Answer = dr["ClientAnswerValue"].ToString();
objDetail.Score = int.Parse(dr["ClientAnswerScore"].ToString());
DB.SurveyClientAnswers.InsertOnSubmit(objDetail);
DB.SubmitChanges();
}
objMaster.FinalScore = myScore;
DB.SubmitChanges();
ViewState["dtQuestions"] = null;
}
else
{
ModalPopupExtender1.Show();
pnl.Visible = true;
}
}
If you don't want to reinsert the data, simply create a unique constraint on the database to disallow this scenario.
As far as doing it in code, you could possibly only insert the data adding this condition to the if statement:
if(IsPostBack && (ViewState["dtQuestions"] != null)
{
//meaning the user is not simply reloading the page but actually posting back
}
Note that there might be some corner cases where this won't work. Without testing is difficult to know but I strongly believe that this should be handled at the database level instead.
I am developing an online exam application using asp.net. In the start exam page I have created a javascript countdown timer.
How can I move to the next page automatically after the timer reaches 00?
Here is my code:
long timerStartValue = 1000 ;
private int TimerInterval
{
get
{
int o =(int) ViewState["timerInterval"];
if(o==0)
{
return (o);
}
return 50 ;
}
set
{
ViewState["timerInterval"] = value;
}
}
protected void Page_PreInit(object sender,EventArgs e)
{
string timerVal = Request.Form["timerData"];
if(! String.IsNullOrEmpty(timerVal))
{
timerVal = timerVal.Replace(",", String.Empty) ;
this.timerStartValue = long.Parse(timerVal);
}
}
protected void Page_Load(object sender, EventArgs e)
{
if(! IsPostBack)
{
this.timerStartValue = 10000; //3599000;//14400000;
this.TimerInterval = 500;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
this.timerStartValue = 3599000;
}
protected void Page_PreRender(object sender, EventArgs e)
{
System.Text.StringBuilder bldr=new System.Text.StringBuilder();
bldr.AppendFormat("var Timer = new myTimer({0},{1},'{2}','timerData');", this.timerStartValue, this.TimerInterval, this.lblTimerCount.ClientID);
bldr.Append("Timer.go()");
ClientScript.RegisterStartupScript(this.GetType(), "TimerScript", bldr.ToString(), true);
ClientScript.RegisterHiddenField("timerData", timerStartValue.ToString());
}
Thanks in advance,
sangita
It sounds like when you click the "Next" button, you are loading an entirely new page. This of course changes all the content and resets all the javascript. You can't maintain state across pages without a bit of work.
The solution to this could be to save the timer state when the next button is pressed, and pass it to the next stage. You could do this by saving the timer state to a hidden form input and submitting it along with the Next button.
The other option would be to load your questions via AJAX. Instead of moving to a new page every time the next button is clicked, you could simply replace the question portion of the page with a new question, and leave the timer intact. This is probably the solution I would use.
Are u reloading the entire page when clicking on the next button ? That may leads to realod the java script file also.So the variable values will reset.May be you can think about showing the questions /answers via Ajax.You need not reload the entire page when showing the next question.the part when you show the quiz will only be updated.so you can maintain the global variables in your java script too. Check the below link to know about partial page updating using jQuery.
http://www.west-wind.com/presentations/jquery/jquerypart2.aspx
Hope this helps.
You can put the timer in an iframe if you can't get rid of the postback.
You need a way to persist information between pages, and there's really only one possibility: To make it part of the next page request.
Now, this could be subdivided into 2 categories:
1) As part of the url: http://www.example.com/page?timer=123;
2) As part of the headers;
And number 2 opens new possibilities:
a) As part of POST data;
b) As a client-side cookie only;
c) As a cookie tied to information on the server;
Number 1, 2a and 2b can be manipulated by the user. So what you can do is store some value in a cookie, a hash for example or a database row ID, that you'll use to fetch information on the server.
tl;dr? Use a asp "Session object". It lets you keep things on the server-side and users will have no idea what they are.