An Unusual UpdatePanel

Posted by João Angelo on Exceptional Code See other posts from Exceptional Code or by João Angelo
Published on Sun, 03 Jun 2012 21:28:34 +0000 Indexed on 2012/06/03 22:46 UTC
Read the original article Hit count: 329

Filed under:
|
|

The code you are about to see was mostly to prove a point, to myself, and probably has limited applicability. Nonetheless, in the remote possibility this is useful to someone here it goes…

So this is a control that acts like a normal UpdatePanel where all child controls are registered as postback triggers except for a single control specified by the TriggerControlID property. You could basically achieve the same thing by registering all controls as postback triggers in the regular UpdatePanel. However with this, that process is performed automatically.

Finally, here is the code:

public sealed class SingleAsyncTriggerUpdatePanel : WebControl, INamingContainer
{
    public string TriggerControlID { get; set; }

    [TemplateInstance(TemplateInstance.Single)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public ITemplate ContentTemplate { get; set; }

    public override ControlCollection Controls
    {
        get
        {
            this.EnsureChildControls();

            return base.Controls;
        }
    }

    protected override void CreateChildControls()
    {
        if (string.IsNullOrWhiteSpace(this.TriggerControlID))
            throw new InvalidOperationException(
                "The TriggerControlId property must be set.");

        this.Controls.Clear();

        var updatePanel = new UpdatePanel()
        {
            ID = string.Concat(this.ID, "InnerUpdatePanel"),
            ChildrenAsTriggers = false,
            UpdateMode = UpdatePanelUpdateMode.Conditional,
            ContentTemplate = this.ContentTemplate
        };

        updatePanel.Triggers.Add(new SingleControlAsyncUpdatePanelTrigger
        {
            ControlID = this.TriggerControlID
        });

        this.Controls.Add(updatePanel);
    }
}

internal sealed class SingleControlAsyncUpdatePanelTrigger : UpdatePanelControlTrigger
{
    private Control target;

    private ScriptManager scriptManager;

    public Control Target
        {
            get
            {
                if (this.target == null)
                {
                    this.target = this.FindTargetControl(true);
                }

                return this.target;
            }
        }

    public ScriptManager ScriptManager
        {
            get
            {
                if (this.scriptManager == null)
                {
                    var page = base.Owner.Page;

                    if (page != null)
                    {
                        this.scriptManager = ScriptManager.GetCurrent(page);
                    }
                }

                return this.scriptManager;
            }
        }

    protected override bool HasTriggered()
    {
        string asyncPostBackSourceElementID = this.ScriptManager.AsyncPostBackSourceElementID;

        if (asyncPostBackSourceElementID == this.Target.UniqueID)
            return true;

        return asyncPostBackSourceElementID.StartsWith(
            string.Concat(this.target.UniqueID, "$"),
            StringComparison.Ordinal);
    }

    protected override void Initialize()
    {
        base.Initialize();

        foreach (Control control in FlattenControlHierarchy(this.Owner.Controls))
        {
            if (control == this.Target)
                continue;

            bool isApplicableControl = false;
            isApplicableControl |= control is INamingContainer;
            isApplicableControl |= control is IPostBackDataHandler;
            isApplicableControl |= control is IPostBackEventHandler;

            if (isApplicableControl)
            {
                this.ScriptManager.RegisterPostBackControl(control);
            }
        }
    }

    private static IEnumerable<Control> FlattenControlHierarchy(
        ControlCollection collection)
    {
        foreach (Control control in collection)
        {
            yield return control;

            if (control.Controls.Count > 0)
            {
                foreach (Control child in FlattenControlHierarchy(control.Controls))
                {
                    yield return child;
                }
            }
        }
    }
}

You can use it like this, meaning that only the B2 button will trigger an async postback:

<cc:SingleAsyncTriggerUpdatePanel ID="Test" runat="server" TriggerControlID="B2">
    <ContentTemplate>
        <asp:Button ID="B1" Text="B1" runat="server" OnClick="Button_Click" />
        <asp:Button ID="B2" Text="B2" runat="server" OnClick="Button_Click" />
        <asp:Button ID="B3" Text="B3" runat="server" OnClick="Button_Click" />
        <asp:Label ID="LInner" Text="LInner" runat="server" />
    </ContentTemplate>
</cc:SingleAsyncTriggerUpdatePanel>


© Exceptional Code or respective owner

Related posts about .NET

Related posts about ASP .NET