GridView 数据绑定扩展-- 动态添加数据列,绑定方法

      GridView 是ASP.NET中表格数据显示控件中的一种,可以支持数据绑定,绑定的数据源我们一般用实现IEnumerable<T>接口的对象,T可以是任何一个CLR类(当然还有一些其他的数据源格式),这些大家基本都很熟悉,但是最近碰到一个新的需求:

     像有一个类似于:   

     public   class  Book
    {
        
public   int  ID {  get set ; }

        
public   string  Name {  get set ; }

        
public  Dictionary < string object >  ExtendProperties {  get set ; }
    }

 

  其中ExtendProperties 是用来存储不同用户自定义的扩展属性的,用键值对的形式保存,Key保存扩展属性名,Value保存扩展属性值,每个用户可能的扩展属性个数和键值都不一样。

     现在,如果我们需要把List<Book>绑定在GridView上面,但是希望将ExtendProperties中的扩展属性也都显示出来,而且相应的Key显示在Column的Header,而Value则显示在相应的Cell中。

     起初想沿着GridView绑定一般数据的思路考虑,看是否能够通过动态属性的方式将ExtendProperties中的键值对匹配成相应的对象属性,(没有测试是否能够成功),但是又觉得数据绑定是控件的事情,这个不应该由数据本身来负责,那我们能否通过修改GridView本身的行为来达到这个目的呢?? 答案是肯定的!

     我们都知道,GridView本身是有一个AutoGenerateColumns的属性,如果设置为True的话,GridView将自动添加一些列,这些列对应Book中的每一个可绑定的属性,这里是用PropertyDescriptor来实现的(好像是考虑到DesignMode,而没有用Type来做反射)。根据这个思路,我们可以先看看AutoGenerateColumns到底对GridView有哪些影响以及GridView本身是如何生成Columns的。

     通过查看GridView的源码,我们可以看到几个重要的方法:

ExpandedBlockStart.gif 代码
protected   virtual  ICollection CreateColumns(PagedDataSource dataSource,  bool  useDataSource);

protected   virtual  AutoGeneratedField CreateAutoGeneratedColumn(AutoGeneratedFieldProperties fieldProperties);

 

    其中 CreateColumns方法是GridView用来生成列的,而在其中会根据AutoGenerateColumns属性来判断是否调用CreateAutoGenerateColumn来自动生成相应属性的列。而且这两个方法都是可以重载的,根据我们的需求,我们可以构建一个GridView的子类,重载 CreateColumns 方法,在 调用 base.CreateColumns的返回结果中插入我们希望生成的Column即可。

   代码如下:

ExpandedBlockStart.gif 代码
         #region  Properties

        
public   bool  AutoGenerateExtendPropertiesColumn {  get set ; }

        
public   int  AutoGenerateColumnsAfter {  get set ; }

        
public   string  ExtendPropertiesDataField {  get set ; }

        
#endregion
         
        
protected   override  ICollection CreateColumns(PagedDataSource dataSource,  bool  useDataSource)
        {
            ICollection collection 
=   base .CreateColumns(dataSource, useDataSource);            

            
if  (AutoGenerateExtendPropertiesColumn )
            {
                ArrayList list 
=   new  ArrayList();

                ICollection extendPropertiesCollection 
=  CreateExtendPropertiesColumns(dataSource, useDataSource);

                ArrayList list1 
=   new  ArrayList();
                
foreach  (var c  in  collection)
                {
                    list1.Add(c);
                }

                ArrayList list2 
=   new  ArrayList();
                
if  (extendPropertiesCollection  !=   null )
                {
                    
foreach  (var c  in  extendPropertiesCollection)
                    {
                        list2.Add(c);
                    }
                }

                
int  copyFrom  =  AutoGenerateColumnsAfter  <  list1.Count  ?  AutoGenerateColumnsAfter : list1.Count  -   1 ;
                copyFrom 
=  copyFrom  >=   0   ?  copyFrom :  - 1 ;

                
for  ( int  i  =   0 ; i  <=  copyFrom; i ++ )
                {
                    list.Add(list1[i]);
                }
                
                list.AddRange(list2.ToArray());

                
for  ( int  i  =  copyFrom  +   1 ; i  <  list1.Count; i ++ )
                {
                    list.Add(list1[i]);
                }
                
return  list;
            }
            
return  collection; 
        }

        
protected   virtual  ICollection CreateExtendPropertiesColumns(PagedDataSource dataSource,  bool  useDataSource);

 

 

 其中我添加了几个属性和一个新的方法:

ExpandedBlockStart.gif 代码
public   bool  AutoGenerateExtendPropertiesColumn {  get set ; }

public   int  AutoGenerateColumnsAfter {  get set ; }
 
public string ExtendPropertiesDataField { getset; }

