ASP.NETステップアップ(7):コントロールを認識するDataBind

13888 ワード

データバインド、databind()はどんなに親切な方法なのか、私たちは毎日何百回も叩くことができません.しかし、彼がどのように実現したか考えたことがありますか?有木有!!!(咆哮体:)今日、私たちはRepeaterを持ってDataBindのことを話します.もし友達が私が書いたテンプレートエンジンのLabelの初級アプリケーションを見たことがあるならば、私は最後にリストのLabelの実現を貼って、その中で少し面白いのはRepeaterを真似します.でも見たことがなくてもいいです.今日はマイクロソフトのまじめなRepeaterを見てみましょう.一般的なコントロールのバインドはPage_ですLoadの事件の方法の中でこのように書きます
if(!IsPostBack) 
{ 
    BindList(); 
} 

BindList法は我々がコントロールをバインドするための方法であり,独立した目的はこの方法を多く使用することである.明らかに、私たちは一般的にこのようにバインドされています.
public void BindList() 
{ 
    lst.DataSource = DataHelper.GetList(); 
    lst.DataBind(); 
}

では、私たちが書いたコードに基づいて、バインドの真相を見てみましょう.
最初のステップは、DataSourceメンバーに値を割り当て、値を割り当てるときに何が起こったかを見ることです.
public virtual object DataSource 
{ 
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] 
    get 
    { 
        return this.dataSource; 
    } 
    set 
    { 
        if (((value != null) && !(value is IListSource)) && !(value is IEnumerable)) 
        { 
            throw new ArgumentException(SR.GetString("Invalid_DataSource_Type", new object[] { this.ID })); 
        } 
        this.dataSource = value; 
        this.OnDataPropertyChanged(); 
    } 
}

 
上記の方法から、DataSourceは本当に簡単ではないことがわかります.1つの付与で多くのことをしましたが、実はgetとsetは2つの方法(get_dataSource、set_dataSource)で、C#に書いたほうがきれいです.
我々が指定したdatasourceがIEnumerable(反復可能)ではなく、IListSourceタイプではない場合、例外(パラメータエラー:無効なデータソース)を投げ出し、IListSourceを必ず使用する方法を説明し、ループして使用する(Repeater肯定的なループでしょう).
次にOnDataPropertyChanged()のメソッドを実行します.名前を見るとこの方法が地下党だと推測できます.彼はクラスの一部のメンバーに「おい、データソースが変わったから、各部門に注意しなさい」と言います.この时Repeaterがデータを取得する时、この地下党に闻きます:“兄弟达、今风は大丈夫です...“
protected virtual void OnDataPropertyChanged() 
{ 
    if (this._throwOnDataPropertyChange) 
    { 
        throw new HttpException(SR.GetString("DataBoundControl_InvalidDataPropertyChange", new object[] { this.ID })); 
    } 
    if (this._inited) 
    { 
        this.RequiresDataBinding = true; 
    } 
    this._currentViewValid = false; 
} 

1番目の文が異常を投げ出すのは「内部組織」で、人を殺して口を滅ぼすことを決めて、まったくチャンスがありません.2番目の文、_inited、初期化したことがありますか、_currentViewValid、現在のDataViewが検証したことがあるかどうかという意味です.(このデータソースは不明ですね)
2つ目はDataBind()を実行すると、DataBindは上の「地下組織」を使用します.
public override void DataBind() 
{ 
    if ((!this.IsBoundUsingDataSourceID || !base.DesignMode) || (base.Site != null)) 
    { 
        this.RequiresDataBinding = false; 
        this.OnDataBinding(EventArgs.Empty); 
    } 
} 

