I have a multiple GridView generate dynamically placed inside an update panel where its data source is from the database. What i want to do is change the color of the Gridview cells that contains all the 90 random number generated every three seconds. The part where i am having problem is that the page waits untill all the random numbers are generated and displays the final output instead of updating the Gridview cells color every time the random number is being generated.I know the problem is with the page lifecycle thing but, i dont know how to approach this problem. Can someone please guide me?
namespace validation
{
public partial class index : System.Web.UI.Page
{
int countTik;
protected void Page_Load(object sender, EventArgs e)
{
//Calling javascript
// ScriptManager.RegisterClientScriptBlock(this,GetType(), "mykey", "myFunction();", true);
// GridView gridview = new GridView();
//HiddenField1.Value = "2";
DataAccessForUserInterface ds = new DataAccessForUserInterface();
countTik = ds.selectAll();
for (int k = 1; k <= countTik; k++)
{
UpdatePanel updatePanel = new UpdatePanel();
updatePanel.ID = "panel"+k;
string Ticketname = "TicketNo" + k;
GridView gridview = new GridView();
RetriveTickets RetTik = new RetriveTickets();
List<RetriveTickets> Ticketdata = ds.GetMasterDetails(Ticketname).ToList();
RetTik.row1 = Ticketdata[0].row1;
RetTik.row2 = Ticketdata[0].row2;
RetTik.row3 = Ticketdata[0].row3;
DataTable dataTable1 = new DataTable();
dataTable1.Columns.Add("1");
dataTable1.Columns.Add("2");
dataTable1.Columns.Add("3");
dataTable1.Columns.Add("4");
dataTable1.Columns.Add("5");
dataTable1.Columns.Add("6");
dataTable1.Columns.Add("7");
dataTable1.Columns.Add("8");
dataTable1.Columns.Add("9");
//rows
// List<int> first = new List<int>();
foreach (var item in Ticketdata[0].row1)
{
var row = dataTable1.NewRow();
row["1"] = item.C0;
row["2"] = item.C1;
row["3"] = item.C2;
row["4"] = item.C3;
row["5"] = item.C4;
row["6"] = item.C5;
row["7"] = item.C6;
row["8"] = item.C7;
row["9"] = item.C8;
dataTable1.Rows.Add(row);
}
DataTable dataTable2 = new DataTable();
dataTable2.Columns.Add("1");
dataTable2.Columns.Add("2");
dataTable2.Columns.Add("3");
dataTable2.Columns.Add("4");
dataTable2.Columns.Add("5");
dataTable2.Columns.Add("6");
dataTable2.Columns.Add("7");
dataTable2.Columns.Add("8");
dataTable2.Columns.Add("9");
//rows
foreach (var item in Ticketdata[0].row2)
{
var row = dataTable2.NewRow();
row["1"] = item.C0;
row["2"] = item.C1;
row["3"] = item.C2;
row["4"] = item.C3;
row["5"] = item.C4;
row["6"] = item.C5;
row["7"] = item.C6;
row["8"] = item.C7;
row["9"] = item.C8;
dataTable2.Rows.Add(row);
}
DataTable dataTable3 = new DataTable();
dataTable3.Columns.Add("1");
dataTable3.Columns.Add("2");
dataTable3.Columns.Add("3");
dataTable3.Columns.Add("4");
dataTable3.Columns.Add("5");
dataTable3.Columns.Add("6");
dataTable3.Columns.Add("7");
dataTable3.Columns.Add("8");
dataTable3.Columns.Add("9");
//rows
foreach (var item in Ticketdata[0].row3)
{
var row = dataTable3.NewRow();
row["1"] = item.C0;
row["2"] = item.C1;
row["3"] = item.C2;
row["4"] = item.C3;
row["5"] = item.C4;
row["6"] = item.C5;
row["7"] = item.C6;
row["8"] = item.C7;
row["9"] = item.C8;
dataTable3.Rows.Add(row);
}
dataTable2.Merge(dataTable3);
dataTable1.Merge(dataTable2);
gridview.ID = "gridview" + k;
gridview.CssClass = "table table-bordered table-dark";
//gridview.AutoGenerateColumns = true;
//gridview.HeaderStyle.CssClass = "table-primary";
gridview.ShowHeader = false;
gridview.DataSource = dataTable1;
// gridview.DataBound += new EventHandler(groupHeader);
gridview.RowDataBound += new GridViewRowEventHandler(Gv_RowDataBound);
gridview.DataBind();
GridViewRow rows = new GridViewRow(0, 0, DataControlRowType.Header, DataControlRowState.Normal);
TableHeaderCell cell = new TableHeaderCell();
cell.Text = "Customer Name";
cell.ColumnSpan = 5;
rows.Controls.Add(cell);
cell = new TableHeaderCell();
cell.ColumnSpan = 4;
cell.Text = "Ticket Number";
rows.Controls.Add(cell);
// row.BackColor = Color.BlanchedAlmond;
gridview.HeaderRow.Parent.Controls.AddAt(0, rows);
updatePanel.ContentTemplateContainer.Controls.Add(gridview);
grid1.Controls.Add(updatePanel);
}
StartCounting();
}
//fill color of already generated random number stored in database and that exist on the gridview on page load
private void Gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
DataAccessForUserInterface da = new DataAccessForUserInterface();
int[] randomList = da.getRandom();
if (e.Row.RowType == DataControlRowType.DataRow)
{
for (int r = 0; r < 90; r++)
{
for (int i = 0; i <= 8; i++)
{
int num = Convert.ToInt32(e.Row.Cells[i].Text);
if (num != 0 && num == randomList[r])
{
//e.Row.BackColor = Color.Red;
e.Row.Cells[i].BackColor = Color.Green;
}
}
}
}
}
public int[] StartCounting()
{
int[] random = new int[90];
{
int[] randomNumber = new int[90];
int count = 0;
for (int i = 0; i < randomNumber.Length; i++)
{
Random rnd = new Random();
int num = rnd.Next(1, 91);
if (randomNumber.Contains(num))
{
i--;
}
else
{
randomNumber[i] = num;
int countingNumber = randomNumber[i];
count++;
DataAccessForUserInterface da = new DataAccessForUserInterface();
da.insertRandom(countingNumber); // Store Random number generated on Database
Task.Delay(3 * 1000).Wait();
for(int GridCount=1;GridCount<=countTik;GridCount++)
{
GridView gv = (GridView)FindControl("gridview"+GridCount);
foreach (GridViewRow row in gv.Rows)
{
for (int cellIndex = 0; cellIndex < 9; cellIndex++)
{
if (row.Cells[cellIndex].Text.Equals(countingNumber))
{
row.BackColor = Color.Green;
}
}
}
}
}
}
return randomNumber;
}
}
}
}
Well, when you post-back, even in a update panel, the web page travels up to the server. The code behind runs - all of it. WHILE that code runs, the web page is up on the server. ANY change of a control is NOT seen by the user (they have a copy of the web page just sitting on their desktop).
The user ONLY will see ANY updates from your code behind ONLY AFTER the code is 100% done, finished and exits. When the code exists, then the WHOLE page (or update panel ) now travels back down to the client side, and is updated.
Clearly, any code behind updates - ALL OF them will appear to occur in "one shot" or "one update".
A basic grasp of how web pages work is required here.
You have this:
NOTE SUPER careful in the above - note how NO WEB page is active on the web server side.
You do NOT have this setup:
And you do NOT have this setup:
so, you JUST have a web page sitting on the client side.
If the user clicks a button, or even a button in a update panel, then that post-back starts, you have this:
The page is sent to the web server (it is just sitting there - waiting for ANY user to post back a page - NOT JUST YOUR page!!!!
So, page travels up to web server.
You now have this:
Now, to be fair, in above a copy of the web page is ALSO still sitting on the users desktop - but they see the little spinner - page is waiting for ALL OF AND 100% of the code behind to finish.
You in fact NEVER directly interact with the user with code behind.
Your server side code ONLY interacts with the web page - and for the SHORT time the page is up on the server.
While the code behind runs, you are updating the copy of the browser on the server side - any chances cannot be seen by the user. In fact, in most cases the order in which you update things don't matter, since ALL of the changes will have to complete.
Once and ONLY once your code behind is done running, then the whole page (or the update panel) now travels back to the client side browser, page loads, JavaScript starts to run, and NOW you see the updates from your code behind.
So, they will ALWAYS appear to update in one shot - not control by control.
And EVEN if you put delays in your code - all you will accomplish is to delay the page sitting up on the server for a LONGER period of time - but no updates will be seen client side.
So, after the updates occur (all code behind is done running), then, and only then does the page travel back to the client side, like this:
At that point, the server TOSSES out your web page, destroys the page class, and even all your code variables now go out of scope. Very much like when you exit a subroutine call - the local vars and values of that sub (or now the web page) go out of scope.
The web server is now just sitting waiting for ANY user to post back a page (or partial post back via a udpate panel - but the process is the same either way).
So, as a result, you can't put in code delays.
Your have to let the code behind finish.
So, if you want the user to "see" the page update (the squares in your case), then you have to use one of several approaches:
One way:
You put in a JavaScript timer, and have it click a update button (even a hidden one) in the update panel. Each time you click (say every half second), then you update one square in code behind, and then the update panel will show those results).
Another way is to have a JavaScript timer, it calls a JavaScript routing every second (or maybe half second, or whatever).
That JS routine will thus call a server side web method, get the value back of ONE control or text box, and the update it. This is typically referred to a ajax call.
This can be a complex bit of code. Worse yet, your squares to update are nested in a gridview, making the js code even more difficult.
The most easy way?
Setup a code behind routine, use a session() counter (or view state).
Drop in a timer control into the update panel.
Have the timer control call your update to the grid routine, have it call it say once ever second. When the timer routine triggers, you update 1 box, increment the counter (to update the next box), and code behind exits.
So, you have to setup a routine that can modify ONLY ONE of the text boxes, and exit. You also need some counter that increments.
So for each timer trigger, your timer code calls the uppate routine - updates one text box, increment counter, and is done.
When the timer code figures out you JUST updated the last box, then the timer event can turn off the timer trigger, and the refreshing of the page will stop.
A REALLY simple example would be to have a text box count from 1 to 10, and update each time.
If we just run a for/next loop, put 1 to 10 in the text box, when the user clicks the button, the code can run, shove 1, then 2 etc into the text box. but due to the post-back and round trip diagrams above, the user will ONLY ever see "10" as the final result.
So, lets do the 1 to 10, use a update panel, and a simple text box.
So, we have this markup:
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<br />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TextBox ID="txtCount" runat="server"
Height="42px"
Width="42px"
Font-Size="XX-Large"
Text="0"
style="text-align: center">
</asp:TextBox>
<br />
<asp:Button ID="Button1" runat="server" Text="Start Timer" CssClass="btn" OnClick="Button1_Click"/>
<asp:Timer ID="Timer1" runat="server"
Enabled="False"
Interval="1000" OnTick="Timer1_Tick"></asp:Timer>
</ContentTemplate>
</asp:UpdatePanel>
And our code behind can be this:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Session["MyCount"] = 0;
Timer1.Interval = 1000; // tick our timer each second
Timer1.Enabled = true;
}
protected void Timer1_Tick(object sender, EventArgs e)
{
int? MyCount = Session["MyCount"] as int?;
if (MyCount < 10 )
{
MyCount = MyCount + 1;
txtCount.Text = MyCount.ToString();
Session["MyCount"] = MyCount;
}
else
{
// we are done, stop the timer
Timer1.Enabled = false;
}
}
And we now see/have this:
So, I suggest you try this on a test page. Drop in that text box, the timer, and the update panel.
Note that if you JUST runt he loop one to 10 in code behind (without the timer), then the code does run, does update the text box (1 to 10), but you NEVER see the results, since you just have to refer to the above post-back diagrams to grasp how this works.
So, you need to "run" that code you have to create the new values, but you need a timer, a counter of some time, and each time you increment the timer, you update ONE box in the grid, and then each time the timer fires, you then have to update another box.
When the counter has updated all of the new changes, then your code behind can set the timer enabled = false to stop this updating process.