创建一个具有完整设计时(design time)支持的自定义数据源控件(DataSourceControl)

原文:Creating a custom DataSourceControl with full design time support

 

Download source files - 13.5 Kb

Download demo project - 26.8 Kb

介绍
这篇文章展示了如何创建一个自定义的数据源控件和怎样给它增加完整的设计时支持

背景
这篇文章假定你已经很熟悉数据源控件(DataSourceControl)和你知道设计时基础工作原理。如果你对这些知识不了解,关注一下下面的文章。

关于数据源控件

  • Data Source Controls - Under the hood (1/4)
  • Data Source Controls - Under the hood (2/4)
  • Data Source Controls - Under the hood (3/4)
  • Data Source Controls - Under the hood (4/4)
    关于设计时的基础知识
  • Introduction to designers
  • ASP.NET designers. The ControlDesigner class
  • ASP.NET designers. The DataSourceDesigner class

    创建自定义的数据源控件

    我们将要编写的数据源是只可以取得数据,但是不能编辑数据。它只支持SELECT操作。它很像ObjectDataSource,但是只是返回数据。它包含一个TypeName属性,用来保存一个类名,包含一个SelectMethod属性,用来保存类中可以调用的方法。为了避免写过多的代码,我们只找出类中的静态方法。我们还有一个参数集合来传递SelectMethod(SelectParameters).在创建数据源控件的同时我将解释将执行的主要任务,但我不详细解释某方法或者某属性具体做什么。

    要做的第一件事情是当实现一个DataSourceControl时,我们要选择多少DataSourceViews和如何编写与IDataSource有关的方法。在这个例子中,我们只有一个View:

    public class CustomDataSource : DataSourceControl
    {
     
        
    protected static readonly string[] _views = "DefaultView" };
     
        
    protected CustomDataSourceView _view;
     
     
        
    protected override DataSourceView GetView(string viewName)
        
    {
            
    if ((viewName == null|| ((viewName.Length != 0&& 
                (String.Compare(viewName, 
    "DefaultView"
                StringComparison.OrdinalIgnoreCase) 
    != 0))) 
            
    {
                
    throw new ArgumentException("An invalid view was requested"
                    
    "viewName");
            }

     
            
    return View;
        }

     
        
    protected override ICollection GetViewNames()
        
    {
            
    return _views;
        }

     
        
    protected CustomDataSourceView View
        
    {
            
    get
            
    {
                
    if (_view == null{
                    _view 
    = new CustomDataSourceView(this, _views[0]);
                    
    if (base.IsTrackingViewState) {
                        ((IStateManager)_view).TrackViewState();
                    }

                }

                
    return _view;
            }

        }

    }

     

    CustomDataSource类作为完成我们全部工作的类,最好我们把需要的属性存储在这里。但是我们需要暴露这些属性在CustomDataSource好让用户去编辑他们在属性编辑器中。因此,我们必须在CustomDataSource类中也添加。

    [Category("Data"), DefaultValue("")]
    public string TypeName
    {
        
    get return View.TypeName; }
        
    set { View.TypeName = value; }
    }

     
    [Category(
    "Data"), DefaultValue("")]
    public string SelectMethod
    {
        
    get return View.SelectMethod; }
        
    set { View.SelectMethod = value; }
    }

     
    [PersistenceMode(PersistenceMode.InnerProperty), Category(
    "Data"), 
        DefaultValue((
    string)null), MergableProperty(false), 
        Editor(
    typeof(ParameterCollectionEditor), 
        
    typeof(UITypeEditor))]
    public ParameterCollection SelectParameters
    {
        
    get return View.SelectParameters; }
     }

    添加这些到CustomDataSouceView类中

    public class CustomDataSourceView : DataSourceView, IStateManager
    {
        
    protected bool _tracking;
        
    protected CustomDataSource _owner;
        
    protected string _typeName;
        
    protected string _selectMethod;
        
    protected ParameterCollection _selectParameters;
     
        
    public string TypeName
        
    {
            
    get
            
    {
                
    if (_typeName == null{
                    
    return String.Empty;
                }

                
    return _typeName;
            }

            
    set
            
    {
                
    if (TypeName != value) {
                    _typeName 
    = value;
                    OnDataSourceViewChanged(EventArgs.Empty);
                }

            }

        }

     
        
    public string SelectMethod
        
    {
            
    get
            
    {
                
    if (_selectMethod == null{
                    
    return String.Empty;
                }

                
    return _selectMethod;
            }

            
    set
            
    {
                
    if (SelectMethod != value) {
                    _selectMethod 
    = value;
                    OnDataSourceViewChanged(EventArgs.Empty);
                }

            }

        }

     
        
    public ParameterCollection SelectParameters
        
    {
            
    get
            
    {
                
    if (_selectParameters == null
                
    {
                    _selectParameters 
    = new ParameterCollection();
                        _selectParameters.ParametersChanged 
    += 
                    
    new EventHandler(ParametersChangedEventHandler);
                    
    if (_tracking) 
                    
    {
                        ((IStateManager)_selectParameters).TrackViewState();
                    }

                }

                
    return _selectParameters;
            }

        }

     
        
    protected void ParametersChangedEventHandler(object o, EventArgs e)
        
    {
            OnDataSourceViewChanged(EventArgs.Empty);
        }

     
        
    public CustomDataSourceView(CustomDataSource owner, string name)
            : 
    base(owner, name)
        
    {
            _owner 
    = owner;
        }

     }

     

     

    注意,当属性改变的时候,OnDataSourceViewChanged 方法被调用去强制重新绑定。同时注意到CustomDataSourceView类实现了IStateManager接口,可以支持定制View 状态管理。既然这样,我们用它去存储SelectParameters。状态管理在CustomDataSource类中这样写:

    protected override void LoadViewState(object savedState)
    {
        Pair previousState 
    = (Pair) savedState;

        
    if (savedState == null
        
    {
            
    base.LoadViewState(null);
        }
     
        
    else 
        
    {
            
    base.LoadViewState(previousState.First);
     
            
    if (previousState.Second != null
            
    {
                ((IStateManager) View).LoadViewState(previousState.Second);
            }

        }

    }

     
    protected override object SaveViewState()
    {
        Pair currentState 
    = new Pair();
     
        currentState.First 
    = base.SaveViewState();
     
        
    if (_view != null
        
    {
            currentState.Second 
    = ((IStateManager) View).SaveViewState();
        }

     
        
    if ((currentState.First == null&& (currentState.Second == null)) 
        
    {
            
    return null;
        }

     
            
    return currentState;
    }

     
    protected override void TrackViewState()
    {
        
    base.TrackViewState();
     
        
    if (_view != null
        
    {
            ((IStateManager) View).TrackViewState();
        }

    }

     
    protected override void OnInit(EventArgs e)
    {
        
    base.OnInit(e);
     
        
    // handle the LoadComplete event to update select parameters
        if (Page != null
        
    {
            Page.LoadComplete 
    += new EventHandler(UpdateParameterValues);
        }

    }

    我们用Pair去存储状态,第一个对象用来存取父视图状态,第二个对象用来存储当前视图的视图状态。关于CustomDataSouceView,状态管理这样写:

    bool IStateManager.IsTrackingViewState
    {
        
    get    return _tracking; }
    }

     
    void IStateManager.LoadViewState(object savedState)
    {
        LoadViewState(savedState);
    }

     
    object IStateManager.SaveViewState()
    {
        
    return SaveViewState();
    }

     
    void IStateManager.TrackViewState()
    {
        TrackViewState();
    }

     
    protected virtual void LoadViewState(object savedState)
    {
        
    if (savedState != null
        
    {
            
    if (savedState != null)
            
    {
                ((IStateManager)SelectParameters).LoadViewState(savedState);
            }

        }

    }

     
    protected virtual object SaveViewState()
    {
        
    if (_selectParameters != null)
        
    {
            
    return ((IStateManager)_selectParameters).SaveViewState();
        }
     
        
    else 
        
    {
            
    return null;
        }

    }

     
    protected virtual void TrackViewState()
    {
        _tracking 
    = true;
     
        
    if (_selectParameters != null)    
        
    {
            ((IStateManager)_selectParameters).TrackViewState();
        }

     }

    我们必须求出SelectParameters的值在每一次请求,因为如果参数改变,我们被需重新绑定:

    protected override void OnInit(EventArgs e)
    {
        
    base.OnInit(e);
     
        
    // handle the LoadComplete event to update select parameters
        if (Page != null
        
    {
            Page.LoadComplete 
    += new EventHandler(UpdateParameterValues);
        }

    }

     
    protected virtual void UpdateParameterValues(object sender, EventArgs e)
    {
        SelectParameters.UpdateValues(Context, 
    this);
    }

    最后我们要做的就是完成在CustomDataSourceView确切的选取部分:

    <PRE lang=cs id=pre6 style="MARGIN-TOP: 0px">protected override IEnumerable ExecuteSelect(
        DataSourceSelectArguments arguments)
    {
        
    // if there isn't a select method, error
        if (SelectMethod.Length == 0
        
    {
            
    throw new InvalidOperationException(