如何设置从代码到可能尚不存在的列表成员的WPF绑定
本文关键字:不存在 列表 绑定 WPF 成员 何设置 设置 代码 | 更新日期: 2023-09-27 18:03:07
请允许我首先介绍一下我正在使用的类:
public class Assessment
{
public List<NormDefinition> Definitions {get;set;}
public List<Parameter> Parameters {get;set;}
}
public class NormDefinition
{
public string ColumnName {get;set;}
}
public class Parameter
{
public string Name {get;set;}
public List<NormValue> Norms {get;set;}
}
public class NormValue
{
public NormDefinition Definition {get;set;}
public string Value {get;set;}
}
用户正在向导中导航,必须完成一些任务才能创建一个评估。
第一步是定义一个或多个normdefinition。它有比上面显示的更多的设置,但其他的与问题无关。
第二步是定义一个或多个parameter。但是,用户还可以为前面定义的NormDefinitions添加一些值。我试图通过创建一个具有ItemsSource ={绑定评估的DataGrid来做到这一点。,所以我知道每个绑定的源是绑定到该行的Parameter对象。当第二个屏幕获得焦点时,一些代码为每个NormDefinition添加一列,用户可以在其中填写值。列的创建如下所示:
private void CreateNormDefinitionColumns()
{
foreach (var definition in Assessment.Definitions)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = definition.ColumnName;
column.Binding = new Binding()
{
// Add a binding to:
// Parameter.Norms.Single(norm => norm.Definition == definition).Value;
}
ParameterDataGrid.Columns.Add(column);
}
}
请给我一个解决方案来设置这个绑定。
更进一步,在. single()不返回结果的情况下,我希望创建NormValue对象并将其添加到参数中。
对于这种情况,我使用GenericRow和GenericTable类:
public class GenericRow : CustomTypeDescriptor, INotifyPropertyChanged
{
#region Private Fields
List<PropertyDescriptor> _property_list = new List<PropertyDescriptor>();
#endregion
#region INotifyPropertyChange Implementation
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChange Implementation
#region Public Methods
public void SetPropertyValue<T>(string propertyName, T propertyValue)
{
var properties = this.GetProperties()
.Cast<PropertyDescriptor>()
.Where(prop => prop.Name.Equals(propertyName));
if (properties == null || properties.Count() != 1)
{
throw new Exception("The property doesn't exist.");
}
var property = properties.First();
property.SetValue(this, propertyValue);
OnPropertyChanged(propertyName);
}
public T GetPropertyValue<T>(string propertyName)
{
var properties = this.GetProperties()
.Cast<PropertyDescriptor>()
.Where(prop => prop.Name.Equals(propertyName));
if (properties == null || properties.Count() != 1)
{
throw new Exception("The property doesn't exist.");
}
var property = properties.First();
return (T)property.GetValue(this);
}
public void AddProperty<T, U>(string propertyName) where U : GenericRow
{
var customProperty =
new CustomPropertyDescriptor<T>(
propertyName,
typeof(U));
_property_list.Add(customProperty);
}
#endregion
#region Overriden Methods
public override PropertyDescriptorCollection GetProperties()
{
var properties = base.GetProperties();
return new PropertyDescriptorCollection(
properties.Cast<PropertyDescriptor>()
.Concat(_property_list).ToArray());
}
#endregion
}
:
public class GenericTable
{
private string tableName = "";
public string TableName
{
get { return tableName; }
set { tableName = value; }
}
private ObservableCollection<DataGridColumn> columnCollection;
public ObservableCollection<DataGridColumn> ColumnCollection
{
get { return columnCollection; }
private set { columnCollection = value; }
}
private ObservableCollection<GenericRow> genericRowCollection;
public ObservableCollection<GenericRow> GenericRowCollection
{
get { return genericRowCollection; }
set { genericRowCollection = value; }
}
public GenericTable(string tableName)
{
this.TableName = tableName;
ColumnCollection = new ObservableCollection<DataGridColumn>();
GenericRowCollection = new ObservableCollection<GenericRow>();
}
/// <summary>
/// ColumnName is also binding property name
/// </summary>
/// <param name="columnName"></param>
public void AddColumn(string columnName)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = columnName;
column.Binding = new Binding(columnName);
ColumnCollection.Add(column);
}
public override string ToString()
{
return TableName;
}
}
我认为你只需要这样修改你的Parameter
类:
public class Parameter
{
public string Name { get; set; }
public List<NormValue> Norms { get; set; }
public NormValue this[string columnName]
{
get
{
/* You can use Linq if you prefer */
foreach (NormValue normValue in Norms)
{
if (StringComparer.OrdinalIgnoreCase.Compare(normValue.Definition.ColumnName, columnName) == 0)
{
return normValue;
}
}
NormValue newNormValue = new NormValue();
newNormValue.Definition = new NormDefinition();
newNormValue.Definition.ColumnName = columnName;
newNormValue.Value = "Data is not available";
Norms.Add(newNormValue);
return newNormValue;
}
}
}
然后你可以这样构建DataGrid的列:
private void CreateNormDefinitionColumns()
{
foreach (var definition in Assessment.Definitions)
{
Binding binding = new Binding(String.Format("[{0}].Value", definition.ColumnName));
binding.Mode = BindingMode.TwoWay;
DataGridTextColumn column = new DataGridTextColumn();
column.Header = definition.ColumnName;
column.Binding = binding;
ParameterDataGrid.Columns.Add(column);
}
}
当然你的类应该实现INotifyPropertyChanged
,如果你想改变反映到UI。出于同样的原因,列表应该被ObservableCollections所取代。