つまり、DataSourceIDを使用してバインドしますか?明らかに私たちはそうではありません.私たちが直接与えたデータソースです.DataSourceIDが指定されている場合、Render RepeaterではDataSourceIDという名前のDataSourceコントロールが検索されます.これは、フロントがデータソースを指定する方法です.しかし、最終的にはDataBindを実行します.RepeaterのOnDataBindingを見てみましょう
protected override void OnDataBinding(EventArgs e) 
{ 
    base.OnDataBinding(e);//     Control OnDataBinding   
    this.Controls.Clear();//      
    base.ClearChildViewState();//      ViewState   
    this.CreateControlHierarchy(true);//      
    base.ChildControlsCreated = true;//      
} 

親のOnDataBingdingイベントは簡単です.
protected virtual void OnDataBinding(EventArgs e) 
{ 
    if (this.HasEvents()) 
    { 
        EventHandler handler = this._events[EventDataBinding] as EventHandler; 
        if (handler != null) 
        { 
            handler(this, e); 
        } 
    } 
}

 
DataBingdingイベントメソッドがサブスクリプションされているかどうかを確認することです.サブスクリプションは実行され、RepeaterのItemTemplateサブコントロールにデータをバインドします.
空の私たちは見ないで、すべていくつかremoveです.CreateControlHierarchyを見てみましょう
/// <summary> 
///       
/// </summary> 
/// <param name="useDataSource">       </param> 
protected virtual void CreateControlHierarchy(bool useDataSource) 
{ 
    IEnumerable data = null;//     
    int dataItemCount = -1;//      
    if (this.itemsArray != null) 
    { 
        this.itemsArray.Clear();//       ,     
    } 
    else 
    { 
        this.itemsArray = new ArrayList();//         
    } 
    if (!useDataSource)//         ,    ViewState      ,       
    { 
        dataItemCount = (int) this.ViewState["_!ItemCount"]; 
        if (dataItemCount != -1) 
        { 
            data = new DummyDataSource(dataItemCount); 
            this.itemsArray.Capacity = dataItemCount; 
        } 
    } 
    else 
    { 
        data = this.GetData();//        ,      
        ICollection is2 = data as ICollection; 
        if (is2 != null) 
        { 
            this.itemsArray.Capacity = is2.Count; 
        } 
    } 
    if (data != null)//       
    { 
        int itemIndex = 0; 
        bool flag = this.separatorTemplate != null;//         
        dataItemCount = 0; 
        if (this.headerTemplate != null)//     ,      
        { 
            this.CreateItem(-1, ListItemType.Header, useDataSource, null);//   -1     ,     ,     ,            null... 
        } 
        foreach (object obj2 in data)//     
        { 
            if (flag && (dataItemCount > 0)) 
            { 
                this.CreateItem(itemIndex - 1, ListItemType.Separator, useDataSource, null);//    
            } 
            ListItemType itemType = ((itemIndex % 2) == 0) ? ListItemType.Item : ListItemType.AlternatingItem; 
            RepeaterItem item = this.CreateItem(itemIndex, itemType, useDataSource, obj2);//Item Alter     
            this.itemsArray.Add(item); 
            dataItemCount++; 
            itemIndex++; 
        } 
        if (this.footerTemplate != null) 
        { 
            this.CreateItem(-1, ListItemType.Footer, useDataSource, null);//     
        } 
    } 
    if (useDataSource) 
    { 
        this.ViewState["_!ItemCount"] = (data != null) ? dataItemCount : -1;// ViewState ItemCount   
    } 
}

上記のコメントから、Itemをループして作成することがわかります.これらのItemは、私たちがよく使うHeadItemTemplate ItemTemplateなどのサブコントロールです.データを彼らに伝え、自分でデータバインドを実現させます.ところで、私のモデルエンジンもそうしましたよ:)、しかし、私は本当に彼をパクリしていません.ははは.
GetDataメソッドは、データを取得することです.
protected virtual IEnumerable GetData()
{
    DataSourceView view = this.ConnectToDataSourceView();
    if (view != null)
    {
        return view.ExecuteSelect(this.SelectArguments);
    }
    return null;
}