protected virtual ICollection CreateExtendPropertiesColumns(PagedDataSource dataSource, bool useDataSource);

 

    AutoGenerateExtendPropertiesColumn是用来判断是否要生成扩展属性对应的Column,

    而 AutoGenerateColumnsAfter是用来判断将自动生成的Column应该从什么位置开始插入,

    ExtendPropertiesDataField 是用来指明在绑定的对象中,哪个属性是存储扩展属性的信息的,

    方法 CreateExtendPropertiesColumns是用来生成与ExtendProperties对应的Columns.

     这里只是简单的举个例子,如何通过重载 CreateColumns 方法来实现自己动态添加Column 的目的。

     现在我们看看如何实现 CreateExtendPropertiesColumns 方法的:

ExpandedBlockStart.gif 代码
         protected   virtual  ICollection CreateExtendPropertiesColumns(PagedDataSource dataSource,  bool  useDataSource)
        {            
            ArrayList array 
=   new  ArrayList();
            
if  (dataSource.DataSourceCount  >   0   &&   ! String.IsNullOrEmpty(ExtendPropertiesDataField))
            { 
                var enumerator 
=  dataSource.DataSource.GetEnumerator();
                enumerator.Reset();
                
if  (enumerator.MoveNext())
                {
                    
object  firstData  =  enumerator.Current;
                    
if  (firstData  !=   null )
                    {
                        PropertyDescriptor extendPropertiesDesc 
=  TypeDescriptor.GetProperties(firstData).Find(ExtendPropertiesDataField,  false );
                        
if  (extendPropertiesDesc  !=   null   &&   typeof (IEnumerable < KeyValuePair < string object >> ).IsAssignableFrom(extendPropertiesDesc.PropertyType))
                        {
                            IEnumerable
< KeyValuePair < string object >>  extendProperties  =  (IEnumerable < KeyValuePair < string object >> )extendPropertiesDesc.GetValue(firstData);
                            
if  (extendProperties  !=   null )
                            {
                                
foreach  (var exProperty  in  extendProperties)
                                {
                                    ExtBouldField bouldField 
=   new  ExtBouldField();
                                    bouldField.UseMethodBinding 
=   true ;
                                    bouldField.MethodName 
=   " GetExtendProperty " ;
                                    bouldField.MethodParam 
=  exProperty.Key;
                                    bouldField.HeaderText 
=  exProperty.Key;
                                    array.Add(bouldField);
                                }
                            }
                        }
                    }
                } 
            }
            
return  array;
        } 

 

      上面的代码很简单,就是根据扩展属性在对象中的属性名,获取相应的值,然后再根据扩展属性构建对应的Column,这里有一个新的自定义的 BouldField: ExtBouldField. 我们知道,一般的 BouldField 都是对对象的property进行绑定,而我们的数据存储在一个类型为 Dictionary<string,Object>的 ExtendProperties中,而且希望该Column的HeaderText是Key, Cell中显示的是Value,这个是系统的BouldField不能实现的,那我就自己实现了一个可以绑定方法的ExtBouldField (这里只是起到一个抛砖引玉的作用,功能很简单,指定一个 MethodName, 和一个暂时只能为String类型的MethodParam,这里只能为 String(或者其他基本类型),是为了能够让其在ASPX页面上直接进行设置,如果这些属性只是纯代码设置,那么MethodParam可以为任何类型).

     通过分析 BouldField 的源码,可以了解到 BouldField 对每一个 DataCell 的数据获取路径大概是这样的:

     InitializeCell() -> InitializeDataCell() -> OnDataBouldField() -> GetValue(),

     最后的数据获取途径是 GetValue() 方法,而且 GetValue() 方法也是可以重载的,那么我们可以重载 GetValue() 方法来实现按照自己的需求(通过绑定方法而不是绑定属性的形式)来获取DataCell的绑定数据。为了考虑到原来的基于对象属性的绑定形式还可以使用,我还对 InitializeDataCell() 方法做了一些稍微的修改,该 ExtBouldField 的详细代码如下:

 

