CodeBehindeSupportクラス

StrutsのActionFormBeanみたいな感じにASP.NETでも使えると楽だなぁと思ってリフレクションの勉強も兼ねてこさえてみた。
(´・ω・`).oO(ていうか、ASP.NETでこんな機能はないのかしら?)


以下ソースコードなので略。


FormBean(便宜上の名前w)のプロパティとコントロールをマッチングするためのアトリビュートクラスを作成。

using System;

namespace AttributeSample
{
    /// <summary>
    /// カスタムアトリビュートクラス:BindControl
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class BindControlAttribute : Attribute
    {
        #region プロパティ
        private string _id;
        
        /// <summary>
        /// コントロールID
        /// </summary>
        public string id
        {
            get { return _id; }
        }
        #endregion

        #region コンストラクタ
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="id">コントロールID</param>
        public BindControlAttribute(string id)
        {
            _id = id;
        }
        #endregion
    }
}


テスト用の画面とそれに対応するFormBeanクラスを作る。
このときプロパティと対応するコントロールIDをひもづけるためにアトリビュートをつける。

using System;
using System.Text;

namespace AttributeSample
{
    public class SampleBean
    {
        #region プロパティ
        private string label1;
        private string textbox1;
        private string textbox2;
        private string dropdown1;

        [BindControl("Label1")]
        public string Label1
        {
            get { return label1; }
            set { label1 = value; }
        }
        [BindControl("TextBox1")]
        public string Text1
        {
            get { return textbox1; }
            set { textbox1 = value; }
        }
        [BindControl("TextBox2")]
        public string Text2
        {
            get { return textbox2; }
            set { textbox2 = value; }
        }
        [BindControl("DropDownList1")]
        public string DropDown1
        {
            get { return dropdown1; }
            set { dropdown1 = value; }
        }
        #endregion

        #region コンストラクタ
        public SampleBean() { }
        #endregion

        #region ToString()
        /// <summary>
        /// 文字列表現を返す
        /// </summary>
        /// <returns>文字列表現</returns>
        override public string ToString()
        {
            StringBuilder sb = new StringBuilder("SampleBean [");
            sb.Append("Label1=" + Label1 + ", ").Append("TextBox1=" + Text1 + ", ")
            .Append("TextBox2=" + Text2 + ", ").Append("TextBox3=" + Text3 + ", ")
            .Append("DropDownList1=" + DropDown1).Append("]");
            return sb.ToString();
        }
        #endregion
    }
}


CodeBehindクラスでリフレクションを使ってFormBeanをインスタンス化。
プロパティにコントロールの値をセットする。

using System;
using System.Data;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using System.Diagnostics;
using System.Reflection;

namespace AttributeSample
{
    abstract public class CodeBehindSupport : System.Web.UI.Page
    {
        #region 画面コントロールの値を保持したオブジェクトの取得処理
        /// <summary>
        /// classTypeで指定されたクラスにコントロールに設定された値を設定したオブジェクトを返す.
        /// classTypeのプロパティに[BindControl]属性が設定されていない場合には初期化されたオブジェクトを返す.
        /// </summary>
        /// <param name="classType">生成するクラス</param>
        /// <returns>生成したクラス</returns>
        protected object GetFormBean(Type classType)
        {
            object newInstance = null;

            // コントロール一覧の取得
            LoadPageControls();

            #region インスタンスの生成
            if ((newInstance = CreateInstance(classType)) == null)
            {
                return null;
            }
            #endregion

            // プロパティの取得
            foreach (PropertyInfo prop in classType.GetProperties())
            {
                Print("PropertyName=" + prop.Name);

                // アトリビュートの参照
                foreach (BindControlAttribute attr in 
                    prop.GetCustomAttributes(typeof(BindControlAttribute), false))
                {
                    if (PageControls.ContainsKey(attr.id))
                    {
                        Control c = PageControls[attr.id];
                        string value = null;

                        #region コントロール種別判定および値の取得
                        if (c is ITextControl)
                        {
                            value = ((ITextControl)c).Text;
                        }
                        else if (c is ListControl)
                        {
                            value = ((ListControl)c).SelectedValue;
                        }
                        else
                        {
                            Print("not 'ITextControl' -> [" + c.GetType() + "]");
                            continue;
                        }
                        #endregion

                        // TODO:フォーマットの適用

                        Print("Value=" + value);
                        prop.SetValue(newInstance, value, null);
                    }
                }
            }
            return newInstance;
        }
        #endregion

        #region インスタンス生成処理
        /// <summary>
        /// 新規インスタンスの生成
        /// </summary>
        /// <param name="classType">生成するクラス</param>
        /// <returns>作成したインスタンス</returns>
        private object CreateInstance(Type classType)
        {
            object obj = null;

            try
            {
                obj = Activator.CreateInstance(classType);
            }
            catch (Exception ex)
            {
                Print("インスタンス化に失敗。\n" + ex.StackTrace);
            }
            return obj;
        }
        #endregion
    }


(コントロール一覧取得するのは省略。取得した一覧はDictionaryに保持。)
こんな感じで完成。
ITextControlとListControl意外にももしかしたら分岐条件あるかも。
サンプルということで。
いつもどおり例外ハンドリングは手抜き。
Javaと違って、Type型からインスタンス化てできないのね。
ついJavaの感覚でType.newInstance()がないなぁと探してたorz


あと、課題としては取得したテキストのフォーマット対応したいところ。
これはICustomFormatterの実装クラスで何とかなりそう。


しかしアノテーションといいアトリビュートといい、覚えればなにげに便利だなぁ。