ASP.NET Progress

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • navyjax2
    New Member
    • Aug 2007
    • 18

    ASP.NET Progress

    What if your event handler has to be nonstatic?
  • Frinavale
    Recognized Expert Expert
    • Oct 2006
    • 9749

    #2
    Navyjax2, could you please provide more information about the problem you are facing?

    There is the INotifyProperty Changed.Propert yChanged Event. When this event is raised it indicates that the property has been changed.



    -Frinny

    Comment

    • navyjax2
      New Member
      • Aug 2007
      • 18

      #3
      Bare with me, I'm fairly new to delegates, interfaces, and events, even though I've been doing C# for almost 2 yrs and VB/VB .NET since VB6.

      Basically I wanted to do an event whenever a variable's value changed. A public variable value (essentially a counter) is increased as a DataTable is parsed through a foreach loop. The variable's value changing would trigger an event that would change the innerHTML of a span tag on the page (which has to happen within a nonstatic function), to give an indication of what row we were on out of the total - creating a progress counter.

      Something like:
      Code:
          private long _rowCount = CSV.rowCount;
          public delegate void PrivateVariableChanged(object sender, EventArgs e);
          public event PrivateVariableChanged SelectedChanged
          {
              add { fileLinkArea.InnerHtml = "Processed " + CSV.rowCount + " of " + CSV.rowTotal; }
              remove { }
         };  
      
          private void NotifyPrivateVariableChanged(String info)
          {
              if (SelectedChanged != null)
              {
                  SelectedChanged(this, new PrivateVariableChangedEventArgs(info));
              }
          }
          public long RowCount
          {
              get
              {
                  return this._rowCount;
              }
      
              set
              {
                  if (value != this._rowCount)
                  {
                      this._rowCount = value;
                      NotifyPrivateVariableChanged("RowCount");
                  }
              }
          }
      But this is totally hosed and does not work. I checked MSDN and there is no PrivateVariable ChangedEventArg s even though there is a PrivateVariable Changed. Any help?

      Thanks,
      Tom

      Comment

      • Frinavale
        Recognized Expert Expert
        • Oct 2006
        • 9749

        #4
        I think that the easiest way to handle this is to make the public variable private and provide a public Property instead. That way your property could either raise an event that notifies all other classes of the change...or it could change the innerHTML (not sure why you're doing this but ok) of the element.

        It's nice when encapsulation works for you :)

        -Frinny

        Comment

        • navyjax2
          New Member
          • Aug 2007
          • 18

          #5
          So what changed property would it be firing on? And is that the right syntax for the event?

          I was trying this another way, too - as the foreach loop processes and increments the counter I have, couldn't I just set the innerHTML updating to occur there? But the thing is, _Default defaultClass = new _Default() won't instantiate the _Default class where my page is, so that it could run a function there that would take care of the innerHTML updating. Is there a way around this? If so, I might not even need events at all...

          Thanks,
          Tom

          Comment

          • Frinavale
            Recognized Expert Expert
            • Oct 2006
            • 9749

            #6
            What is really confusing me is the that you're setting the innerHTML.
            First of all this isn't typically done in ASP.NET any more. Instead the Text property is usually used (for example: myLabel.Text = "1 of 5")

            The other thing that is confusing me is that your C# code is executed on the server...this means that the user will not see the change to the innerHTML (or even the Text property) until the response to the request is sent to the browser....in which case, why don't you just do your loop and when you're finished, set the innerHTML (or Text property) of the control?

            -Frinny

            Comment

            • navyjax2
              New Member
              • Aug 2007
              • 18

              #7
              Well, what I was doing was setting a span on the page equal to this text. I wanted the span to be able to show the progress to a user ("Row 1 of 14298 processed", etc.) as the DataTable is being looped through. I found that, even if I put the code into the _Default class (to prevent instantiation issues) and had it try and update the field "updateText " as the counter increased, it doesn't show on the page, at least while debugging it and letting it run a couple of loops - probably for the very reason you are saying - me not waiting til the end probably prevented it from displaying, but I want it to do it as it's going, not wait til the end.

              So I thought about how you might be able to do this with JavaScript, since I know that JavaScript can do this, but adding an attribute to the button click that initiates this whole thing in Page_Load():
              Code:
              btnDBExport.Attributes.Add("onclick", "sendIt(" + data + ")");
              where
              Code:
              function sendIt(data) {
                 document.getElementById('updateText').innerHTML = data;
              }
              and
              Code:
              data = "Row " + rowCount + " of " + dt.Rows.Count + " processed.";
              would only update the field once, and before my counter even exists - not throughout the loop. Is there a way to write on the fly to the page from C# to JavaScript in this way? Thanks for the replies.

              -Tom
              Last edited by Frinavale; Dec 21 '09, 02:11 PM. Reason: Please post code in [code] ... [/code] tags. Added code tags.

              Comment

              • Frinavale
                Recognized Expert Expert
                • Oct 2006
                • 9749

                #8
                This is actually a little more complicated than you would think.
                I'm sure that there may be another way to do this but this is the first thing that comes to my mind....

                When you click the button that starts the processing, also start a Ajax Timer control that posts back to the server after a given amount of time has passed.

                Have the loop set a Session variable so that when the Ajax Timer posts back to the server the method that handles the tick event can retrieve the value and display it in the web page.

                I suggest using an ASP.Net Label control instead of your typical HTML <span> because this control renders as a <span> and lets you manipulate the Text and style aspects (along with a bunch of other properties) in your VB.NET/C# server code.

                Place the Label that is going to display the text inside an UpdatePanel so that only that section of the page is updated....like wise place the GridView and the Button in an UpdatePanel so only that section of the page is being processed when you click the button.

                -Frinny

                Comment

                • Frinavale
                  Recognized Expert Expert
                  • Oct 2006
                  • 9749

                  #9
                  I've moved your question into a thread of it's own because this question is straying off topic from the original question.

                  New questions should not be posted onto the end of old questions.

                  This thread has been split off of:


                  -Frinny

                  Comment

                  • jumbojs
                    New Member
                    • May 2009
                    • 20

                    #10
                    This sounds like it would work but I'm trying to add the Timer control in code behind after the button is clicked and it isn't really working

                    Code:
                     protected void btnRunUpdateNow_Click(object sender, EventArgs e)
                        {
                            
                    
                            Timer timerControl = new Timer();
                            timerControl.Tick += new EventHandler<EventArgs>(timerControl_Tick);
                            timerControl.Interval = 3000;
                              
                           .......
                       }
                    What am I missing. Nothings happening as I run this code

                    Comment

                    • Frinavale
                      Recognized Expert Expert
                      • Oct 2006
                      • 9749

                      #11
                      Instead of adding the event handler in this code try moving it to your declaration.
                      For example:
                      Code:
                      <asp:Timer ID="timerControl" runat="server" OnTick="timerControl_Tick" Interval="3000" ></asp:Timer>
                      Set your timer to be disabled at first....you will need to enable it when you need it to start ticking back to the server.

                      The thing is that I was a little mistaken earlier....you can't really enable the timer in your button click event because you need to send the request back to the client before you start the looping. You're probably going to have to send 2 requests: the first one starts the timer, the second starts the looping.

                      Or you need to figure out a way to send the request back to the client while the server continues to loop.

                      -Frinny

                      Comment

                      • jumbojs
                        New Member
                        • May 2009
                        • 20

                        #12
                        Timer

                        Thanks for the reply. I actually tried setting up the Timer control this way (disabling it and enabling it on button click) Didn't really work. I was trying to do something like this.

                        Code:
                        <asp:Timer ID="Timer1" runat="server" Interval="3000" Enabled="false" OnTick="Timer1_Tick">
                            </asp:Timer>
                        
                            <asp:UpdatePanel ID="upProgress" runat="server" >
                                <ContentTemplate>
                                    <div>
                                        <asp:Label ID="Progress" runat="server" Text="Sample"></asp:Label>
                                    </div>
                                </ContentTemplate>
                            </asp:UpdatePanel>
                        Then in my Timer Click Event

                        Code:
                        protected void Timer1_Tick(object sender, EventArgs e)
                            {
                        
                                Session["Progress"] = "Started processing...";
                        
                                Progress.Text = (string)Session["Progress"];
                            }
                        Then what I want to do here is on click, I start the import process by passing the control of to a class housed in an assembly.

                        Code:
                        protected void btnRunUpdateNow_Click(object sender, EventArgs e)
                            {
                                         Timer1.Enabled = true;
                                         myClass.RunUpdate();
                            }

                        Inside myClass housed in an assembly, I was trying to update progress using the Session variable.

                        Code:
                        public void RunUpdate()
                           {
                                    .....
                                    HttpContext.Current.Session["Progress"] = _Orders.Count.ToString();
                           }
                        I'm not sure how else to get this to work. If I need to move this to another thread, let me know.

                        Comment

                        • Frinavale
                          Recognized Expert Expert
                          • Oct 2006
                          • 9749

                          #13
                          I just tried to implement what I was suggesting and it doesn't work.
                          This comes back to a problem that I got stuck on about a year ago (or so) that I had forgotten about...

                          Apparently (I'm not sure if this is a Cassini thing or not) the server code will not process more than one request from the same webpage at the same time.

                          For example:
                          • Button posts back to server and a long process is started
                          • TimerControl posts back to server


                          The TimerControl postback won't be processed until the button click event is finished processing. This defeats the whole purpose of Asynchronous. I was very annoyed when I discovered this and never did find out why this was happening, nor did I find out how to get around this.

                          -Frinny

                          Comment

                          • Frinavale
                            Recognized Expert Expert
                            • Oct 2006
                            • 9749

                            #14
                            I think I'm getting closer to finding an answer.
                            Apparently you have to specify your page is Async in the @Page directive. Then you have to implement Begin and End methods for the things are going to take a long time so that they are executed asynchronously. ..

                            I'm reading Wicked Code:Asynchrono us Pages in ASP.NET 2.0 in an attempt to solve this problem.

                            -Frinny

                            Comment

                            • Frinavale
                              Recognized Expert Expert
                              • Oct 2006
                              • 9749

                              #15
                              Upon further reading of the article I discovered the asynchronous page was simply for processing more than one thing in different threads before the response is sent to the browser. It was an interesting read but doesn't help with the problem.

                              So, I have something that sort of works....it works the very first time the page is loaded but after that it doesn't.

                              This is what I have in my ASPX page:
                              Code:
                              <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestingProgress.aspx.cs" Inherits="WebApplication1.TestingProgress" %>
                              
                              <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                              <html xmlns="http://www.w3.org/1999/xhtml" >
                              <head runat="server">
                                  <title>Testing</title>
                              </head>
                              <body>
                                  <form id="form1" runat="server">
                                  <asp:ScriptManager ID="sm" runat="server"></asp:ScriptManager>
                                  <div>
                                      <asp:UpdatePanel ID="upnl" runat="server">
                                      <ContentTemplate>
                                        <asp:Timer ID="timerControl" runat="server" Enabled="false" Interval="500" OnTick="timerControl_Tick"></asp:Timer>
                                      
                                        <asp:LinkButton ID="startTimer" runat="server" OnClick="startTimer_Click" Text="Start Timer" style="display:none;"></asp:LinkButton>
                                        <asp:Label ID="processingMessage" runat="server"></asp:Label>
                                        <asp:Button runat="server" ID="btnLongProcess" Text="start processing" OnClientClick="return startTimerBeforePosting();" OnClick="btnLongProcess_Click" />
                                        <script type="text/javascript">
                                            function startTimerBeforePosting() {
                                                setTimeout(waitBeforeStartingTimer, 1000);
                                                return true;
                                            }
                                            function waitBeforeStartingTimer() {
                                                __doPostBack('<%=startTimer.ClientID %>', '');
                                            }
                                        </script>
                                      </ContentTemplate>
                                      </asp:UpdatePanel>
                                    
                                   </div>
                                  </form>
                              </body>
                              </html>
                              And this is what I have in my C# code:
                              Code:
                              namespace WebApplication1
                              {
                                  public partial class TestingProgress: System.Web.UI.Page
                                  {
                                      private static int loopIteration = 0;
                                      private int max = 10;
                                      protected void Page_Load(object sender, EventArgs e)
                                      {   if (!IsPostBack) {
                                          loopIteration = 0;
                                          }
                                      }
                                      protected void timerControl_Tick(object sender, EventArgs e)
                                      {  /*int iteration = 0;
                                          if (Session["loopIteration"] != null) {
                                              iteration = (int)Session["loopIteration"];
                                          }
                                          processingMessage.Text = iteration.ToString() +"/"+ max.ToString();
                                          if (loopIteration >= max-1)
                                          {
                                              timerControl.Enabled = false;
                                              processingMessage.Text = "Done. Timer stopped in tick.";
                                          }*/
                                         
                                          processingMessage.Text = loopIteration.ToString() + "/" + max.ToString();
                                          if (loopIteration >= max)
                                          {
                                              timerControl.Enabled = false;
                                              processingMessage.Text += " Done. Timer stopped in tick.";
                                          }
                                      }
                                      protected void startTimer_Click(object sender, EventArgs e)
                                      {
                                          timerControl.Enabled = true;
                                          processingMessage.Text = "Timer started...";
                                      }
                                      protected void btnLongProcess_Click(object sender, EventArgs e)
                                      {
                                          Session["loopIteration"] = 0;
                                          loopIteration = 0;
                                          for (loopIteration = 0; loopIteration < max; loopIteration++)
                                          {
                                              //Session["loopIteration"] = loopIteration;
                                              System.Threading.Thread.Sleep(1000);
                                          }
                                          timerControl.Enabled = false;
                                          processingMessage.Text = "Done. Timer stopped in button click.";
                                      }
                                  }
                              }
                              Now, like I said this works the first time.

                              The JavaScript in the ASPX page calls a function that preforms a postback for a hidden LinkButton on the page a second after the button is clicked. The code that handles the LinkButton click event enables the timer. So the Timer is started after button click the process begins.

                              The button click process sets a static integer to 0 and then increments the static integer by one in a loop that puts the main thread to sleep for a second each loop.

                              In the timer tick event, the static integer is displayed on the page along indicating how much more it has to go before the process is done.

                              This works the first time the page is ever loaded.
                              Once the button has been clicked and the processing stuff has been displayed it stops working.

                              What do I mean by that?
                              Well, if you click the button again the JavaScript waits for one second and fires the postback for the hidden LinkButton...th e thing is that the click event for the link button is not fired until after the button click event is complete. This means that the timer is started after the looping process is done which is not really useful.

                              I don't know why this is the case...

                              Aside from that, another thing that I found is that you can't use Session in this solution. I never realized how slow accessing Session was until this experiment. I tried locking session every time I accessed it thinking that the timer tick event implementation couldn't access Session since the button click event was using it...but this didn't help. You can see the Session attempt commented out in my above posted code.

                              And, I don't think that using a static variable is going to really be feasible in your real-world application, but Session wasn't working...so I tried something else....which "kind of" worked.

                              I'm not sure what to suggest...

                              -Frinny

                              Comment

                              Working...