ExpandedBlockStart.gif 代码
     public   class  ExtBouldField :BoundField
    {
        
#region  Ctor

        
public  ExtBouldField()
            : 
base ()
        {
            UseMethodBinding 
=   false ;
        }

        
#endregion

        
#region  Properties

        
public   bool  UseMethodBinding
        {
            
get ;
            
set ;
        }

        
public   string  MethodName
        {
            
get ;
            
set ;
        }

        
public   string  MethodParam
        {
            
get ;
            
set ;
        }

        
#endregion

        
#region  Overrided methods

        
protected   override   void  InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState)
        { 
            Control child 
=   null ;
            Control control2 
=   null ;
            
bool  needBinding  =  ( ! UseMethodBinding  &&   ! String.IsNullOrEmpty(DataField))
                             
|| (UseMethodBinding  &&   ! String.IsNullOrEmpty(MethodName));

            
if  ((((rowState  &  DataControlRowState.Edit)  !=  DataControlRowState.Normal)  &&   ! this .ReadOnly)  ||  ((rowState  &  DataControlRowState.Insert)  !=  DataControlRowState.Normal))
            {
                TextBox box 
=   new  TextBox();
                box.ToolTip 
=   this .HeaderText;
                child 
=  box;
                
if  ((needBinding)  &&  ((rowState  &  DataControlRowState.Edit)  !=  DataControlRowState.Normal))
                {
                    control2 
=  box;
                }
            }
            
else   if  (needBinding)
            {
                control2 
=  cell;
            }
            
if  (child  !=   null )
            {
                cell.Controls.Add(child);
            }
            
if  ((control2  !=   null &&   base .Visible)
            {
                control2.DataBinding 
+=   new  EventHandler( this .OnDataBindField);
            }
        }

        
protected   override   object  GetValue(Control controlContainer)
        { 
            
if  (UseMethodBinding)
            {
                
if  (String.IsNullOrEmpty(MethodName))
                {
                    
throw   new  HttpException( " DataItem No MethodName " );
                }

                
object  component  =   null
                
if  (controlContainer  ==   null )
                {
                    
throw   new  HttpException( " DataControlField_NoContainer " );
                }
                component 
=  DataBinder.GetDataItem(controlContainer);

                
if  (component  !=   null )
                {
                    MethodInfo bindingMethodInfo 
=  component.GetType().GetMethod(MethodName);
                    
if  (bindingMethodInfo  ==   null )
                    {
                        
throw   new  HttpException(String.Format( " Not Found the Method:{0} " , MethodName));
                    }
                     
                    
return  bindingMethodInfo.Invoke(component,  new   object [] { MethodParam });
                }
                 
                
return  component;
            }

            
return   base .GetValue(controlContainer);
        }

        
#endregion

    }

 

      其中:

      UsingMethodBinding 用来指明是用方法绑定还是原来的基于属性的绑定;

      MethodName 用来指明通过绑定对象的哪个方法来获取绑定数据;

      MethodParam 用来指明方法的参数(如果是代码设置该属性的话,可以让他为任何类型,但是这里只是用string,为了使ASPX页面也能直接设置该参数的值)。

 

      到这里,原来碰到的问题都解决了:

  1. 通过继承 GridView 并且重载  CreateColumns() 添加自己想要的 Column,根据扩展属性中的键值对生成Column。
  2. 通过继承 BouldField,并且重载 InitializeDataCell() 和 GetValue() 实现自己希望的绑定方法,实现可以基于方法的数据绑定,从而可以将扩展属性中的键值绑定到对应的Cell中。

   上面的例子只有考虑第一次加载的情况,如果页面有PostBack,则还需要改进一下,这个需要谢谢geass..,由于原来的目的只是做这个扩展属性的绑定是否可行性研究,所以存在一些bug。 不过在新的可下载的代码中,这个由PostBack产生的问题已经解决了,具体的可见代码。

 ExtendPropertiesBindingForGridView.rar

转载于:https://www.cnblogs.com/bmwchampion/archive/2011/02/14/GridViewDataBinding.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用WPF MVVM中的Binding和Converter来实现这个功能。以下是一个简单的示例: 1.在你的GridView中,设置两个,第一个绑定到你的数据源的第一列,第二个绑定到你的数据源的第二。 2.为第二个设置一个Converter,该Converter将第二个的第一个数据设置为第一列的上一个数据。 下面是一些示例代码: 在GridView中设置两个: ``` <ListView ItemsSource="{Binding MyData}"> <ListView.View> <GridView> <GridViewColumn Header="Column 1" DisplayMemberBinding="{Binding Column1}" /> <GridViewColumn Header="Column 2" DisplayMemberBinding="{Binding Column2, Converter={StaticResource MyConverter}}" /> </GridView> </ListView.View> </ListView> ``` 设置Converter: ``` public class MyConverter : IValueConverter { private object _lastValue; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) { _lastValue = null; return null; } if (_lastValue == null) { // If this is the first value, return null. _lastValue = value; return null; } // Set the first value of the second column to be the last value of the first column. var result = _lastValue; _lastValue = value; return result; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } ``` 在这个Converter中,我们使用了一个私有变量 _lastValue 来存储上一个值。在 Convert 方法中,我们首先检查当前值是否为 null,如果是,则将 _lastValue 设置为 null 并返回 null。如果当前值不是 null,我们再检查 _lastValue 是否为 null,如果是,则将 _lastValue 设置为当前值并返回 null。如果 _lastValue 不是 null,我们将第二的第一个值设置为 _lastValue,并将 _lastValue 设置为当前值。最后,返回 _lastValue 作为第二的第一个值。 在这个示例中,我们使用了一个ListView和一个GridView来显示数据,但您也可以使用其他控件来实现相同的效果。此外,请注意,Converter中的代码非常简单,您可以根据您的需求进行修改和扩展

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值