近来做项目遇到一个问题:需要将某个类的列表通过DataGrid进行展示,按照常规的做法,假设类名为T,T中实现如下:
class T
{
private string _a;
public string A
{
get{return _a;}
set{_a = value;}
}
}
列表对象为
List<T> TList= new List<T>();
DataGrid需要设定好列以及所需要绑定的属性名A,代码实现如下:
System.Windows.Controls.DataGrid dataGrid = new System.Windows.Controls.DataGrid();
System.Windows.Controls.DataGridTextColumn aColumn = new DataGridTextColumn();
aColumn.Header = "A";
aColumn.Binding = new System.Windows.Data.Binding("A");//绑定A
dataGrid .Columns.Add(aColumn);
再通过
dataGrid.ItemSource = TList;
即可。
但是,如果dataGrid表格中需要展示的列(包括列名和数量)只有在运行时才能确定,而在编译期并不存在,那么该如何实现呢?
比如,T中的属性数量和具体内容会根据用户实现的输入日期范围进行确定,日期范围如果是2017.1.1到2017.1.10,那么T中的属性就会有10个,并且画面上应该展示出10列分为为2017.1.1,2017.1.2...2017.1.10。日期范围如果是2017.1.1至2017.1.31,阿么T中的属性就有31个,画面上应该展示出31列数据。由此可见,类的属性在代码编写期根本就不知道,那么该如何进行实现呢?
我的解决方案就是使用动态对象DynamicObject。具体含义及固定数量列的用法可自行百度。以下实现提供了更为复杂场景下的解决方案:类中的属性值又需要依赖于其他对象——初值取自指定对象,当指定对象变化时,该类中的值也发生变化并自动刷新画面数据。
将T实现代码重新改装如下:
基础类M_DoubleValue实现:
/// <summary>
/// 自定义的数值类型,用以绑定和被绑定
/// </summary>
public class M_DoubleValue: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string ID = "";
public readonly static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(M_DoubleValue),new PropertyMetadata(OnValueChanged));
public static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
M_DoubleValue valueObj = d as M_DoubleValue;
if (valueObj.PropertyChanged != null)
valueObj.PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
public double Value
{
set
{
this.SetValue(M_DoubleValue.ValueProperty, value);
PC("Value");
}
get { return (double)this.GetValue(M_DoubleValue.ValueProperty); }
}
private Binding _bindingValue;
public Binding BindingValue
{
get
{
if(_bindingValue == null)
{
_bindingValue = new Binding("Value") { Source = this,NotifyOnTargetUpdated = true,NotifyOnSourceUpdated = true };
}
return _bindingValue;
}
}
}
类T实现:
/// <summary>
/// T类型
/// </summary>
public class T: DynamicObject, INotifyPropertyChanged
{
Dictionary<string, M_DoubleValue> Properties = new Dictionary<string, M_DoubleValue>();
public event PropertyChangedEventHandler PropertyChanged;
protected void PC(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public void SetMemberBinding(string dateStr, Binding binding)
{
M_DoubleValue value = new M_DoubleValue();
value.Key = dateStr;
value.SetBinding(M_DoubleValue.ValueProperty, binding);
//刷新Grid方式
value.PropertyChanged += value_PropertyChanged;
Properties[date.Value] = value;
}
//当value值更新时,通知的是绑定到列上的ID
void value_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PC((sender as M_DoubleValue).ID);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
M_DoubleValue value;
Properties.TryGetValue(binder.Name, out value);
result = value.Value;
return true;
}
}
客户端调用:
System.Windows.Controls.DataGrid datagrid = new System.Windows.Controls.DataGrid();
datagrid .AutoGenerateColumns = false;
List<T> TList= new List<T>();
//this.AreaPlan.UnitList业务对象
foreach(M_Unit unit in this.AreaPlan.UnitList)
{
T t= new T();
//遍历日期,形成动态列
foreach (M_Date date in this.AreaPlan.PlanCalendar)
{
t.SetMemberBinding(date.Value, unit.Produce.ProduceCalendar[date].BindingPlanProduction);
}
TList.Add(t);
}
foreach(M_Date date in this.AreaPlan.PlanCalendar)
{
System.Windows.Controls.DataGridTextColumn dateColumn = new DataGridTextColumn();
dateColumn.Header = date.ValueDT.Month + "/" + date.ValueDT.Day;
dateColumn.Binding = new System.Windows.Data.Binding(date.Value) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged};
dataGrid.Columns.Add(dateColumn);
}
dataGrid.ItemsSource = TList;
如此即可显示指定日期范围内的数据,并可与业务数据源绑定,保证画面数据的实时性。