ConnectToDataSourceView()メソッドが長い主な作業は、データ・ソースを取得することです.
private DataSourceView ConnectToDataSourceView()
{
    if (!this._currentViewValid || base.DesignMode)//        ,  DataSource    _currentViewValid  = false
    {
        if ((this._currentView != null) && this._currentViewIsFromDataSourceID)
        {
            this._currentView.DataSourceViewChanged -= new EventHandler(this.OnDataSourceViewChanged);
        }
        IDataSource source = null;
        string dataSourceID = this.DataSourceID;
        if (dataSourceID.Length != 0)//      DataSourceID    , FindControl      DataSource  
        {
            Control control = DataBoundControlHelper.FindControl(this, dataSourceID);
            if (control == null)
            {
                throw new HttpException(SR.GetString("DataControl_DataSourceDoesntExist", new object[] { this.ID, dataSourceID }));
            }
            source = control as IDataSource;
            if (source == null)
            {
                throw new HttpException(SR.GetString("DataControl_DataSourceIDMustBeDataControl", new object[] { this.ID, dataSourceID }));
            }
        }
        if (source == null)//           ,          
        {
            source = new ReadOnlyDataSource(this.DataSource, this.DataMember);
        }
        else if (this.DataSource != null)//               
        {
            throw new InvalidOperationException(SR.GetString("DataControl_MultipleDataSources", new object[] { this.ID }));
        }
        DataSourceView view = source.GetView(this.DataMember);
        if (view == null)
        {
            throw new InvalidOperationException(SR.GetString("DataControl_ViewNotFound", new object[] { this.ID }));
        }
        this._currentViewIsFromDataSourceID = this.IsBoundUsingDataSourceID;
        this._currentView = view;
        if ((this._currentView != null) && this._currentViewIsFromDataSourceID)
        {
            this._currentView.DataSourceViewChanged += new EventHandler(this.OnDataSourceViewChanged);
        }
        this._currentViewValid = true;
    }
    return this._currentView;
}

実際のデータがどこにあるかを示すため、CreateItemメソッドに注目します.ここでは大体のフレームワークを作成するだけです.
/// <summary> 
///   Repeater Item 
/// </summary> 
/// <param name="itemIndex">   </param> 
/// <param name="itemType">Item   </param> 
/// <param name="dataBind">      </param> 
/// <param name="dataItem">    </param> 
private RepeaterItem CreateItem(int itemIndex, ListItemType itemType, bool dataBind, object dataItem) 
{ 
    RepeaterItem item = this.CreateItem(itemIndex, itemType);//    Item 
    RepeaterItemEventArgs e = new RepeaterItemEventArgs(item);//        
    this.InitializeItem(item);// repeater   template   
    if (dataBind) 
    { 
        item.DataItem = dataItem;//              DataItem   
    } 
    this.OnItemCreated(e);//    Item   (           ) 
    this.Controls.Add(item);//  Item 
    if (dataBind) 
    { 
        item.DataBind();//  ,       ~ 
        this.OnItemDataBound(e);//           
        item.DataItem = null;//    ,       
    } 
    return item; 
} 

