Вопрос

The code below contains two update panels, both with UpdateMode set to "Conditional". One has a Timer that triggers every 5 secs and the other has a Label and TextBox (AutoPostBack set to true) with a wired TextChanged event that changes the label text. The timer is not involved in page rendering: it does an heartbeat and other logging operations. What happens is that TextChanged event is fired each time the user types input in the textbox and the timer triggers, effectively changing the label text even if the postback originated from another update panel (the one with the timer). I would like instead the TextChanged event to fire only when the textbox loose focus because of an user action. Is it possible? Or should I just avoid using System.Web.UI.Timer and use something else? What in this case?

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>

<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">

<asp:ScriptManager ID="ScriptManager1" runat="server"
    EnablePartialRendering="true">
</asp:ScriptManager>

<asp:UpdatePanel ID="UpdatePanel1" runat="server"
    ChildrenAsTriggers="False"  UpdateMode="Conditional">

    <ContentTemplate>
        <asp:Timer ID="Timer1" runat="server" ontick="Timer1_Tick"
            Interval="5000"></asp:Timer>
    </ContentTemplate>

    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
    </Triggers>

</asp:UpdatePanel>

<asp:UpdatePanel ID="UpdatePanel2" runat="server"
    ChildrenAsTriggers="False" UpdateMode="Conditional">

    <ContentTemplate>
        <asp:TextBox ID="TextBox1" runat="server" AutoPostBack="True" 
            ontextchanged="TextBox1_TextChanged"></asp:TextBox>
        <asp:Label ID="Label1" runat="server"
            Text="Label"></asp:Label>
    </ContentTemplate>

    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="TextBox1"
            EventName="TextChanged" />
    </Triggers>

</asp:UpdatePanel>

</asp:Content>

Code behind:

    protected void Timer1_Tick(object sender, EventArgs e)
    {
        // Heartbeat, logging and other stuff
    }

    protected void TextBox1_TextChanged(object sender, EventArgs e)
    {
        Label1.Text = TextBox1.Text;
    }
Это было полезно?

Решение

I ended up understanding that a postback is not much suitable for background activities not related to page rendering. A cleaner approach would be calling a WebService or posting to a different page. Extending the Timer class, I was able to achieve the second strategy overriding the javascript function called when the timer ticks, making it able to perform a Post to a different url. Feel free to point me to other ajax controls that behaves similarly.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.ComponentModel;
using System.Text;

namespace AjaxExt
{

    [ToolboxData("<{0}:TimerEx runat=server></{0}:TimerEx>")]
    public class TimerEx : Timer
    {
        public const string POST_FIELD = "__TIMEREXDATA";

        public TimerEx()
        {
            TimerMode = TimerMode.DoPostBack;
            PostUrl = "";
            PostData = "";
            PostField = "";
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (TimerMode.HasFlag(TimerMode.DoPost) && !String.IsNullOrEmpty(PostUrl))
            {
                string postUrl = PostUrl.StartsWith("/") ? PostUrl : "/" + PostUrl;
                string postField = String.IsNullOrEmpty(PostField) ? POST_FIELD : PostField;
                // Override tick function
                StringBuilder builder = new StringBuilder();
                builder.AppendLine("function pageLoad() {");
                builder.AppendLine("    var timer = $find('" + this.ClientID + "');");
                builder.AppendLine("    timer._doPostback = function () {");
                builder.AppendLine("        $.ajax({");
                builder.AppendLine("            url: \"" + postUrl + "\",");
            if (String.IsNullOrEmpty(PostData))
                builder.AppendLine("            data: null,");
            else
                builder.AppendLine("            data: { " + postField + ": " + PostData + "},");
                builder.AppendLine("            success: null,");
                builder.AppendLine("            error: null");
                builder.AppendLine("        });");
            if (TimerMode.HasFlag(TimerMode.DoPostBack))
                builder.AppendLine("        __doPostBack(this.get_uniqueID(),'');");
                builder.AppendLine("    };");
                builder.AppendLine("}");

                Page.ClientScript.RegisterClientScriptBlock(typeof(Page), "dopostscript", builder.ToString(), true);
            }
        }

        [DefaultValue(TimerMode.DoPostBack)]
        public TimerMode TimerMode
        {
            get;
            set;
        }

        [DefaultValue("")]
        public string PostUrl
        {
            get;
            set;
        }

        [DefaultValue("")]
        public string PostData
        {
            get;
            set;
        }

        [DefaultValue("")]
        public string PostField
        {
            get;
            set;
        }
    }

    [Flags]
    public enum TimerMode
    {
        DoPost = 1,
        DoPostBack = 2,
        DoBoth = DoPost | DoPostBack
    }
}

It can be added to a page as it follow:

<%@ Register Assembly="AjaxExt" Namespace="AjaxExt" TagPrefix="jaxext" %>

<ajaxext:TimerEx ID="PingTimer" runat="server" PostUrl="HeartBeat.aspx" 
    TimerMode="DoPost" Interval="10000" />
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top