ASP.NET カスタム Repeater サンプル

前の記事の続き

RSSの各Item に対して表示するような Repeater に相当するカスタムコントロールのサンプル。DataBinder.Eval は遅いので最近はこのような カスタムRepeater を大量に作成して値の参照がコンパイルされるようにしている。

カスタムコントロールのソース:

using System.Collections;
using System.ComponentModel;
using System.ServiceModel.Syndication;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace AsyncRss
{
    /// <summary>
    /// InstantiateIn でこのコントロールの中にテンプレートが展開されます
    /// テンプレート中では Container という変数に格納されています
    /// </summary>
    [ToolboxItem(false)] // ツールボックスには表示しない
    public class SyndicationItemRepeaterコンテナ : Control, INamingContainer
    {
        private readonly SyndicationItem _syndicationItem;

        public SyndicationItemRepeaterコンテナ(SyndicationItem item)
        {
            _syndicationItem = item;
        }

        /// <summary>
        /// 各 SyndicationItem
        /// テンプレート中 Container.Item により参照できる
        /// <example><![CDATA[ <%# Container.Item.Title.Text %> ]]>
        /// </example>
        /// </summary>
        public SyndicationItem Item
        {
            get { return _syndicationItem; }
        }
    }

    /// <summary>
    /// 基本は Repeater だが、コンテナの型をカスタマイズしているクラス
    /// </summary>
    [ParseChildren(true)] // Templateを持つためには ParseChildren が必要
    [ToolboxData("<{0}:SyndicationItemRepeater runat=\"server\" />")]
    [ToolboxItem(true)]
    public class SyndicationItemRepeater : CompositeDataBoundControl
    {
        /// <summary>
        /// 各Itemを表示するテンプレート
        /// </summary>
        [Browsable(false)]
        [TemplateContainer(typeof (SyndicationItemRepeaterコンテナ))]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty), TemplateInstance(TemplateInstance.Multiple)]
        public ITemplate ItemTemplate { get; set; }

        /// <summary>
        /// ヘッダ
        /// </summary>
        [Browsable(false)]
        [TemplateContainer(typeof (WebControlEmptyContainer))]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty), TemplateInstance(TemplateInstance.Single)]
        public ITemplate HeaderTemplate { get; set; }

        /// <summary>
        /// フッタ
        /// </summary>
        [Browsable(false)]
        [TemplateContainer(typeof (WebControlEmptyContainer))]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty), TemplateInstance(TemplateInstance.Single)]
        public ITemplate FooterTemplate { get; set; }

        private void ControlにTemplateを追加(ITemplate template, bool dataBinding)
        {
            var item = new WebControlEmptyContainer();
            template.InstantiateIn(item);
            if (dataBinding) item.DataBind();
            Controls.Add(item);
        }

        private int ControlにItemTemplateを追加(IEnumerable datasource, bool dataBinding)
        {
            int pos = 0;
            foreach (SyndicationItem entity in datasource)
            {
                var container = new SyndicationItemRepeaterコンテナ(entity);
                ItemTemplate.InstantiateIn(container);
                Controls.Add(container);
                if (dataBinding) container.DataBind();
                pos++;
            }
            return pos;
        }

        /// <summary>
        /// データソースから、レコードを取り出しつつテンプレートに基づいたWebControlを追加する。
        /// データソースが空の場合は、ヘッダ・フッタを含めて全く追加しない
        /// </summary>
        /// <param name="dataSource"></param>
        /// <param name="dataBinding"></param>
        /// <returns></returns>
        protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
        {
            int pos = 0;
            Controls.Clear();
            if (dataSource != null)
            {
                if (HeaderTemplate != null) ControlにTemplateを追加(HeaderTemplate, dataBinding);
                pos += ControlにItemTemplateを追加(dataSource, dataBinding);
                if (FooterTemplate != null) ControlにTemplateを追加(FooterTemplate, dataBinding);
            }
            ChildControlsCreated = true;
            return pos;
        }

        /// <summary>
        /// 自分自身をレンダリングしない、すなわちspan要素を出させないための override
        /// </summary>
        /// <param name="writer"></param>
        public override void RenderControl(HtmlTextWriter writer)
        {
            foreach (Control c in Controls)
            {
                c.RenderControl(writer);
            }
            // base.RenderControl(); // 呼ぶとspan要素が出力される
        }
    }
}

利用する aspx ページ。DataSource のところで SyndicationFeed の Items を渡しているところがポイント。

<%@ Page Language="C#" AutoEventWireup="true" Async="true" AsyncTimeout="30" %>
<!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>Rss非同期読込テスト</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <AsyncRss:Rss表示 runat="server" ID="rssdisp1">
            <RssTemplate>
                rss...
                <%# Container.RssFeed取得().Title.Text %><br />
                <AsyncRss:SyndicationItemRepeater ID="rep1" runat="server" DataSource="<%# Container.reader.RssFeed取得().Items %>">
                    <ItemTemplate>
                        RSS各記事のタイトル:
                        <%# Container.Item.Title.Text %>
                        <br />
                    </ItemTemplate>
                </AsyncRss:SyndicationItemRepeater>
            </RssTemplate>
            <TimeoutTemplate>
                RSS読込がタイムアウトしました。</TimeoutTemplate>
            <ErrorTemplate>
                RSS読込時にエラーが発生しました。</ErrorTemplate>
        </AsyncRss:Rss表示>
    </div>
    </form>
</body>
</html>

 

詳しくは ITemplateでコントロールのプレゼンテーションを分離する(CodeZine) を読んで下さい。(他力本願ですいません…)

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。