ItemのデータはDataBinderですEval(「xxx」)では反射されるオブジェクトが必要です.つまり、各コントロールの現在のデータ、はい、各コントロール、さっきrepeaterのitemが山積みになっているのを見ました.の
DataBinderのEvalは静的な方法であり,コードは非常に簡単で,反射によってDataItemの値を取得することである.彼の実現を見ることができます
    public static object Eval(object container, string expression) 
    { 
        if (expression == null) 
        { 
            throw new ArgumentNullException("expression"); 
        } 
        expression = expression.Trim(); 
        if (expression.Length == 0) 
        { 
            throw new ArgumentNullException("expression"); 
        } 
        if (container == null) 
        { 
            return null; 
        } 
        string[] expressionParts = expression.Split(expressionPartSeparator); 
        return Eval(container, expressionParts); 
    }

    private static object Eval(object container, string[] expressionParts) 
    { 
        object propertyValue = container; 
        for (int i = 0; (i < expressionParts.Length) && (propertyValue != null); i++) 
        { 
            string propName = expressionParts[i]; 
            if (propName.IndexOfAny(indexExprStartChars) < 0) 
            { 
                propertyValue = GetPropertyValue(propertyValue, propName); 
            } 
            else 
            { 
                propertyValue = GetIndexedPropertyValue(propertyValue, propName); 
            } 
        } 
        return propertyValue; 
    }

    public static string Eval(object container, string expression, string format) 
    { 
        object obj2 = Eval(container, expression); 
        if ((obj2 == null) || (obj2 == DBNull.Value)) 
        { 
            return string.Empty; 
        } 
        if (string.IsNullOrEmpty(format)) 
        { 
            return obj2.ToString(); 
        } 
        return string.Format(format, obj2); 
    }

    public static object GetDataItem(object container) 
    { 
        bool flag; 
        return GetDataItem(container, out flag); 
    }

RepeaterItemのDataBindを見てみると、実はControlクラスのDataBindです
public virtual void DataBind() 
{ 
    this.DataBind(true); 
} 
protected virtual void DataBind(bool raiseOnDataBinding) 
{ 
    bool flag = false; 
    if (this.IsBindingContainer)//        
    { 
        bool flag2; 
        object dataItem = DataBinder.GetDataItem(this, out flag2);//      DataItem,       ,            ,       。 
        if (flag2 && (this.Page != null))//        ,    Page  null 
        { 
            this.Page.PushDataBindingContext(dataItem);//          ,             Page          ,      Page.GetDataItem()  ,     Page Eval    ,               ?       :) 
            flag = true; 
        } 
    } 
    try 
    { 
        if (raiseOnDataBinding) 
        { 
            this.OnDataBinding(EventArgs.Empty); 
        } 
        this.DataBindChildren();//      
    } 
    finally 
    { 
        if (flag) 
        { 
            this.Page.PopDataBindingContext();//      ,       
        } 
    } 
} 

サブコントロールのバインドは実際にはプロセス全体の再帰であり、どのサブコントロールがあっても、サブコントロールにDataBind()を一度来させています.方法は以下の通りです.
protected virtual void DataBindChildren() 
{ 
    if (this.HasControls()) 
    { 
        string errorMsg = this._controls.SetCollectionReadOnly("Parent_collections_readonly"); 
        try 
        { 
            try 
            { 
                int count = this._controls.Count; 
                for (int i = 0; i < count; i++) 
                { 
                    this._controls[i].DataBind(); 
                } 
            } 
            finally 
            { 
                this._controls.SetCollectionReadOnly(errorMsg); 
            } 
        } 
        catch 
        { 
            throw; 
        } 
    } 
} 

このように、バインドプロセス全体がこのようにして、Repeaterの表現は実は彼のItemのすべての表現です.
CMSモデルエンジンの同級生を見て、ここではネストについて多く紹介します.私のCMSテンプレートエンジンはネストラベルのブロックを作っていません.もしそうすれば、この方法も似ていますが、この方法は現在のDataItemをサブコントロールに渡していません.私たちはItemDataBoundedイベントメソッドでサブコントロールのdataSourceに値を割り当てるからです.しかし、テンプレートエンジンではカスタマイズ方法ができないので、DateItemを渡さなければなりません.
プログラミングが面白いと感じるのは、时にはあなたが考えていることがあって、他の人がすでに実現していることを発見して、しかもするのは異常に強くて、しかしこれも自分の成長の1つの過程でしょう.WebFormのソースコードを分析したのも、CMSモデルを書いた後、WebFormに似ているものを発見したからだ.ただ、彼ほど膨大ではないが、不思議に似ている考えもある.繰り返し車輪を作ることで、何かを理解しやすくなるかもしれません.コードの組み立てを1つだけするのは面白くありません.あなたはどう言いますか.